记录锁相当于线程同步中读写锁的一种扩展类型,可以用来对有亲缘或无亲缘关系的进程进行文件读与写的同步,通过fcntl函数来执行上锁操作。尽管读写锁也可以通过在共享内存区来进行进程的同步,但是fcntl记录上锁往往更容易使用,且效率更高。
记录锁的功能:当一个进程正在读或修改文件的某个部分是,它可以阻止其他进程修改同一文件区。如果我们在用读写锁对一个文件进行加锁。有2个读用户,一个写用户,在读用户读取的时候,写用户是不能操作该文件的。但是读用户也许只需要读取文件前面20个字节的内容,
而写用户需要操作文件末尾的20个字节,这种场景下其实读用户和写用户是可以同时进行的。但是读写锁却不能满足,因此拉长了处理时间。
因此记录锁有如下3个特点:
      记录锁不仅仅可以用来同步不同进程对同一文件的操作,还可以通过对同一文件加记录锁,来同步不同进程对某一共享资源的访问,如共享内存,I/O设备。
    • 对于劝告性上锁,当一个进程通过上锁对文件进行操作时,它不能阻止另一个非协作进程对该文件的修改。
    • 即使是强制性上锁,也不能完全保证该文件不会被另一个进程修改。因为强制性锁对unlink函数没有影响,所以一个进程可以先删除该文件,然后再将修改后的内容保存为同一文件来实现修改

函数接口:
//根据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会执行不同的操作,fcntl根据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, 阻塞设置一把锁
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 命令有效) */8
      /*...*/
};
F_SETLK:获取(l_type为F_RDLCK或F_WRLCK)或释放由lock指向flock结构所描述的锁,如果无法获取锁时,该函数会立即返回一个EACCESS或EAGAIN错误,而不会阻塞。
F_SETLKW:F_SETLKW和F_SETLK的区别是,无法设置锁的时候,调用线程会阻塞到该锁能够授权位置。
F_GETLK:IF_GETLK主要用来检测是否有某个已存在锁会妨碍将新锁授予调用进程,如果没有这样的锁,lock所指向的flock结构的l_type成员就会被置成F_UNLCK,否则已存在的锁的信息将会写入lock所指向的flock结构中
这里需要注意的是,用F_GETLK测试能否建立一把锁,然后接着用F_SETLK或F_SETLKW企图建立一把锁,由于这两者不是一个原子操作,所以不能保证两次fcntl之间不会有另外一个进程插入并建立一把相关的锁,从而使一开始的测试情况无效。所以一般不希望上锁时阻塞,会直接通过调用F_SETLK,并对返回结果进行测试,以判断是否成功建立所要求的锁。
记录锁相当于读写锁的一种扩展类型,记录锁和读写锁一样也有两种锁:共享读锁(F_RDLCK)和独占写锁(F_WRLCK)。在使用规则上和读写锁也基本一样:
    • 文件给定字节区间,多个进程可以有一把共享读锁,即允许多个进程以读模式访问该字节区;
    • 文件给定字节区间,只能有一个进程有一把独占写锁,即只允许有一个进程已写模式访问该字节区;
    • 文件给定字节区间,如果有一把或多把读锁,不能在该字节区再加写锁,同样,如果有一把写锁,不能再该字节区再加任何读写锁。
如下表所示:

需要说明的是:上面所阐述的规则只适用于不同进程提出的锁请求,并不适用于单个进程提出的多个锁请求。即如果一个进程对一个文件区间已经有了一把锁,后来该进程又试图在同一文件区间再加一把锁,那么新锁将会覆盖老锁

下面来进行测试:

测试一:在同一进程中测试能否在加写锁后,继续加读写锁

#include <fcntl.h>

#include <stdio.h>

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
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_SETLKW, &lock) != 0)

{

return
-1;

}

return 0;

}

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 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)

{

struct
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;

}

#include
<stdio.h>

#include
<stdlib.h>

#include
<unistd.h>

#include
<sys/wait.h>

#include
<sys/types.h>

#include
"func1.h"

#define FILE_MODE (S_IRUSR
| S_IWUSR | S_IRGRP | S_IROTH)

