Linux 读写锁
线程的读写锁函数:
1,读写锁的初始化与销毁,静态初始化的话,可以直接使用PTHREAD_RWLOCK_INITIALIZER。
#include <pthread.h>
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict attr);
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
2,用读的方式加锁和尝试(没锁上就立即返回)加锁。
#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
3,用写的方式加锁和尝试(没锁上就立即返回)加锁。
#include <pthread.h>
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
4,解锁
#include <pthread.h>
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
多个进程在同时读写同一个文件,会发生什么?
例子1:用下面的例子的执行结果,观察多个进程在同时读写同一个文件,会发生什么。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAXLINE 100
#define FN "num1"
void my_lock(int fd){
return;
}
void my_unlock(int fd){
return;
}
int main(int args, char** argv){
int fd;
long i,seqno;
pid_t pid;
ssize_t n;
char line[MAXLINE + 1];
pid = getpid();
fd = open(FN, O_RDWR, 0664);
for(i = 0; i < 20; ++i){
my_lock(fd);
lseek(fd, 0L, SEEK_SET);
n = read(fd, line, MAXLINE);
line[n] = '\0';
seqno = atol(line);
printf("%s:pid = %ld, seq = %ld\n", argv[0], (long)pid, seqno);
seqno++;
snprintf(line, sizeof(line), "%ld\n", seqno);
lseek(fd, 0L, SEEK_SET);
write(fd, line, strlen(line));
my_unlock(fd);
}
return 0;
}
执行方法:同时执行上面例子的程序2次,也就是2个进程同时读写同一个文件。
ubuntu$ ./flockmain1 & ./flockmain1 &
执行结果如下,发现2个进程同时读写,在①处开始,内核切换进程时,数字乱套了。
ubuntu$ ./flockmain1 & ./flockmain1 &
[1] 4760
[2] 4761
ubuntu$ ./flockmain1:pid = 4761, seq = 1
./flockmain1:pid = 4761, seq = 2
./flockmain1:pid = 4761, seq = 3
./flockmain1:pid = 4761, seq = 4
./flockmain1:pid = 4761, seq = 5
./flockmain1:pid = 4761, seq = 6
./flockmain1:pid = 4761, seq = 7
./flockmain1:pid = 4761, seq = 8
./flockmain1:pid = 4761, seq = 9
./flockmain1:pid = 4761, seq = 10 ------------①
./flockmain1:pid = 4760, seq = 10
./flockmain1:pid = 4761, seq = 11
./flockmain1:pid = 4761, seq = 12
./flockmain1:pid = 4761, seq = 13
./flockmain1:pid = 4761, seq = 14
./flockmain1:pid = 4761, seq = 15
./flockmain1:pid = 4761, seq = 16
./flockmain1:pid = 4761, seq = 17
./flockmain1:pid = 4761, seq = 18
./flockmain1:pid = 4761, seq = 19
./flockmain1:pid = 4761, seq = 20
./flockmain1:pid = 4760, seq = 11
./flockmain1:pid = 4760, seq = 12
./flockmain1:pid = 4760, seq = 13
./flockmain1:pid = 4760, seq = 14
./flockmain1:pid = 4760, seq = 15
./flockmain1:pid = 4760, seq = 16
./flockmain1:pid = 4760, seq = 17
./flockmain1:pid = 4760, seq = 18
./flockmain1:pid = 4760, seq = 19
./flockmain1:pid = 4760, seq = 20
./flockmain1:pid = 4760, seq = 21
./flockmain1:pid = 4760, seq = 22
./flockmain1:pid = 4760, seq = 23
./flockmain1:pid = 4760, seq = 24
./flockmain1:pid = 4760, seq = 25
./flockmain1:pid = 4760, seq = 26
./flockmain1:pid = 4760, seq = 27
./flockmain1:pid = 4760, seq = 28
./flockmain1:pid = 4760, seq = 29
为了解决上面的问题,必须对文件的内容进行加锁。
如何对文件内容加锁?
使用fcntl函数,它既可以锁整文件,也可以锁文件里的某段内容。通过结构体flock来指定要锁的范围。如果 whence = SEEK_SET;l_start = 0;l_len = 0;就是锁定整个文件。
struct flock {
...
short l_type; /* Type of lock: F_RDLCK,
F_WRLCK, F_UNLCK */
short l_whence; /* How to interpret l_start:
SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start; /* Starting offset for lock */
off_t l_len; /* Number of bytes to lock */
pid_t l_pid; /* PID of process blocking our lock
(set by F_GETLK and F_OFD_GETLK) */
...
};
- F_SETLK:上锁。如果发现已经被别的进程上锁了,就直接返回-1,errno被设置成EACCES或者EAGAIN,不阻塞。
- F_SETLKW:上锁。阻塞等待。
- F_GETLK:得到锁的状态。
修改上面的函数my_lock,my_unlock。main函数不变。
例子2:
void my_lock(int fd){
struct flock lock;
lock.l_type = F_WRLCK;
wlock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
fcntl(fd, F_SETLKW, lock);
}
void my_unlock(int fd){
struct flock lock;
lock.l_type = F_UNLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
fcntl(fd, F_SETLK, lock);
}
执行结果如下,发现数字不乱套了。
ubuntu$ ./flockmain & ./flockmain &
[1] 4882
[2] 4883
ubuntu$ ./flockmain:pid = 4883, seq = 1
./flockmain:pid = 4883, seq = 2
./flockmain:pid = 4883, seq = 3
./flockmain:pid = 4883, seq = 4
./flockmain:pid = 4883, seq = 5
./flockmain:pid = 4883, seq = 6
./flockmain:pid = 4883, seq = 7
./flockmain:pid = 4883, seq = 8
./flockmain:pid = 4883, seq = 9
./flockmain:pid = 4883, seq = 10
./flockmain:pid = 4883, seq = 11
./flockmain:pid = 4883, seq = 12
./flockmain:pid = 4883, seq = 13
./flockmain:pid = 4883, seq = 14
./flockmain:pid = 4883, seq = 15
./flockmain:pid = 4883, seq = 16
./flockmain:pid = 4883, seq = 17
./flockmain:pid = 4883, seq = 18
./flockmain:pid = 4883, seq = 19
./flockmain:pid = 4883, seq = 20
./flockmain:pid = 4882, seq = 21
./flockmain:pid = 4882, seq = 22
./flockmain:pid = 4882, seq = 23
./flockmain:pid = 4882, seq = 24
./flockmain:pid = 4882, seq = 25
./flockmain:pid = 4882, seq = 26
./flockmain:pid = 4882, seq = 27
./flockmain:pid = 4882, seq = 28
./flockmain:pid = 4882, seq = 29
./flockmain:pid = 4882, seq = 30
./flockmain:pid = 4882, seq = 31
./flockmain:pid = 4882, seq = 32
./flockmain:pid = 4882, seq = 33
./flockmain:pid = 4882, seq = 34
./flockmain:pid = 4882, seq = 35
./flockmain:pid = 4882, seq = 36
./flockmain:pid = 4882, seq = 37
./flockmain:pid = 4882, seq = 38
./flockmain:pid = 4882, seq = 39
./flockmain:pid = 4882, seq = 40
到此为止,貌似解决了问题,但是如果同时执行例子1和例子2,结果如下,发现还是乱的。
也就是说在协作线程(cooperating processes)间,文件锁(也叫劝告性上锁)也起作用的。但是不完全不相关的进程中,文件锁也不起作用的。如何解决呢?使用强制性上锁。
ys@ys-VirtualBox:~/IPC$ ./flockmain1 & ./flockmain &
[1] 3602
[2] 3603
ys@ys-VirtualBox:~/IPC$ ./flockmain1:pid = 3602, seq = 1
./flockmain:pid = 3603, seq = 1
./flockmain:pid = 3603, seq = 2
./flockmain:pid = 3603, seq = 3
./flockmain:pid = 3603, seq = 4
./flockmain:pid = 3603, seq = 5
./flockmain:pid = 3603, seq = 6
./flockmain:pid = 3603, seq = 7
./flockmain:pid = 3603, seq = 8
./flockmain:pid = 3603, seq = 9
./flockmain:pid = 3603, seq = 10
./flockmain1:pid = 3602, seq = 2
./flockmain1:pid = 3602, seq = 3
./flockmain1:pid = 3602, seq = 4
./flockmain:pid = 3603, seq = 11
./flockmain:pid = 3603, seq = 12
./flockmain1:pid = 3602, seq = 5
./flockmain:pid = 3603, seq = 13
./flockmain1:pid = 3602, seq = 6
./flockmain1:pid = 3602, seq = 7
./flockmain1:pid = 3602, seq = 8
./flockmain:pid = 3603, seq = 14
./flockmain:pid = 3603, seq = 15
./flockmain1:pid = 3602, seq = 9
./flockmain1:pid = 3602, seq = 10
./flockmain:pid = 3603, seq = 16
./flockmain:pid = 3603, seq = 17
./flockmain1:pid = 3602, seq = 11
./flockmain:pid = 3603, seq = 18
./flockmain1:pid = 3602, seq = 12
./flockmain1:pid = 3602, seq = 13
./flockmain1:pid = 3602, seq = 14
./flockmain:pid = 3603, seq = 19
./flockmain:pid = 3603, seq = 20
./flockmain1:pid = 3602, seq = 15
./flockmain1:pid = 3602, seq = 16
./flockmain1:pid = 3602, seq = 17
./flockmain1:pid = 3602, seq = 18
./flockmain1:pid = 3602, seq = 19
./flockmain1:pid = 3602, seq = 20
第一个问题:假如一个文件被一个进程以读的方式锁定,并有另一个进程在等待读锁定解锁后,用写入的方式锁定,这时是否允许另一个进程的还以读的方式取得锁定?
用例子3来观察:
#include <time.h>
#include <sys/time.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
void gftime(char* buf){
struct timeval tv;
gettimeofday(&tv, NULL);
long usec = tv.tv_usec;
struct tm* tm = localtime(&tv.tv_sec);
sprintf(buf, "%d:%d:%d.%ld",tm->tm_hour, tm->tm_min, tm->tm_sec,usec);
}
int main(){
char buff[100] = {0};
int fd = open("test.dat", O_RDWR | O_CREAT, 0664);
struct flock lock;
lock.l_type = F_RDLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
fcntl(fd, F_SETLK, &lock);
gftime(buff);
printf("%s: parent has read lock\n", buff);
//first child
if(fork() == 0){
char buf2[100] = {0};
sleep(1);
gftime(buf2);
printf("%s: first child tries to obtain write lock\n", buf2);
struct flock lock2;
lock2.l_type = F_WRLCK;
lock2.l_whence = SEEK_SET;
lock2.l_start = 0;
lock2.l_len = 0;
fcntl(fd, F_SETLKW, &lock2);
gftime(buf2);
printf("%s: first child obtains write lock\n", buf2);
sleep(2);
lock2.l_type = F_UNLCK;
lock2.l_whence = SEEK_SET;
lock2.l_start = 0;
lock2.l_len = 0;
fcntl(fd, F_SETLK, &lock2);
gftime(buf2);
printf("%s: first child releases write lock\n", buf2);
exit(0);
}
//secodn child
if(fork() == 0){
char buf1[100] = {0};
sleep(3);
gftime(buf1);
printf("%s: second child tries to obtain read lock\n", buf1);
struct flock lock1;
lock1.l_type = F_RDLCK;
lock1.l_whence = SEEK_SET;
lock1.l_start = 0;
lock1.l_len = 0;
fcntl(fd, F_SETLKW, &lock1);
gftime(buf1);
printf("%s: second child obtains read lock\n", buf1);
sleep(4);
lock1.l_type = F_UNLCK;
lock1.l_whence = SEEK_SET;
lock1.l_start = 0;
lock1.l_len = 0;
fcntl(fd, F_SETLK, &lock1);
gftime(buf1);
printf("%s: second child release read lock\n", buf1);
exit(0);
}
//parent process
sleep(5);
lock.l_type = F_UNLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
fcntl(fd, F_SETLK, &lock);
gftime(buff);
printf("%s: parent releases read lock\n", buff);
wait(NULL);
wait(NULL);
exit(0);
}
在ubuntu上执行结果:
17:49:44.348946: parent has read lock
17:49:45.350191: first child tries to obtain write lock
17:49:47.350155: second child tries to obtain read lock
17:49:47.350409: second child obtains read lock
17:49:49.349442: parent releases read lock
17:49:51.351197: second child release read lock
17:49:51.351582: first child obtains write lock
17:49:53.351689: first child releases write lock
第一个问题的答案:允许另一个进程的还以读的方式取得锁定
第二个问题:假如一个文件被一个进程以写的方式锁定,这时又有2个进程在等待这个锁的释放,其中一个进程是以写锁的方式等待,其中另一个进程是以读锁的方式等待,哪一个会优先取得锁?
用例子4来观察:
#include <time.h>
#include <sys/time.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
void gftime(char* buf){
struct timeval tv;
gettimeofday(&tv, NULL);
long usec = tv.tv_usec;
struct tm* tm = localtime(&tv.tv_sec);
sprintf(buf, "%d:%d:%d.%ld",tm->tm_hour, tm->tm_min, tm->tm_sec,usec);
}
int main(){
char buff[100] = {0};
int fd = open("test.dat", O_RDWR | O_CREAT, 0664);
struct flock lock;
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
fcntl(fd, F_SETLK, &lock);
gftime(buff);
printf("%s: parent has write lock\n", buff);
//first child
if(fork() == 0){
char buf2[100] = {0};
sleep(1);
gftime(buf2);
printf("%s: first child tries to obtain write lock\n", buf2);
struct flock lock2;
lock2.l_type = F_WRLCK;
lock2.l_whence = SEEK_SET;
lock2.l_start = 0;
lock2.l_len = 0;
fcntl(fd, F_SETLKW, &lock2);
gftime(buf2);
printf("%s: first child obtains write lock\n", buf2);
sleep(2);
lock2.l_type = F_UNLCK;
lock2.l_whence = SEEK_SET;
lock2.l_start = 0;
lock2.l_len = 0;
fcntl(fd, F_SETLK, &lock2);
gftime(buf2);
printf("%s: first child releases write lock\n", buf2);
exit(0);
}
//secodn child
if(fork() == 0){
char buf1[100] = {0};
sleep(3);
gftime(buf1);
printf("%s: second child tries to obtain read lock\n", buf1);
struct flock lock1;
lock1.l_type = F_RDLCK;
lock1.l_whence = SEEK_SET;
lock1.l_start = 0;
lock1.l_len = 0;
fcntl(fd, F_SETLKW, &lock1);
gftime(buf1);
printf("%s: second child obtains read lock\n", buf1);
sleep(4);
lock1.l_type = F_UNLCK;
lock1.l_whence = SEEK_SET;
lock1.l_start = 0;
lock1.l_len = 0;
fcntl(fd, F_SETLK, &lock1);
gftime(buf1);
printf("%s: second child release read lock\n", buf1);
exit(0);
}
//parent process
sleep(5);
lock.l_type = F_UNLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
fcntl(fd, F_SETLK, &lock);
gftime(buff);
printf("%s: parent releases write lock\n", buff);
wait(NULL);
wait(NULL);
exit(0);
}
在ubuntu上执行结果:
17:49:29.796599: parent has write lock
17:49:30.797099: first child tries to obtain write lock
17:49:32.796885: second child tries to obtain read lock
17:49:34.796868: parent releases write lock
17:49:34.796987: second child obtains read lock
17:49:38.797148: second child release read lock
17:49:38.797297: first child obtains write lock
17:49:40.797727: first child releases write lock
第二个问题的答案:没有准确答案。在Ubuntu上的执行结果上看,读锁优先了,但是,可能在别的环境上又是写锁优先。按道理来说应该写锁优先吧?
c/c++ 学习互助QQ群:877684253
本人微信:xiaoshitou5854
Linux 读写锁的更多相关文章
- linux读写锁
一.概述 读写锁与互斥量的功能类似,对临界区的共享资源进行保护!互斥量一次只让一个线程进入临界区, ...
- linux 读写锁应用实例
转自:http://blog.csdn.net/dsg333/article/details/22113489 /*使用读写锁实现四个线程读写一段程序的实例,共创建了四个新的线程,其中两个线程用来读取 ...
- Linux读写锁的使用
读写锁是用来解决读者写者问题的,读操作可以共享,写操作是排它的,读可以有多个在读,写只有唯一个在写,写的时候不允许读. 具有强读者同步和强写者同步两种形式: 强读者同步:当写者没有进行写操作时,读者就 ...
- Linux的线程同步对象:互斥量Mutex,读写锁,条件变量
进程是Linux资源分配的对象,Linux会为进程分配虚拟内存(4G)和文件句柄等 资源,是一个静态的概念.线程是CPU调度的对象,是一个动态的概念.一个进程之中至少包含有一个或者多个线程.这 ...
- linux中读写锁的rwlock介绍-nk_ysg-ChinaUnix博客
linux中读写锁的rwlock介绍-nk_ysg-ChinaUnix博客 linux中读写锁的rwlock介绍 2013-02-26 13:59:35 分类: C/C++ http://yaro ...
- linux 内核的另一个自旋锁 - 读写锁
除spinlock外,linux 内核还有一个自旋锁,名为arch_rwlock_t.它的头文件是qrwlock.h,包含在spinlock.h,头文件中对它全称为"Queue read/w ...
- linux线程间同步(1)读写锁
读写锁比mutex有更高的适用性,能够多个线程同一时候占用读模式的读写锁.可是仅仅能一个线程占用写模式的读写锁. 1. 当读写锁是写加锁状态时,在这个锁被解锁之前,全部试图对这个锁加锁的线程都会被堵塞 ...
- Linux系统编程 —读写锁rwlock
读写锁是另一种实现线程间同步的方式.与互斥量类似,但读写锁将操作分为读.写两种方式,可以多个线程同时占用读模式的读写锁,这样使得读写锁具有更高的并行性. 读写锁的特性为:写独占,读共享:写锁优先级高. ...
- linux kernel RCU 以及读写锁
信号量有一个很明显的缺点,没有区分临界区的读写属性,读写锁允许多个线程进程并发的访问临界区,但是写访问只限于一个线程,在多处理器系统中允许多个读者访问共享资源,但是写者有排他性,读写锁的特性如下:允许 ...
随机推荐
- C语言中函数返回字符串的4中方法
C语言中函数返回字符串的4中方法 函数的构成部分:返回类型.函数名称.参数.函数主体 参数:函数调用时传入的参数称为实参,函数定义时出现的参数为形参 形参的作用在于接收实参传入的值,形参和函数内部的其 ...
- 一次shell脚本小事故,从中学习排错过程-软件测试
一次shell脚本小事故,从中学习排错过程 事出,童鞋使用shell脚本搭建测试环境的过称中..... 配置环境变量文件:/etc/profile(用于升级JDK或其他) 手动编辑方法:vi /etc ...
- C++:获取基本变量的类型
方法: typeid(变量).name() 代码: /**获取变量类型*/ #include<iostream> #include<string> #include<ty ...
- Web-babyphp
题目地址 http://web.jarvisoj.com:32798/ 首先先观察网页,点击about发现下面包含这些 看到GIT,那么基本可以明确这题有.git泄露,我们用GitHack来获取源码分 ...
- [Taro] taro中定义以及使用全局变量
taro中定义以及使用全局变量 错误的姿势 // app.tsx文件中 class App extends Component { componentDidMount() { this.user = ...
- MySQL实战45讲学习笔记:第三十二讲
一.本节分析案例 在 MySQL 中有两个 kill 命令:一个是 kill query + 线程 id,表示终止这个线程中正在执行的语句:一个是 kill connection + 线程 id,这里 ...
- Paper | Deep Residual Learning for Image Recognition
目录 1. 故事 2. 残差学习网络 2.1 残差块 2.2 ResNet 2.3 细节 3. 实验 3.1 短连接网络与plain网络 3.2 Projection解决短连接维度不匹配问题 3.3 ...
- Vue中MVVM模式的双向绑定原理 和 代码的实现
今天带大家简单的实现MVVM模式,Object.defineProperty代理(proxy)数据 MVVM的实现方式: 模板编译(Compile) 数据劫持(Observer) Object ...
- C#.Net 使用 JsonReader/JsonWriter 高性能解析/生成 Json 文档
Swifter.Json 是由本人编写的高性能且多功能的 Json 解析库.下图是 Swifter.Json 与 .Net 平台上的其他 Json 库性能对比: 在 Swifter.Json 近期更新 ...
- 微信小程序开发-蓝牙功能开发
0. 前言 这两天刚好了解了一下微信小程序的蓝牙功能.主要用于配网功能.发现微信的小程序蓝牙API已经封装的很好了.编程起来很方便.什么蓝牙知识都不懂的情况下,不到两天就晚上数据的收发了,剩下的就是数 ...