在介绍open函数时,我们详细介绍过O_EXCL标志的作用。
它和O_CREAT一起作用时,就会先测试需要创建的文件在不在,不在就会创建
该文件。如果存在,就出错返回。
但这里的测试和出错是一个原子操作。
什么意思呢,就是说测试和创建是一个不可分割的步骤,他们要么都不执行,
要么就都执行。不会发生测试完后,cpu切换到另一个进程的情况。
那如果没有这O_EXCL标志我们会怎么做呢。
我们可能会编写下面这样的程序:
if((fd=open(pathname,O_WRONLY))==-1){
if(ENOENT==errno){
if((fd=crear(pathname,mode))==-1){
perror("creat error");
exit(1);
}else{
perror("open error");
exit(1);
}
}
}
现在如果a进程在执行这段当执行完open时发现没有这个文件(因为没有指定O_CREAT这个标志)
那么open出错返回,errno被设置成ENOENT,在执行creat前,cpu切换到了另一个进程 B 执行,假如这个时候进程
B 创建了这个文件。并写入了部分数据。然后cpu又切换到A进程去执行。然后A接着调用creat函数。
这时候就会清空这个文件的内容(creat调用相当于open(pathname,O_WRONLY | O_CREAT | O_TRUNC,mode))
那么B进程写入的数据就会丢失。
问题就是出在了 对文件存在与否的测试和不存在就创建文件 是两个函数完成的,而任何一个需要两个分开的函数调用
的操作都不可能是原子操作,因为在这两个函数调用之间,内核有可能挂起改进程而去执行别的进程。就像
上面做的那样。
那么如果我们需要再一个文件的某个任意位置出开始(读)写数据。那么我们就需要两个操作一个是定位操作,然后是
(读)写数据操作。
XSI扩展中定义了两个可以原子 定位读写的 函数
ssize_t pread(int fileds, void *buf ,sizt_t nbutes,off_t offset);
ssize_t pwrite(int filedes,void *buf,size_t nbytes,off_t offset);
这里除了他们是原子操作外还有一点需要注意 pread()不更新文件指针
什么意思呢,我们写一段测试代码看看就知道了
我们创建了一个文件test 内容为“123456789”
a程序先先练两次调用read(),每次读取三个字节
b程序先调用pread()在离文件头五个字节出读3数据,然后在调用read()读取三个字节。
a程序为:
9 fd=open("test",O_RDONLY);
10 read(fd,buf,3);
11 buf[3]='\0';
12 printf("%s\n",buf);
13 read(fd,buf,3);
14 buf[3]='\0';
15 printf("%s\n",buf);
输出为:
123
456
b程序为
9 fd=open("test",O_RDONLY);
10 pread(fd,buf,3,5);
11 buf[3]='\0';
12 printf("%s\n",buf);
13 read(fd,buf,3);
14 buf[3]='\0';
15 printf("%s\n",buf);
输出为:
678
123
从输出我们可以很清楚的看出。pread()调用并不会改变文件指针。上面b程序中pread读取三个数据后
你可能会觉得文件当前偏移量变成8了 后面的read调用会从9开始读。但是pread实际上是不会改变文件指针
所以后面的read会从文件头开始读取。
而read调用会改变文件指针,所以a程序中第二次read调用会从第一次读完的地方接着读。
阅读(8055) | 评论(0) | 转发(0) |