int main()

{

char
*FILE_PATH='/home/zhf/codeblocks_prj/linux_c/1.txt';

pid_t
t1,t2;

int
fd=open(FILE_PATH,O_RDWR | O_CREAT,FILE_MODE);

writew_lock(fd);

t1=lock_test(fd,F_WRLCK,SEEK_SET,0,0);

t2=lock_test(fd,F_RDLCK,SEEK_SET,0,0);

printf("pid_t1=%ld\n",t1);

printf("pid_t2=%ld\n",t2);

unlock(fd);

return 0;

}

运行结果如下:可以看到反馈的进程号都是一样的。证明同一个进程锁是可以覆盖前一个的。

测试二:父进程中加写锁后,然后再子进程中测试能否继续加读写锁

int main()

{

char *FILE_PATH='/home/zhf/codeblocks_prj/linux_c/1.txt';

pid_t t1,t2,child;

int fd=open(FILE_PATH,O_RDWR | O_CREAT,FILE_MODE);

writew_lock(fd);

if(child=fork() == 0)

{

t1=lock_test(fd,F_WRLCK,SEEK_SET,0,0);

t2=lock_test(fd,F_RDLCK,SEEK_SET,0,0);

printf("pid_t1=%ld\n",t1);

printf("pid_t2=%ld\n",t2);

printf("pid_child=%ld\n",child);

}

sleep(3);

unlock(fd);

return 0;

}

测试结果如下:表明不同进程不能对已加写锁的同一文件区间,获得加锁权限;

前面的表格可以看出在已经获取写锁的情况,只能允许读锁,而拒绝写锁。下面这个代码来测试下这种情况

int main()

{

char *FILE_PATH='/home/zhf/codeblocks_prj/linux_c/1.txt';

pid_t t1,t2,child;

int ret;

int fd=open(FILE_PATH,O_RDWR | O_CREAT,FILE_MODE);

readw_lock(fd);

if(fork() == 0)

{

printf("child1 try to get write lock\n");

ret=writew_lock(fd);

if (ret >= 0)

{

printf("child1 get write lock\n");

unlock(fd);

printf("child1 release write lock\n");

exit(0);

}

else

{

printf("child1 get write lock failed\n");

}

exit(0);

}

if(fork() == 0)

{

sleep(3);

printf("child2 try to get read lock\n");

readw_lock(fd);

printf("child2 get read lock\n");

unlock(fd);

printf("child2 release read lock\n");

exit(0);

}

sleep(10);

unlock(fd);

return 0;

}

执行结果:

可以看出在父进程获取读锁的情况下,child1进程对文件同一区域获取写锁失败,而child2进程可以获取到读锁

下面的代码来测试父进程获得写锁,child1试图获取写锁,child2试图获取读锁

int main()

{

char *FILE_PATH='/home/zhf/codeblocks_prj/linux_c/1.txt';

pid_t t1,t2,child;

int ret1,ret2;

int fd=open(FILE_PATH,O_RDWR | O_CREAT,FILE_MODE);

writew_lock(fd);

if(fork() == 0)

{

printf("child1 try to get write lock\n");

ret1=writew_lock(fd);

if (ret1 >= 0)

{

printf("child1 get write lock\n");

unlock(fd);

printf("child1 release write lock\n");

exit(0);

}

else

{

printf("child1 get write lock failed\n");

}

exit(0);

}

if(fork() == 0)

{

sleep(3);

printf("child2 try to get read lock\n");

ret2=readw_lock(fd);

if(ret2 >= 0)

{

printf("child2 get read lock\n");

unlock(fd);

printf("child2 release read lock\n");

}

else

{

printf("child2 get read lock failed\n");

}

exit(0);

}

sleep(10);

unlock(fd);

return 0;

}

执行结果:

child1进程获取写锁失败,child2进程获取读锁失败

