多进程之间同步可以用互斥锁进行实现,默认情况下,互斥锁的属性是在一个进程的多线程使用的。
多进程因为分开之后内存是相对独立的,所以要实现进程间互斥锁,必须要把互斥锁放在共享内存里使用,在 pthread_mutex_init 初始化之前,修改其属性为进程间共享,即设置互斥锁的属性为PTHREAD_PROCESS_SHARED。
但是,如果某个进程在获得了锁之后崩溃并且未释放锁,则其他进程会阻塞造成死锁,但是文件锁,则会自动释放该锁。
2) 文件锁
借助 fcntl 函数来实现文件锁,可以用来对有亲缘或无亲缘关系的进程进行文件读与写的同步。
当一个进程正在读或修改文件或文件的某个部分时,它可以阻止其他进程修改同一文件。
fcntl函数:int fcntl(int fd, int cmd ,struct flock* lock);
应包含头文件: #include
第一个参数fd:文件操作。如,int fd = open(FILE_PATH, O_RDWR | O_CREAT, FILE_MODE);
注意的是:读锁要给读权限,写锁要给写权限。
第二个参数cmd:fcntl函数功能依据cmd的值的不同而不同,与文件锁相关的有:
F_SETLK:获取(l_type为F_RDLCK或F_WRLCK)或释放由lock指向flock结构所描述的锁,如果无法获取锁时,该函数会立即返回一个EACCESS或EAGAIN错误,而不会阻塞。
F_SETLKW:以阻塞方式给文件上锁,当希望设置的锁因为其他锁而被阻止设置时,该命令会等待相冲突的锁被释放。
F_GETLK:主要用来检测是否有某个已存在锁会妨碍将新锁授予调用进程,如果没有这样的锁,lock所指向的flock结构的l_type成员就会被置成F_UNLCK,否则已存在的锁的信息将会写入lock所指向的flock结构中。
这里需要注意的是,用F_GETLK测试能否建立一把锁,然后接着用F_SETLK或F_SETLKW企图建立一把锁,由于这两者不是一个原子操作,所以不能保证两次fcntl之间不会有另外一个进程插入并建立一把相关的锁,从而使一开始的测试情况无效。所以一般不希望上锁时阻塞,会直接通过调用F_SETLK,并对返回结果进行测试,以判断是否成功建立所要求的锁。
第三个参数指向一个struct flock *lock的结构体。
struct flock
{
short_l_type; // 锁的类型
short_l_whence; // 加锁区域的起始位置
off_t_l_start; // 加锁的起始偏移
off_t_l_len; // 上锁字节
pid_t_l_pid; // 锁的属主进程ID
};
short_l_type: 用来指定设置共享锁(F_RDLCK,读锁)、互斥锁(F_WDLCK,写锁)、解锁(F_UNLCK);
l_whence,l_start,l_len三个变量来确定给文件上锁的区域,当对整个文件上锁时,l_whence,l_start,l_len都设为0;
pid_t_l_pid: 当前锁进程ID,只在获取文件锁命令F_GETLK时起作用。
示例代码:
void lock_init(flock *lock, short type, short whence, off_t start, off_t len) {
if (lock == nullptr) {
return;
}
lock->l_type = type;
lock->l_whence = whence;
lock->l_start = start;
lock->l_len = len;
}
int readw_lock(int fd) {
if (fd < 0) {
return -1;
}
struct flock lock;
lock_init(&lock, F_RDLCK, SEEK_SET, 0, 0);
if (fcntl(fd, F_SETLK, &lock) != 0) {
return -1;
}
return 0;
}
int writew_lock(int fd) {
if (fd < 0) {
return -1;
}
struct flock lock;
lock_init(&lock, F_WRLCK, SEEK_SET, 0, 0);
if (fcntl(fd, F_SETLKW, &lock) != 0) {
return -1;
}
return 0;
}
int unlock(int fd) {
if (fd < 0) {
return -1;
}
struct flock lock;
lock_init(&lock, F_UNLCK, SEEK_SET, 0, 0);
if (fcntl(fd, F_SETLKW, &lock) != 0) {
return -1;
}
return 0;
}
pid_t lock_test(int fd, short type, short whence, off_t start, off_t len) {
flock lock;
lock_init(&lock, type, whence, start, len);
if (fcntl(fd, F_GETLK, &lock) == -1) {
return -1;
}
if(lock.l_type == F_UNLCK)
return 0;
return lock.l_pid;
}
int main() {
int fd = open(FILE_PATH, O_RDWR | O_CREAT, FILE_MODE);
writew_lock(fd);
cout<