📄 12.htm
字号:
</p>
<p>多精灵进程都把它们的进程ID写到一个它们各自专用的一个PID文件上。当系统停
</p>
<p>机时,可以从这些文件中取用这些精灵进程的进程ID。防止一个精灵进程有多份副
</p>
<p>本同时运行的方法是:在精灵进程开始运行时,在它的进程ID文件上企图设置一把
</p>
<p>写锁。如果在它运行时一直保持这把锁,则就不可能再起动它的其它副本。程序1
</p>
<p>2.5实现了这一技术。 </p>
<p>因为进程ID文件可能包含以前的精灵进程ID,而且其长度还可能长于当前进程的I
</p>
<p>D,例如该文件中以前的内容可能是12345\n,而现在的进程ID是654,我们希望该
</p>
<p>文件现在只包含654\n,而不是654\n5,所以在写该文件时,先将其截短为0。注意
</p>
<p>,要在设置了锁之后再调用截短文件长度的函数ftruncate。在调用open时不能指
</p>
<p>定O_TRUNC,因为这样做会在有一个这种精灵进程运行并对该文件加了锁时也会使
</p>
<p>该文件截短为0。(如果使用强制性锁而不是建议性锁,则可使用O_TRUNC。在本节
</p>
<p>最后部分将讨论强制性锁。) </p>
<p>在本实例中,也对该描述符设置exec时关闭(close-on-exec)标志。这是因为精
</p>
<p>灵进程常常fork并exec其它进程,无需在另一个进程中使该文件也处在打开状态。
</p>
<p>#include <sys/types.h> </p>
<p>#include <sys/stat.h> </p>
<p>#include <errno.h> </p>
<p>#include <fcntl.h> </p>
<p>#include "ourhdr.h" </p>
<p>#define PIDFILE "daemon.pid" </p>
<p>int </p>
<p>main(void) </p>
<p>{ </p>
<p>int fd, val; </p>
<p>char buf[10]; </p>
<p>if ( (fd = open(PIDFILE, O_WRONLY | O_CREAT, FILE_MODE)) < 0) </p>
<p>err_sys("open error"); </p>
<p>/* try and set a write lock on the entire file */ </p>
<p>if (write_lock(fd, 0, SEEK_SET, 0) < 0) { </p>
<p>if (errno == EACCES || errno == EAGAIN) </p>
<p>exit(0); /* gracefully exit, daemon is already ru </p>
<p>ning */ </p>
<p>else </p>
<p>err_sys("write_lock error"); </p>
<p>} </p>
<p>/* truncate to zero length, now that we have the lock */ </p>
<p>if (ftruncate(fd, 0) < 0) </p>
<p>err_sys("ftruncate error"); </p>
<p>/* and write our process ID */ </p>
<p>sprintf(buf, "%d\n", getpid()); </p>
<p>if (write(fd, buf, strlen(buf)) != strlen(buf)) </p>
<p>err_sys("write error"); </p>
<p>/* set close-on-exec flag for descriptor */ </p>
<p>if ( (val = fcntl(fd, F_GETFD, 0)) < 0) </p>
<p>err_sys("fcntl F_GETFD error"); </p>
<p>val |= FD_CLOEXEC; </p>
<p>if (fcntl(fd, F_SETFD, val) < 0) </p>
<p>err_sys("fcntl F_SETFD error"); </p>
<p>/* leave file open until we terminate: lock will be held */ </p>
<p>/* do whatever ... */ </p>
<p>exit(0); </p>
<p>} </p>
<p>程序12.5 精灵进程阻止其多份副本同时运行的起动代码 </p>
<p>实例 </p>
<p>在相对文件尾端加锁或解锁时需要特别小心。大多数实现按照I_whence的SEEK_CU
</p>
<p>R或SEEN_END值,用文件当前位置或当前长度以及l_start得到绝对的文件位移量。
</p>
<p>但是,通常我们需要相对于文件的当前位置或当前长度指定一把锁。
</p>
<p>程序12.6写一个文件,一次一个字节。每次循环中,从文件当前尾端开始处加锁直到
</p>
<p>将来可能扩充到的尾端为止(最后一个参数,长度,指定为0),然后写1个字节。
</p>
<p>然后解除这把锁,写另一个字节。如果系统用"从当前尾端开始,直到将来可能扩
</p>
<p>充的尾端"这种记法来跟踪锁,那么这段程序能够正常工作。但是如果系统将相对
</p>
<p>位移量变换成绝对位移量就会有问题。在SVR4中运行此程序的确会发生问题:
</p>
<p>#include <sys/types.h> </p>
<p>#include <sys/stat.h> </p>
<p>/* set close-on-exec flag for descriptor */ </p>
<p>if ( (val = fcntl(fd, F_GETFD, 0)) < 0) </p>
<p>err_sys("fcntl F_GETFD error"); </p>
<p>val |= FD_CLOEXEC; </p>
<p>if (fcntl(fd, F_SETFD, val) < 0) </p>
<p>err_sys("fcntl F_SETFD error"); </p>
<p>/* leave file open until we terminate: lock will be held */ </p>
<p>/* do whatever ... */ </p>
<p>exit(0); </p>
<p>} </p>
<p>程序12.5 精灵进程阻止其多份副本同时运行的起动代码 </p>
<p>实例 </p>
<p>在相对文件尾端加锁或解锁时需要特别小心。大多数实现按照I_whence的SEEK_CU
</p>
<p>R或SEEN_END值,用文件当前位置或当前长度以及l_start得到绝对的文件位移量。
</p>
<p>但是,通常我们需要相对于文件的当前位置或当前长度指定一把锁。
</p>
<p>程序12.6写一个文件,一次一个字节。每次循环中,从文件当前尾端开始处加锁直到
</p>
<p>将来可能扩充到的尾端为止(最后一个参数,长度,指定为0),然后写1个字节。
</p>
<p>然后解除这把锁,写另一个字节。如果系统用"从当前尾端开始,直到将来可能扩
</p>
<p>充的尾端"这种记法来跟踪锁,那么这段程序能够正常工作。但是如果系统将相对
</p>
<p>位移量变换成绝对位移量就会有问题。在SVR4中运行此程序的确会发生问题:
</p>
<p>#include <sys/types.h> </p>
<p>#include <sys/stat.h> </p>
<p>#include <fcntl.h> </p>
<p>#include "ourhdr.h" </p>
<p>int </p>
<p>main(void) </p>
<p>{ </p>
<p>int i, fd; </p>
<p>if ( (fd = open("temp.lock", O_RDWR | O_CREAT | O_TRUNC, </p>
<p>FILE_MODE)) < 0) </p>
<p>err_sys("open error"); </p>
<p>for (i = 0; i < 1000000; i++) { /* try to write 2 Mbytes */ </p>
<p>/* lock from current EOF to EOF */ </p>
<p>if (writew_lock(fd, 0, SEEK_END, 0) < 0) </p>
<p>err_sys("writew_lock error"); </p>
<p>if (write(fd, &fd, 1) != 1) </p>
<p>err_sys("write error"); </p>
<p>if (un_lock(fd, 0, SEEK_END, 0) < 0) </p>
<p>err_sys("un_lock error"); </p>
<p>if (write(fd, &fd, 1) != 1) </p>
<p>err_sys("write error"); </p>
<p>} </p>
<p>exit(0); </p>
<p>赋与l_start)改换成所写字节数的负值(在本程序中是-1)。这就使得un_lock去
</p>
<p>除上次加的锁。 </p>
<p>建议性锁和强制性锁 </p>
<p>考虑数据库存取例程序。如果该库中所有函数都以一致的方法处理记录锁,则我们
</p>
<p>称使用这些函数存取数据库的任何进程集为合作进程。如果这些函数是唯一的用来
</p>
<p>存取数据库的函数,那么它们使用建议性锁是可行的。但是建议性锁并不能阻止对
</p>
<p>数据库文件有写许可权的任何其它进程写数据库文件。不使用协同一致的方法(数
</p>
<p>据库存取例程库)来存取数据库的进程是一个非合作进程。 </p>
<p>强制性锁机制中,系统核对每一个open、read和write都要检查调用进程对正在存
</p>
<p>取的文件是否违背了某一把锁的作用。 </p>
<p>对一个特定文件打开其设置_组_ID位,关闭其组_执行位则对该文件启动了强制性
</p>
<p>锁机制。(回忆程序4.4)。因为当组_ 执行位关闭时,设置_组_ID位不再有意义
</p>
<p>,所以SVR3的设计者借用两者的这种组合来指定对一个文件的锁是强制性的而非建
</p>
<p>议性的。 </p>
<p>如果一个进程试图读、写一个强制性锁起作用的文件,而欲读、写的部分又由其它
</p>
<p>进程加上了读、写锁,此时会发生什么呢?对这一问题的回答取决于三方面的因素
</p>
<p>:操作类型(read或write),其它进程保有的锁的类型(读锁或写锁),以及有
</p>
<p>关描述符是阻塞还是非阻塞的。图12.7显示了这八种可能性。 </p>
<p>图12.7 强制性锁对其它进程读、写的影响 </p>
<p>除了图12.7中的read,write函数,其它进程的强制性锁也会对open函数产生影响。
</p>
<p>通常,即使正在打开的文件具有强制性记录锁,该打开操作也会成功。下面的rea
</p>
<p>d或write依从于图12.7中所示的规则。但是,如果欲打开的文件具有强制性锁(读
</p>
<p>锁或写锁),而且open调用中的flag为O_TRUNC或O_CREAT,则不论是否指定O_NON
</p>
<p>BLOCK,open都立即出错返回,erron设置为EAGAIN。(对O_TRUNC情况出错返回是有
</p>
<p>意义的,因为其它进程对该文件持有读、写锁,所以不能将其截短为0。对O_CREA
</p>
<p>T情况在返回时也设置erron则无意义,因为该标志的意义是如果该文件不存在则创
</p>
<p>建,由于其它进程对该文件持有记录锁,因而该文件肯定是存在的。)
</p>
<p>这种处理方式可能导致令人惊异的结果。我们曾编写过一个程序,它打开一个文件
</p>
<p>(其mode指定为强制性锁),然后对该文件的整体设置一把读锁,然后进入睡眠一
</p>
<p>段时间。在这段睡眠时间内,用某些常规的Unix程序和操作符对该文件进行处理,
</p>
<p>发现下列情况: </p>
<p>l 可用ed编辑程序对该文件进行编辑操作,而且编辑结果写回磁盘!强制性记录锁
</p>
<p>对此毫无影响。对ed操作进行跟踪分析发现,ed将新内容写到一个临时文件中,然
</p>
<p>后删除原文件,最后将临时文件名改名为原文件名。于是,发现强制性锁机制对u
</p>
<p>nlink函数没有影响。 </p>
<p>在SVR4中,用truss(1)命令可以得到一个进程的系统调用跟踪信息,在4.3+BSD中
</p>
<p>,则使用ktrace(1)和kdump(1)命令。 </p>
<p>l 不能用vi编辑程序编辑该文件。vi可以读该文件,但是如果试图将新的数据写到
</p>
<p>该文件中,则出错返回(EAGAIN)。如果试图将新数据添加到该文件中,则write
</p>
<p>阻塞。vi的这种行为与所希望的一样。 </p>
<p>l 使用KornShell的>和》算符重写或添写到该文件中,产生出错信息"cannot
cre </p>
<p>at"。 </p>
<p>l 在Bourne Shell下使用>算符出错,但是使用》算符则阻塞,在删除了强制性锁
</p>
<p>后再继续进行处理。(执行添加操作所产生的区别是因为:Korn
Shell以O_CREAT </p>
<p>和O_APPEND标志打开文件,而上面已提及指定O_CREAT会产生出错返回。但是,Bo
</p>
<p>urne Shell在该文件已存在时并不指定O_CREAT,所以open成功,而下一个write则
</p>
<p>阻塞。) </p>
<p>从这样一个例子中可见,在使用强制性锁时还需有所警惕。 </p>
<p>一个别有用心的用户可以对大家都可读的文件加一把读锁(强制性),这样就能阻
</p>
<p>止任何其它人写该文件(当然,该文件应当是强制性锁机制起作用的,这可能要求
</p>
<p>该用户能够更改该文件的许可权位。)考虑一个数据库文件,它是大家都可读的,
</p>
<p>并且是强制性锁机制起作用的。如果一个别有用心的用户对该整个文件保有一把读
</p>
<p>锁,则其它进程不能再写该文件。 </p>
<p>实例 </p>
<p>程序12.7 检查一个系统是否支持强制性锁机制。 </p>
<p>#include <sys/types.h> </p>
<p>#include <sys/stat.h> </p>
<p>#include <sys/wait.h> </p>
<p>#include <errno.h> </p>
<p>#include <fcntl.h> </p>
<p>#include "ourhdr.h" </p>
<p>int </p>
<p>main(void) </p>
<p>{ </p>
<p>int fd; </p>
<p>pid_t pid; </p>
<p>char buff[5]; </p>
<p>struct stat statbuf; </p>
<p>if ( (fd = open("templock", O_RDWR | O_CREAT | O_TRUNC, </p>
<p>FILE_MODE)) < 0) </p>
<p>err_sys("open error"); </p>
<p>if (write(fd, "abcdef", 6) != 6) </p>
<p>err_sys("write error"); </p>
<p>/* turn on set-group-ID and turn off group-execute */ </p>
<p>if (fstat(fd, &statbuf) < 0) </p>
<p>err_sys("fstat error"); </p>
<p>if (fchmod(fd, (statbuf.st_mode & ~S_IXGRP) | S_ISGID) < 0) </p>
<p>err_sys("fchmod error"); </p>
<p>TELL_WAIT(); </p>
<p>if ( (pid = fork()) < 0) { </p>
<p>err_sys("fork error"); </p>
<p>} else if (pid > 0) { /* parent */ </p>
<p>/* write lock entire file */ </p>
<p>if (write_lock(fd, 0, SEEK_SET, 0) < 0) </p>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -