inux c编程:记录锁
记录锁的功能:当一个进程正在读或修改文件的某个部分是,它可以阻止其他进程修改同一文件区。对于这个功能阐述我认为有三点要解释的:
记录锁不仅仅可以用来同步不同进程对同一文件的操作,还可以通过对同一文件加记录锁,来同步不同进程对某一共享资源的访问,如共享内存,I/O设备。
对于劝告性上锁,当一个进程通过上锁对文件进行操作时,它不能阻止另一个非协作进程对该文件的修改。
即使是强制性上锁,也不能完全保证该文件不会被另一个进程修改。因为强制性锁对unlink函数没有影响,所以一个进程可以先删除该文件,然后再将修改后的内容保存为同一文件来实现修改。
1记录锁函数接口
记录上锁的POSIX接口函数fcntl如下:
/* Do the file control operation described by CMD on FD.
The remaining arguments are interpreted depending on CMD. */
int fcntl (int __fd, int __cmd, ...);
//根据cmd的不同有以下三种类型的调用
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
由函数名称可知fcntl的功能是对文件的控制操作,根据传入不同的操作类型命令cmd,fcntl会执行不同的操作,fcnt根据cmd不同,接收可变的参数。具体有以下五种类型的操作:
/*
cmd = F_DUPFD,复制一个文件描述符;
*/
int fcntl(int fd, int cmd);
/*
cmd = F_GETFD,获得文件描述符标志;
cmd = F_SETFD,设置文件描述符标志;arg = 描述符标志的值,目前只定义了一个标志: FD_CLOEXEC
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
*/
/*
cmd = F_GETFL,获得文件状态标志;
cmd = F_SETFL,设置文件状态标志;arg = 状态标志的值
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
*/
/*
cmd = F_GETOWN,获得当前接收SIGIO和SIGURG信号的进程ID或进程组ID
cmd = F_SETOWN,设置接收SIGIO和SIGURG信号的进程ID或进程组ID;arg = 进程ID或进程组ID
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
*/
/*
Return value:
对于成功的调用,根据操作类型cmd不同,有以下几种情况:
F_DUPFD 返回新的文件描述符
F_GETFD 返回文件描述符标志
F_GETFL 返回文件状态标志
F_GETOWN 进程ID或进程组ID
All other commands 返回0
调用失败, 返回-1,并设置errno。
*/
上面四个功能都是fcntl提供的很常用的操作,关于记录锁的功能就是fcntl提供的第五个功能,具体使用如下:
int fcntl(int fd, int cmd, struct flock *lock);
/*
cmd = F_GETLK,测试能否建立一把锁
cmd = F_SETLK,设置锁
cmd = F_SETLKW, 阻塞设置一把锁
*/
//POSIX只定义fock结构中必须有以下的数据成员,具体实现可以增加
struct flock {
short l_type; /* 锁的类型: F_RDLCK, F_WRLCK, F_UNLCK */
short l_whence; /* 加锁的起始位置:SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start; /* 加锁的起始偏移,相对于l_whence */
off_t l_len; /* 上锁的字节数*/
pid_t l_pid; /* 已经占用锁的PID(只对F_GETLK 命令有效) */
/*...*/
};
//Return value: 前面已经说明;
l F_SETLK:获取(l_type为F_RDLCK或F_WRLCK)或释放由lock指向flock结构所描述的锁,如果无法获取锁时,该函数会立即返回一个EACCESS或EAGAIN错误,而不会阻塞。
l F_SETLKW:F_SETLKW和F_SETLK的区别是,无法设置锁的时候,调用线程会阻塞到该锁能够授权位置。
l F_GETLK:IF_GETLK主要用来检测是否有某个已存在锁会妨碍将新锁授予调用进程,如果没有这样的锁,lock所指向的flock结构的l_type成员就会被置成F_UNLCK,否则已存在的锁的信息将会写入lock所指向的flock结构中
这里需要注意的是,用F_GETLK测试能否建立一把锁,然后接着用F_SETLK或F_SETLKW企图建立一把锁,由于这两者不是一个原子操作,所以不能保证两次fcntl之间不会有另外一个进程插入并建立一把相关的锁,从而使一开始的测试情况无效。所以一般不希望上锁时阻塞,会直接通过调用F_SETLK,并对返回结果进行测试,以判断是否成功建立所要求的锁。
2记录锁规则说明
前面我们说了记录锁相当于读写锁的一种扩展类型,记录锁和读写锁一样也有两种锁:共享读锁(F_RDLCK)和独占写锁(F_WRLCK)。在使用规则上和读写锁也基本一样:
- 1 文件给定字节区间,多个进程可以有一把共享读锁,即允许多个进程以读模式访问该字节区;
- 2 文件给定字节区间,只能有一个进程有一把独占写锁,即只允许有一个进程已写模式访问该字节区;
- 3 文件给定字节区间,如果有一把或多把读锁,不能在该字节区再加写锁,同样,如果有一把写锁,不能再该字节区再加任何读写锁。
如下表所示:
需要说明的是:上面所阐述的规则只适用于不同进程提出的锁请求,并不适用于单个进程提出的多个锁请求。即如果一个进程对一个文件区间已经有了一把锁,后来该进程又试图在同一文件区间再加一把锁,那么新锁将会覆盖老锁。
下面进行测试;第一个程序是在同一进程中测试能否在加写锁后,继续加读写锁
void lock_init(struct flock *lock, short type, short whence, off_t start, off_t len)
{
if (lock == NULL)
return;
lock->l_type = type;
lock->l_whence = whence;
lock->l_start = start;
lock->l_len = len;
}
int writew_lock(int fd)
{
struct flock lock;
if (fd < 0)
{
return -1;
}
lock_init(&lock, F_WRLCK, SEEK_SET, 0, 0);
if (fcntl(fd, F_SETLKW, &lock) != 0)
{
return -1;
}
return 0;
}
int main()
{
char *path="home/zhf/test1.txt";
int fd;
pid_t t,t1;
fd = fopen(path, "r");
writew_lock(fd);
t=lock_test(fd, F_WRLCK, SEEK_SET, 0, 0);
t1=lock_test(fd, F_RDLCK, SEEK_SET, 0, 0);
printf("the pid_t is %d\n",t);
printf("the pid_t is %d\n",t1);
unlock(fd);
return 0;
}
执行结果表明同一进程可以对已加锁的同一文件区间,仍然能获得加锁权限;
2 由fork产生的子进程不继承父进程所设置的锁。即对于父进程建立的锁而言,子进程被视为另一个进程。记录锁本身就是用来同步不同进程对同一文件区进行操作,如果子进程继承了父进程的锁,那么父子进程就可以同时对同一文件区进行操作,这有违记录锁的规则,所以存在这么一条规则。
int main()
{
int fd = open(FILE_PATH, O_RDWR | O_CREAT, FILE_MODE);
writew_lock(fd);
if (fork() == 0)
{
cout<<lock_test(fd, F_WRLCK, SEEK_SET, 0, 0)<<endl;
cout<<lock_test(fd, F_RDLCK, SEEK_SET, 0, 0)<<endl;
exit(0);
}
sleep(3);
unlock(fd);
return 0;
}
我们知道在前面已经说过,同一个进程可以重复对同一个文件区间加锁,后加的锁将覆盖前面加的锁。那么再假设如果子进程继承了父进程的锁,那么子进程可以对该锁进行覆盖,那么在子进程内对该锁是否能获得权限的测试应该是可以,但测试结果为:
24791
24791
表明已经进程24791已经占用该锁,所以假设不成立,子进程不会继承父进程的锁;
3 记录锁的读和写的优先级
int main()
{
int fd = open(FILE_PATH, O_RDWR | O_CREAT, FILE_MODE);
readw_lock(fd);
//child 1
if (fork() == 0)
{
cout<<"child 1 try to get write lock..."<<endl;
writew_lock(fd);
cout<<"child 1 get write lock..."<<endl;
unlock(fd);
cout<<"child 1 release write lock..."<<endl;
exit(0);
}
//child 2
if (fork() == 0)
{
sleep(3);
cout<<"child 2 try to get read lock..."<<endl;
readw_lock(fd);
cout<<"child 2 get read lock..."<<endl;
unlock(fd);
cout<<"child 2 release read lock..."<<endl;
exit(0);
}
sleep(10);
unlock(fd);
return 0;
}
在Linux 2.6.18下执行结果如下:
child 1 try to get write lock...
child 2 try to get read lock...
child 2 get read lock...
child 2 release read lock...
child 1 get write lock...
child 1 release write lock...
可知在有写入进程等待的情况下,对于读出进程的请求,系统会一直给予的。那么这也就可能导致写入进程饿死的局面。
inux c编程:记录锁的更多相关文章
- linux c编程:记录锁
记录锁相当于线程同步中读写锁的一种扩展类型,可以用来对有亲缘或无亲缘关系的进程进行文件读与写的同步,通过fcntl函数来执行上锁操作.尽管读写锁也可以通过在共享内存区来进行进程的同步,但是fcntl记 ...
- 高级I/O之记录锁
若两个人同时编辑一个文件,其后果将如何呢?在很多UNIX系统中,该文件的最后状态取决于写该文件的最后一个进程.但是对于有些应用程序(例如数据库),进程有时需要确保它正在单独写一个文件.为了向进程提供这 ...
- Mysql加锁过程详解(9)-innodb下的记录锁,间隙锁,next-key锁
Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...
- 推荐:mysql锁 innodb下的记录锁,间隙锁,next-key锁
你需要知道的 之前我们介绍了排他锁,其实innodb下的记录锁(也叫行锁),间隙锁,next-key锁统统属于排他锁. 行锁 记录锁其实很好理解,对表中的记录加锁,叫做记录锁,简称行锁. 生活中的间隙 ...
- 【Mysql】表锁 行锁 记录锁 间隙锁
Mysql中的锁 基于锁的属性分类:共享锁.排他锁. 基于锁的状态分类:意向共享锁.意向排它锁 根据锁的粒度分类:全局锁.页锁.表级锁.行锁(记录锁.间隙锁.和临键锁),实际上的锁就这些,上面两种分类 ...
- [11]APUE:(文件)记录锁
[a] 概念 建议锁:在遵循相同记录锁规则的进程间生效,通常用于保证某个程序自身多个进程间的数据一致性 强制锁:意在保证所有进程间的数据一致性,但不一定有效:如不能应对先 unlink 后建立同名副本 ...
- 非阻塞io与记录锁
非阻塞io 1.对比 阻塞io的例子:scanf从终端获取输入时,如果不输入程序就会一直停在那; 对一个已经有写锁的文件请求读时, 会一直空等直到前面的进程释放锁... 非阻塞的例子:读取文件内容, ...
- fcntl记录锁实例
fcntl 函数是一个相当常用的对文件进行加锁操作的函数. 文件锁包括强制锁.建议锁.记录锁, 不过一般系统和内核都是用的强制锁. 以下为记录锁的例子:------------------------ ...
- Linux进程同步之记录锁(fcntl)
记录锁相当于线程同步中读写锁的一种扩展类型,可以用来对有亲缘或无亲缘关系的进程进行文件读与写的同步,通过fcntl函数来执行上锁操作.尽管读写锁也可以通过在共享内存区来进行进程的同步,但是fcntl记 ...
随机推荐
- EffectiveJava(3)使用是由构造器或枚举类型强化singleton
使用是由构造器或枚举类型强化singleton 普通singleton:借助AccessibleObject.setAssess方法,通过反射机制调用私有构造器.抵御这种攻击可以修改构造器,让其再被要 ...
- Node.js 网页瘸腿稍强点爬虫再体验
这回爬虫走得好点了,每次正常读取文章数目总是一样的,但是有程序僵住了情况,不知什么原因. 代码如下: // 内置http模块,提供了http服务器和客户端功能 var http=require(&qu ...
- 基于WPF系统框架设计(2)-Fluent Ribbon之HelloWorld
Fluent/Ribbon是微软在其最新桌面操作系统Windows 7中使用的图形用户界面. Windows平台的进化,伴随着系统图形界面的重新设计.从Windows XP到Windows Vista ...
- 使用C++11封装线程池ThreadPool
读本文之前,请务必阅读: 使用C++11的function/bind组件封装Thread以及回调函数的使用 Linux组件封装(五)一个生产者消费者问题示例 线程池本质上是一个生产者消费者模型,所 ...
- Django的Form、CSRF、cookie和session
Django是一个大而全的web框架,为我们提供了很多实用的功能,本文主要介绍Form.CSRF.cookie和session 一.Form 在web页面中form表单是重要的组成部分,为了数据安全和 ...
- Android应用程序快速更换包名的方法
今天遇到需要修改Android包名的问题,开始试了直接修改包名,会出现一些错误 1.layout中直接引用类的地方 2.Manifest中对gen定义的地方 经测试,总结出一下方法: 1.关闭自动编译 ...
- react-native 路由 react-native-router-flux
引言 react-native-router-flux 是一个基于 react-navigation 路由框架,进一步简化了页面跳转的步骤,并且一直随着 react-navigation升级更新版本. ...
- SearchView的全面解析
代码地址如下:http://www.demodashi.com/demo/12535.html 前言 今天来讲讲searchView的使用,这里讲的searchView是引用android.suppo ...
- 操作log.py
# 把双数日期的日志,里面给随便写点东西# 1.获取到log目录下的所有文件os.walk()# 2.根据文件名来判断,是否是双数日期,分割字符串,取到日期# 3.12%2==0# 4.打开这个文件 ...
- 工作总结 npoi 模板 导出公式 excel
Apache POI(5):公式(formula) Apache POI(5):公式(formula) 2016年08月01日 17:44:49 阅读数:1145 package com.hthk ...