- 浏览: 142320 次
文章分类
最新评论
记录锁的功能是:当有进程正在读或修改文件的某个部分时,用以阻止其他进程修改同一文件区。下表列出了各种系统提供的不同形式的记录锁。
本文只介绍 POSIX.1 标准的 fcntl 锁。在fcntl 函数介绍一节我们曾介绍了 fcntl 函数在设置打开文件标志时的作用,这里将继续它在记录锁上扮演的角色。不过首先还是先来重温一下它的函数原型。
在此先说明 flock 的结构如下。
(1)所希望的锁类型:F_RDLCK(共享读锁)、F_WRLCK(独占性写锁)或 F_UNLCK(解锁一个区域)。
(2)要加锁或解锁区域的起始字节偏移量(l_start 和 l_whence,同 lseek 函数)。
(3)区域的字节长度。
(4)进程 l_pid 持有的锁能阻塞当前进程(仅由 F_GETLK 返回)。
关于加锁或解锁区域的说明还要注意下列几项规则。
(1)锁可以在当前文件尾端或者越过尾端处开始,但不能在文件起始位置之前开始。
(2)如若 l_len 为 0,则表示锁的范围可以扩展到最大可能偏移量。这意味着不管向文件中追加了多少数据,它们都可以处于锁的范围内,而且起始位置可以是文件中的任意一个位置。
(3)要对整个文件加锁,可以设置 l_start 和 l_whence 指向文件的起始位置,并且指定 l_len 为 0。
另外,关于读锁和写锁之间的兼容性规则如下表所示。
注意,这里说明的兼容性规则使用于不同进程提出的锁请求,但并不适用于单个进程提出的多个锁请求。如果一个进程对一个文件区间已经有了一把锁,若它又企图在同一区间再加一把锁,那么新锁将替换已有的锁,而不管是什么类型的锁。另外,加读锁时,描述符 fd 必须是读打开;加写锁时,该描述符 fd 必须是写打开。
接下来再来说明一下 cmd 参数用于记录锁时使用的 3 个值。
* F_GETLK:判断由 flockptr 所描述的锁是否会被另外一把锁所排斥(阻塞)。如果存在一把锁会阻止创建由 flockptr 所描述的锁,则该现有锁的信息将重写 flockptr 指向的信息。否则,除了 l_type 被设置为 F_UNLCK 之外,flockptr 所指向结构中的其他信息保持不变。
* F_SETLK:设置由 flockptr 所描述的锁。如果试图获取一把违反兼容性规则的锁,那么 fcntl 会立即出错返回,并把 errno 设置为 EACCES 或 EAGAIN(多数实现返回 EAGAIN)。此命令也用来清除由 flockptr 指定的锁(l_type 为 F_UNLCK)。
* F_SETLKW:这个命令是 F_SETLK 的阻塞版本。如果请求的锁因另一个进程当前已经对所请求区域的某部分进行了加锁而不能被授予,那么调用进程就会进入休眠,直到请求创建的锁可用或者休眠被信号中断。
应当了解,用 F_GETLK 测试后再企图用 F_SETLK 或 F_SETLKW 来建立锁的操作不是一个原子操作。此外,如果不希望在等待锁变为可用时产生阻塞,就必须处理由 F_SETLK 返回的可能的出错。另外,当读锁请求比较频繁时,可能使写锁请求产生饥饿现象。
在设置或释放文件上的一把锁时,系统会按要求组合或分裂相邻区。例如,若第 100~199 字节是加锁的区,然后解锁第 150 字节,则内核将维持两把锁,一把用于第 100~149 字节,另一把用于第 151~199 字节。当又对第 150 字节加锁时,系统将会再把 3 个相邻的加锁区合并成一个区。
关于记录锁的自动继承和释放有以下 3 条规则:
(1)锁与进程和文件两者相关联。这表示:当一个进程终止时,它所建立的锁会全部释放;以及无论何时 close 一个文件描述符时,该进程在这一描述符引用的文件上设置的任何一把锁都会释放,而不管是通过哪一个文件描述符设置的。
(2)由 fork 产生的子进程不继承父进程所设置的记录锁,以免父子进程同时写一个文件。
(3)在执行 exec 后,新程序可以继承原执行程序的锁。但如果一个文件描述符设置了执行时关闭标志,那么当作为 exec 的一部分关闭该文件描述符时,将释放相应文件的所有锁。
可以通过观察 FreeBSD 实现中使用的数据结构来进一步理解这几条规则。考虑一个进程,它执行了下列语句:
fd1 = open(pathname, ...);
write_lock(fd1, ...); // parent write locks
if((pid = fork()) > 0){
fd2 = dup(fd1);
fd3 = open(pathname, ...);
}else if(pid == 0){
read_lock(fd1, ...); // child read locks
}
pause();
下图显示了父进程和子进程执行 pause 后的数据结构情况。
图中的 lockf 结构代表记录锁,每个 lockf 结构都描述了一个给定进程的一个加锁区域。图中显示了两个 lockf 结构,一个是父进程调用 write_lock 形成的,另一个是子进程调用 read_lock 形成的。每一个都包含了相应的进程 ID。在父进程中,关闭 fd1、fd2 或 fd3 中的任意一个都将释放由父进程设置的写锁。在关闭其中的任意一个时,内核会从该描述符所关联的 i 节点开始,逐个检查 lockf 链接表中的各项,找到并释放由调用进程持有的各把锁,而并不关心父进程是用其中的哪一个来设置这把锁的。
在前面守护进程惯例一节中曾提到可以使用一把文件锁来保证只有守护进程的唯一副本在运行,下面就是其中用到的函数 lockfile 的实现。
本文只介绍 POSIX.1 标准的 fcntl 锁。在fcntl 函数介绍一节我们曾介绍了 fcntl 函数在设置打开文件标志时的作用,这里将继续它在记录锁上扮演的角色。不过首先还是先来重温一下它的函数原型。
#include <fcntl.h> int fcntl(int fd, int cmd, .../* struct flock *flockptr */); /* 返回值:若成功,则依赖于 cmd;否则,返回 -1 */ struct flock{ short l_type; // F_RDLCK, F_WRLCK, or F_UNLCK short l_whence; // SEEK_SET, SEEK_CUR, or SEEK_END off_t l_start; // offset in bytes, relative to l_whence off_t l_len; // length, in bytes; 0 means lock to EOF pid_t l_pid; // returned with F_GETLK };
在此先说明 flock 的结构如下。
(1)所希望的锁类型:F_RDLCK(共享读锁)、F_WRLCK(独占性写锁)或 F_UNLCK(解锁一个区域)。
(2)要加锁或解锁区域的起始字节偏移量(l_start 和 l_whence,同 lseek 函数)。
(3)区域的字节长度。
(4)进程 l_pid 持有的锁能阻塞当前进程(仅由 F_GETLK 返回)。
关于加锁或解锁区域的说明还要注意下列几项规则。
(1)锁可以在当前文件尾端或者越过尾端处开始,但不能在文件起始位置之前开始。
(2)如若 l_len 为 0,则表示锁的范围可以扩展到最大可能偏移量。这意味着不管向文件中追加了多少数据,它们都可以处于锁的范围内,而且起始位置可以是文件中的任意一个位置。
(3)要对整个文件加锁,可以设置 l_start 和 l_whence 指向文件的起始位置,并且指定 l_len 为 0。
另外,关于读锁和写锁之间的兼容性规则如下表所示。
注意,这里说明的兼容性规则使用于不同进程提出的锁请求,但并不适用于单个进程提出的多个锁请求。如果一个进程对一个文件区间已经有了一把锁,若它又企图在同一区间再加一把锁,那么新锁将替换已有的锁,而不管是什么类型的锁。另外,加读锁时,描述符 fd 必须是读打开;加写锁时,该描述符 fd 必须是写打开。
接下来再来说明一下 cmd 参数用于记录锁时使用的 3 个值。
* F_GETLK:判断由 flockptr 所描述的锁是否会被另外一把锁所排斥(阻塞)。如果存在一把锁会阻止创建由 flockptr 所描述的锁,则该现有锁的信息将重写 flockptr 指向的信息。否则,除了 l_type 被设置为 F_UNLCK 之外,flockptr 所指向结构中的其他信息保持不变。
* F_SETLK:设置由 flockptr 所描述的锁。如果试图获取一把违反兼容性规则的锁,那么 fcntl 会立即出错返回,并把 errno 设置为 EACCES 或 EAGAIN(多数实现返回 EAGAIN)。此命令也用来清除由 flockptr 指定的锁(l_type 为 F_UNLCK)。
* F_SETLKW:这个命令是 F_SETLK 的阻塞版本。如果请求的锁因另一个进程当前已经对所请求区域的某部分进行了加锁而不能被授予,那么调用进程就会进入休眠,直到请求创建的锁可用或者休眠被信号中断。
应当了解,用 F_GETLK 测试后再企图用 F_SETLK 或 F_SETLKW 来建立锁的操作不是一个原子操作。此外,如果不希望在等待锁变为可用时产生阻塞,就必须处理由 F_SETLK 返回的可能的出错。另外,当读锁请求比较频繁时,可能使写锁请求产生饥饿现象。
在设置或释放文件上的一把锁时,系统会按要求组合或分裂相邻区。例如,若第 100~199 字节是加锁的区,然后解锁第 150 字节,则内核将维持两把锁,一把用于第 100~149 字节,另一把用于第 151~199 字节。当又对第 150 字节加锁时,系统将会再把 3 个相邻的加锁区合并成一个区。
关于记录锁的自动继承和释放有以下 3 条规则:
(1)锁与进程和文件两者相关联。这表示:当一个进程终止时,它所建立的锁会全部释放;以及无论何时 close 一个文件描述符时,该进程在这一描述符引用的文件上设置的任何一把锁都会释放,而不管是通过哪一个文件描述符设置的。
(2)由 fork 产生的子进程不继承父进程所设置的记录锁,以免父子进程同时写一个文件。
(3)在执行 exec 后,新程序可以继承原执行程序的锁。但如果一个文件描述符设置了执行时关闭标志,那么当作为 exec 的一部分关闭该文件描述符时,将释放相应文件的所有锁。
可以通过观察 FreeBSD 实现中使用的数据结构来进一步理解这几条规则。考虑一个进程,它执行了下列语句:
fd1 = open(pathname, ...);
write_lock(fd1, ...); // parent write locks
if((pid = fork()) > 0){
fd2 = dup(fd1);
fd3 = open(pathname, ...);
}else if(pid == 0){
read_lock(fd1, ...); // child read locks
}
pause();
下图显示了父进程和子进程执行 pause 后的数据结构情况。
图中的 lockf 结构代表记录锁,每个 lockf 结构都描述了一个给定进程的一个加锁区域。图中显示了两个 lockf 结构,一个是父进程调用 write_lock 形成的,另一个是子进程调用 read_lock 形成的。每一个都包含了相应的进程 ID。在父进程中,关闭 fd1、fd2 或 fd3 中的任意一个都将释放由父进程设置的写锁。在关闭其中的任意一个时,内核会从该描述符所关联的 i 节点开始,逐个检查 lockf 链接表中的各项,找到并释放由调用进程持有的各把锁,而并不关心父进程是用其中的哪一个来设置这把锁的。
在前面守护进程惯例一节中曾提到可以使用一把文件锁来保证只有守护进程的唯一副本在运行,下面就是其中用到的函数 lockfile 的实现。
#include <unistd.h> #include <fcntl.h> int lockfile(int fd){ struct flock fl; fl.l_type = F_WRLCK; fl.l_start = 0; fl.l_whence = SEEK_SET; fl.l_len = 0; return fcntl(fd, F_SETLK, &fl); }
发表评论
-
打开伪终端设备
2018-07-09 20:50 1251在伪终端概述一节中已对 PTY进行了初步的介绍。尽管 ... -
伪终端概述
2018-06-02 11:05 1548伪终端就是指,一个应用程序看上去像一个终端,但事实上它 ... -
终端窗口大小和 termcap
2018-05-29 22:39 798多数 UNIX 系统都提供了一种跟踪当前终端窗口大小的 ... -
终端规范模式和非规范模式
2018-05-29 00:25 948终端规范模式很简单:发一个读请求,当一行已经输入后,终 ... -
终端标识
2018-05-23 11:18 568尽管控制终端的名字在多数 UNIX 系统上都是 /de ... -
波特率和行控制函数
2018-05-22 07:53 943虽然大多数终端设 ... -
终端属性和选项标志
2018-05-20 07:40 709tcgetattr 和 tcsetattr ... -
终端特殊输入字符
2018-05-17 06:33 814终端支持下表所示的特殊输入字符。 为了更改 ... -
终端 I/O 综述
2018-05-10 07:56 437终端设备可认为是由内核中的终端驱动程序控制的。每个终端 ... -
POSIX 信号量
2018-05-09 00:03 579在XSI IPC通信之信 ... -
XSI IPC 通信之共享存储
2018-04-25 07:18 946在XSI IPC通信之消息队列和XSI IPC通信之信 ... -
XSI IPC通信之信号量
2018-04-17 23:38 616在XSI IPC通信之消 ... -
XSI IPC通信之消息队列
2018-04-15 10:54 497消息队列是消息的链接表,存储在内核中,由消息队列标识符 ... -
XSI IPC 相似特征介绍
2018-02-08 23:48 484有 3 种称作 XSI IPC ... -
IPC 通信之 FIFO
2018-02-06 22:55 420FIFO 也被称为命名管道,未命名的管道只能在两个相关 ... -
IPC 通信之管道
2018-01-30 22:22 389管道是 UNIX 系统 IPC 的最古老但也是最常用的 ... -
readv/writev 函数及存储映射 I/O
2018-01-19 00:57 888readv 和 writev 函数可用于在一次函数调用 ... -
POSIX 异步 I/O
2018-01-16 21:33 454POSIX 异步 I/O 接口为对不同类型的文件进行异 ... -
守护进程惯例
2018-01-06 23:52 438UNIX 系统中,守护进程遵循下列通用惯例。 ... -
守护进程编写规则与出错记录
2017-12-26 01:53 454在编写守护进程程 ...
相关推荐
fcntl 函数可以用来获取和设置记录锁。其中,cmd 参数的值为 F_GETLK,可以获取记录锁;cmd 参数的值为 F_SETLK 或 F_SETLKW,可以设置记录锁。 在使用 fcntl 函数时,需要注意一些细节。例如,在修改文件描述符...
5. **记录锁**:除了基本的文件锁外,`fcntl`还支持记录锁(也称为行级锁),这意味着可以锁定文件的特定部分而不是整个文件。这对于处理大型数据文件时的并发控制特别有用。 6. **文件重定向**:在某些情况下,`...
5. **获取/设置记录锁** (`cmd = F_GETLK`, `F_SETLK` 或 `F_SETLKW`): - `F_GETLK`用于获取文件上的当前锁信息。 - `F_SETLK`用于设置一个锁,不等待。如果锁无法立即获得,则失败。 - `F_SETLKW`同样用于设置...
linux 编程实战,fcntl 文件记录锁的应用实例程序。。。。
12.3.2 fcntl记录锁 276 12.3.3 锁的隐含继承和释放 280 12.3.4 4.3+BSD的实现 281 12.3.5 建议性锁和强制性锁 284 12.4 流 288 12.4.1 流消息 289 12.4.2 putmsg和putpmsg函数 290 12.4.3 流ioctl操作 291 ...
12.3.2 fcntl记录锁 276 12.3.3 锁的隐含继承和释放 280 12.3.4 4.3+BSD的实现 281 12.3.5 建议性锁和强制性锁 284 12.4 流 288 12.4.1 流消息 289 12.4.2 putmsg和putpmsg函数 290 12.4.3 流ioctl操作 291 12.4.4 ...
12.3.2 fcntl记录锁 276 12.3.3 锁的隐含继承和释放 280 12.3.4 4.3+BSD的实现 281 12.3.5 建议性锁和强制性锁 284 12.4 流 288 12.4.1 流消息 289 12.4.2 putmsg和putpmsg函数 290 12.4.3 流ioctl操作 291 12.4.4 ...
12.3.2 fcntl记录锁 276 12.3.3 锁的隐含继承和释放 280 12.3.4 4.3+BSD的实现 281 12.3.5 建议性锁和强制性锁 284 12.4 流 288 12.4.1 流消息 289 12.4.2 putmsg和putpmsg函数 290 12.4.3 流ioctl操作 291 ...
12.3.2 fcntl记录锁 276 12.3.3 锁的隐含继承和释放 280 12.3.4 4.3+BSD的实现 281 12.3.5 建议性锁和强制性锁 284 12.4 流 288 12.4.1 流消息 289 12.4.2 putmsg和putpmsg函数 290 12.4.3 流ioctl操作 291 12.4.4 ...
12.3.2 fcntl记录锁 12.3.3 锁的隐含继承和释放 12.3.4 4.3+BSD的实现 12.3.5 建议性锁和强制性锁 12.4 流 12.4.1 流消息 12.4.2 putmsg和 putpmsg函数 12.4.3 流ioct1操作 12.4.4 write至流设备 12.4.5 写方式 12.4...
12.3.2 fcntl记录锁 276 12.3.3 锁的隐含继承和释放 280 12.3.4 4.3+BSD的实现 281 12.3.5 建议性锁和强制性锁 284 12.4 流 288 12.4.1 流消息 289 12.4.2 putmsg和putpmsg函数 290 12.4.3 流ioctl操作 291 12.4.4 ...
12.3.2 fcntl记录锁 276 12.3.3 锁的隐含继承和释放 280 12.3.4 4.3+BSD的实现 281 12.3.5 建议性锁和强制性锁 284 12.4 流 288 12.4.1 流消息 289 12.4.2 putmsg和putpmsg函数 290 12.4.3 流ioctl操作 291 12.4.4 ...
12.3.2 fcntl记录锁 276 12.3.3 锁的隐含继承和释放 280 12.3.4 4.3+BSD的实现 281 12.3.5 建议性锁和强制性锁 284 12.4 流 288 12.4.1 流消息 289 12.4.2 putmsg和putpmsg函数 290 12.4.3 流ioctl操作 291 12.4.4 ...
12.3.2 fcntl记录锁 276 12.3.3 锁的隐含继承和释放 280 12.3.4 4.3+BSD的实现 281 12.3.5 建议性锁和强制性锁 284 12.4 流 288 12.4.1 流消息 289 12.4.2 putmsg和putpmsg函数 290 12.4.3 流ioctl操作 291 12.4.4 ...
12.3.2 fcntl记录锁 276 12.3.3 锁的隐含继承和释放 280 12.3.4 4.3+BSD的实现 281 12.3.5 建议性锁和强制性锁 284 12.4 流 288 12.4.1 流消息 289 12.4.2 putmsg和putpmsg函数 290 12.4.3 流ioctl操作 291 12.4.4 ...
12.3.2 fcntl记录锁 276 12.3.3 锁的隐含继承和释放 280 12.3.4 4.3+BSD的实现 281 12.3.5 建议性锁和强制性锁 284 12.4 流 288 12.4.1 流消息 289 12.4.2 putmsg和putpmsg函数 290 12.4.3 流ioctl操作 291 12.4.4 ...
12.3.2 fcntl记录锁 276 12.3.3 锁的隐含继承和释放 280 12.3.4 4.3+BSD的实现 281 12.3.5 建议性锁和强制性锁 284 12.4 流 288 12.4.1 流消息 289 12.4.2 putmsg和putpmsg函数 290 12.4.3 流ioctl操作 291 12.4.4 ...
12.3.2 fcntl记录锁 276 12.3.3 锁的隐含继承和释放 280 12.3.4 4.3+BSD的实现 281 12.3.5 建议性锁和强制性锁 284 12.4 流 288 12.4.1 流消息 289 12.4.2 putmsg和putpmsg函数 290 12.4.3 流ioctl操作 291 12.4.4 ...
12.3.2 fcntl记录锁 276 12.3.3 锁的隐含继承和释放 280 12.3.4 4.3+BSD的实现 281 12.3.5 建议性锁和强制性锁 284 12.4 流 288 12.4.1 流消息 289 12.4.2 putmsg和putpmsg函数 290 12.4.3 流ioctl操作 291 12.4.4 ...