Linux 多進程鎖的幾種實現方案


我們知道,多線程可以用多線程互斥量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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM