设计多个进程共同操作一个文件的,就需要原子的操作下面几个问题:
1.添加至一个文件
多个进程都要添加数据到一个文件的尾端
if(lseek(fd,0,SEEK_END)>=0)
{
write(fd,buff,100);
}
对单个进程而言,这个程序能正常工作,若多个进程同时调用,则会出现问题。
每个进程都有它自己的文件表项,但共享同一个i节点。假定A进程调用lseek,它将A进程的该文件当前偏移量设置到1500字节,然后内核切换,使进程B执行,B调用lseek把当前偏移量便宜到1500字节处,写入了100个字节,文件偏移量到1600字节处,文件长度增加,所有i节点中的当前文件长度更新为1600,然后,内核又切换到A进程运行,A调用write就从当前文件偏移量处(1500)写入文件,这样就覆盖了进程B刚写入的数据。
问题出在“定位到文件尾端,然后写”上,它使用了两个分开的函数调用,解决办法是使这两个分开的操作原子执行,不允许在执行完第一个函数的时候执行内核调度。
unix提供了一种方法,就是打开文件时设置O_APPEND标志。
2.pread和pwrite函数
SUS包括了XSI扩展,允许原子性的定位搜索(seek)和执行IO
#include<unistd.h>
ssize_t pread(int fileds, void* buf, size_t nbytes, off_t offset);
ssize_t pwrite(int fileds, void* buf, size_t nbytes, off_t offset);
pread相当于原子调用lseek和read
pwrite相当于原子调用lseek和write
3.创建一个文件
if ((fd = open(pathname, O_WRONLY)) < 0)
{
if (errno == ENOENT)
{
fd = creat(pathname, mode);
}
}
如果在open和creat之间,另一个进程创建了该文件,就会引起问题。例如,另一个进程创建该文件,并写入了一些数据,然后原先的进程执行并在此创建,由先前进程写入的数据就被擦去了。
open函数提供了原子操作,O_EXEC 和 O_CREAT ,如果文件存在就会返回出错,文件不存在则创建,使测试文件存在和创建成原子操作。
4. dup2函数
#include <unistd.h>
int dup(int filedes);
int dup2(int filedes, int filedes2);
dup2的操作先关闭filedes2文件,然后连接filedes文件对象到filedes2上。
dup2的操作等效于:
close(filedes2);
fcntl(filedes, F_DUPFD, filedes2);
dup2使close和fcntl成为一个原子操作