我們知道,多線程可以用多線程互斥量pthread_mutex_t實現線程之間上鎖,那么多進程之間如何共享鎖呢?
1. 文件鎖實現多進程鎖
由於文件鎖是存放到位於內存的系統文件表中, 所有進程/線程可通過系統訪問。如果不同進程使用同一文件鎖(寫鎖/排他鎖),當取得文件鎖時,進程可繼續執行;如果沒有取得鎖,則阻塞等待。而唯一標識該文件的是文件路徑,因此,可以通過一個共同的文件路徑,來實現多進程鎖機制。
參見文件鎖的本質核心和原理 | CSDN
關鍵點:
1)創建/打開一個唯一路徑的文件;
2)P操作,取得文件的寫鎖(排他鎖);V操作,釋放文件的鎖;
static struct flock lock_it; /* 用於加鎖的flock對象 */
static struct unlock_it; /* 用於解鎖的flock對象 */
static int lock_fd = -1;
void
my_lock_init(char *pathname)
{
char lock_file[1024]; /* 存放文件名的臨時緩存 */
/* 由於mkstemp會修改參數的最后6個字符,為避免pathname是字符串常量,需要將字符串從pathname copy到臨時緩存lock_file中,然后作為創建唯一臨時文件的參數 */
strncpy(lock_file, pathname, sizeof(lock_file));
lock_fd = mkstemp(lock_file); /* 創建唯一的臨時文件並打開 */
unlink(lock_file); /* 刪除指定文件名的文件,如果有進程打開了該文件,會在進程結束后所有指向該文件的描述符都關閉后刪除文件。這樣可以確保即使程序崩潰,臨時文件也會完全消失 */
/* 設置flock對象的寫鎖屬性 */
lock_it.l_type = F_WRLCK;
lock_it.l_whence = SEEK_SET;
lock_it.l_start = 0;
lock_it.l_len = 0;
/* 設置flock對象的解鎖屬性 */
unlock_it.l_type = F_UNLCK;
unlock_it.l_whence = SEEK_SET;
unlock_it.l_start = 0;
unlock_it.l_len = 0;
}
void
my_lock_wait()
{
int rc;
/* 取得寫鎖,如果阻塞等待時被中斷喚醒,則繼續恢復阻塞等待鎖資源 */
whilel ((rc = fcntl(lock_fd, F_SETLKW, &lock_it)) < 0) {
if (errno == EINTR) continue;
else err_sys("my_lock_wait fcntl error"); /* 如果沒有調用my_lock_init就直接調用fcntl,將會失敗 */
}
}
void
my_lock_release()
{
if (fcntl(lock_fd, F_SETLKW, &unlock_it) < 0)
err_sys("my_lock_release fcntl error");
}
2. 多線程鎖實現多進程鎖
多線程之間天然共享內存/變量,而多進程各有自己的進程空間,它們之間是不共享數據的
2個關鍵步驟
1)互斥鎖變量存放到共享內存;
2)設置互斥鎖變量的進程共享屬性(PTHREAD_PROCESS_SHARED);
static pthread_mutex_t *mptr; /* 互斥鎖變量指針,互斥鎖變量存放到共享內存 */
/**
* 多進程互斥鎖變量初始化
*/
void
my_lock_init()
{
int fd;
pthread_mutexattr_t mattr;
/* 新建共享內存區域,但不映射到實際的普通文件 */
fd = open("/dev/zero", O_RDWR, 0);
if (fd < 0) err_sys("open error");
mptr = mmap(0, sizeof(pthread_mutex_t), PORT_READ | PORT_WRITE, MAP_SHARED, fd, 0);
if (mptr == MAP_FAILED) err_sys("mmap");
if (close(fd)) err_sys("close error");
pthread_mutexattr_init(&mattr);
pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
phtread_mutex_init(mptr, &mattr);
}
/**
* 加鎖
*/
void
my_lock_wait()
{
pthread_mutex_lock(mptr);
}
/**
* 解鎖
*/
void
my_lock_release()
{
pthread_mutex_unlock(mptr);
}
對於符合4.4BSD的Linux系統,直接支持匿名內存映射,可以通過指定mmap的參數flag = MAP_ANON,fd = -1,offset被忽略.
而上面將fd指向打開的/dev/zero,是更通用的做法,任何類Unix系統通常都支持。
# ifdef MAP_ANON
ptr = mmap(0, nchildren * sizeof(long), PORT_READ | PORT_WRITE, MAP_ANON | MAP_SHARED, -1 , 0);
#else
int fd;
fd = open("/dev/zero", O_RDWR, 0);
if (fd < 0)
err_sys("open error");
ptr = mmap(0, nchildren * sizeof(long), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
#endif