linux c编程:记录锁的更多相关文章

  1. Linux进程同步之记录锁(fcntl)

    记录锁相当于线程同步中读写锁的一种扩展类型,可以用来对有亲缘或无亲缘关系的进程进行文件读与写的同步,通过fcntl函数来执行上锁操作.尽管读写锁也可以通过在共享内存区来进行进程的同步,但是fcntl记 ...

  2. inux c编程:记录锁

    记录锁的功能:当一个进程正在读或修改文件的某个部分是,它可以阻止其他进程修改同一文件区.对于这个功能阐述我认为有三点要解释的: 记录锁不仅仅可以用来同步不同进程对同一文件的操作,还可以通过对同一文件加 ...

  3. linux 文件记录锁详解

    一: linux记录锁更恰当的称呼应该是范围锁,它是对文件某个范围的锁定. 关于记录锁的功能就是fcntl提供的第五个功能,具体使用如下: int fcntl(int fd, int cmd, str ...

  4. Linux Socket编程

    “一切皆Socket!” 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. ——有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览 ...

  5. Linux Socket编程(不限Linux)【转】

    转自:http://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html “一切皆Socket!” 话虽些许夸张,但是事实也是,现在的网络编程几 ...

  6. Linux Socket编程(不限Linux)

    "一切皆Socket!" 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. --有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信 ...

  7. Linux系统编程@进程通信(一)

    进程间通信概述 需要进程通信的原因: 数据传输 资源共享 通知事件 进程控制 Linux进程间通信(IPC)发展由来 Unix进程间通信 基于System V进程间通信(System V:UNIX系统 ...

  8. 【转】Linux Soclet编程

    原文地址:http://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html “一切皆Socket!” 话虽些许夸张,但是事实也是,现在的网络编 ...

  9. 高级I/O之记录锁

    若两个人同时编辑一个文件,其后果将如何呢?在很多UNIX系统中,该文件的最后状态取决于写该文件的最后一个进程.但是对于有些应用程序(例如数据库),进程有时需要确保它正在单独写一个文件.为了向进程提供这 ...

随机推荐

  1. 浅谈myeclipse2015做cordova/phonegap开发

    近期公司项目经理要求用cordova写安卓程序,之前对cordova什么都不懂,然后从网上搜啊搜,好歹有所了解了,感觉做web开发的比較合适,属于三种开发模式的第三种. myeclipse2015有个 ...

  2. 机器学习算法( 二、K - 近邻算法)

    一.概述 k-近邻算法采用测量不同特征值之间的距离方法进行分类. 工作原理:首先有一个样本数据集合(训练样本集),并且样本数据集合中每条数据都存在标签(分类),即我们知道样本数据中每一条数据与所属分类 ...

  3. What is Web Application Architecture? How It Works, Trends, Best Practices and More

    At Stackify, we understand the amount of effort that goes into creating great applications. That’s w ...

  4. CAP定理(原则)以及BASE理论

    CAP定理(原则)以及BASE理论 CAP定理(原则)概念 CAP原则又称CAP定理,指的是在一个分布式系统中, Consistency(一致性). Availability(可用性).Partiti ...

  5. logback参考配置

    logback配置 <?xml version="1.0" encoding="UTF-8"?> <configuration> < ...

  6. COCOS2D-HTML5 开发之二】cocos2d-html5项目定义成员,局部变量,函数笔记随笔

    本站文章均为李华明Himi原创,转载务必在明显处注明:(作者新浪微博:@李华明Himi) 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/cocos2d- ...

  7. .net 编码常见问题

    问题一: windows service 启动时需要用死循环来控制程序,如果不开启异步,死循环会导致windows sevice 程序无法启动,代码情况如下 protected override vo ...

  8. 使用AngularJS创建应用的5个框架

    [导读] 如果你计划使用AngularJS创建你的Web应用,那现在就开始吧.你不需要有任何的恐惧和担心,因为现在有很多的框架都可以很好地支持AngularJS.这些框架都有事先安装的Web组件,使用 ...

  9. log4j详解(转)

    转载自:http://blog.csdn.net/evatian/article/details/8501517 Log4j – 如何配置多个logger? 分类: java2013-01-14 16 ...

  10. openwrt U盘启动

    参考链接: http://m.blog.csdn.net/blog/zcynical/44892785