您的位置:首页 > 其它

由unix文件I/O引出的原子操作

2016-03-18 12:49 218 查看
第二遍看APUE,打算把知识点总结一下。

问题提出:当两个独立进程A、B同时对一个文件进行追加写操作时(假设这里打开时没有使用O_APPEND参数,即没有从文件尾打开),如果此时进程A调用lseek对该文件偏移量设为100(文件尾),同时内核切换为进程B,B将偏移量也设为100,然后B调用write函数使得B偏移量增加到110。之后内核切换为A,此时如果A调用write的话,还是会从100处开始写入,那么A之前写入的数据会被清除。

这里又涉及到打开文件的内核数据结构:



每个进程在进程表中都有一个记录项,其中含有一张文件描述符表,内容如上图。如果两个独立进程各自打开同一个文件,则有下图关系,分别有自己的文件表项(包含文件状态、偏移量等),APUE里讲的比较详细。



解决问题的方法是将这两个操作相对于其他进程而言成为原子操作,原子操作是不可分割的操作,在执行完毕时它不会被任何事件中断。

下面介绍一下原子操作:

对于单处理器原子操作

操作可以在一条指令内完成。因为进程的上下文切换是在总是在一条指令执行完成后,所以不会出现上述的并发问题。对于单处理器来说,一条处理器指令就是一个原子操作。

对于多处理器原子操作

在多处理器系统(Symmetric Multi-Processor,简称 SMP)中情况有所不同,由于系统中有多个处理器在独立的运行,即使在能单条指令中完成的操作也可能受到干扰。Intel x86指令集提供了指令前缀lock用于锁定前端串行总线(FSB),保证了指令执行时不会受到其他处理器的干扰。使用lock指令前缀后,处理器间对count内存的并发访问(读/写)被禁止,从而保证了指令的原子性。



x86架构处理器原子操作

在linux源码对应体系架构下可以找到原子操作的定义文件atomic.h,里面自加、自减等原子操作。



其中LOCK宏的定义在多处理器架构下,LOCK被定义为lock指令,单处理器架构,LOCK为空,因为此时并不需要 LOCK 指令前缀,处理器只要有可能,原子操作就会被编译成单个机器指令。。

ARM架构下原子操作

在对应源码目录下,找到atomic.h,同样也是内嵌汇编代码。

-


回到初始问题,unix系统为其提供了一个原子操作,即open文件时候使用O_APPEND标志,这样使得内核在每次进行写操作之前,都会将偏移量设为文件尾,避免了上述问题

unix系统针对文件I/O还有其他原子操作,例如函数pread和pwrite,调用pread相当于调用lseek后调用read,但是又实现了原子操作,其特点为:

调用pread时,不能中断其定位和读操作,因此其他进程无法将其打断。

不更新当前文件偏移量。

另外还有open函数中的O_CREAT和O_EXCL参数,同时使用这两个参数时,如果文件已经存在,则出错,因此可以用来检查文件是否存在。如果不存在,则创建它,这使得创建和测试成为一个原子操作。如果没有该原子操作的话,下面这段程序可以实现其功能,但是当两个进程同时操作时,还是会出现问题。

if((fd = open(pathname,O_WRONLY)) < 0){
if(errno == ENOENT){
if((fd = creat(path,mode)) < 0)
err_sys("creat error");
}else
err_sys("open error");
}


参考资料:

APUE

/article/5092800.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: