pwrite写操作——原子操作,不改变当前文件操作符位置,写入位置offset是一个相对于fd起始地址的偏移量

意外发现,pwrite() 函数是一个原子操作,现测试验证如下:

 

  1 /*
  2 pwrite相当于先调用lseek接着调用write。但又不完全是这样:
  3 (1)pwrite是原子操作,定位和写操作在一个原子操作中完成,期间不可中断。但分开的lseek和write中间可能被其他程序中断。
  4 (2)pwrite不更改当前文件的指针,也就是说不改变当前文件偏移量。
  5 (3)pwrite中的offset是一个绝对量,相对于文件开始处的绝对量,与当前文件指针位置无关。
  6 
  7 如下实例:
  8 
  9 */
 10 #include <fcntl.h>
 11 #include <unistd.h>
 12 #include <stdio.h>
 13 #include <errno.h>
 14 
 15 int main(void)
 16 {
 17     char pathName[] = {"/home/zll/201907-12/test"};
 18     int fd = -1;
 19     unsigned int offsetLen = 0;
 20     unsigned int writeLen;
 21     char bufData[] = {"0123456789asdfgh"};
 22     int ret;
 23 
 24     fd = open(pathName, O_RDWR | O_CREAT);
 25     if (fd < 0) {
 26         printf("open error: %s.\n", strerror(errno));
 27         return -1;
 28     }
 29 
 30     offsetLen = lseek(fd, 0, SEEK_SET);
 31     if (offsetLen < 0) {
 32         //printf("lseek error: %d.\n", perror(errno));
 33         perror("lseek error.\n");
 34         return -1;
 35     }
 36 
 37     writeLen = 10;
 38     ret = write(fd, bufData, writeLen);
 39     if (ret != writeLen) {
 40         printf("write error: %s.\n", strerror(errno));
 41         return -1;
 42     }
 43 // pwrite() 操作写文件时,只会按照偏移来写,不会影响当前的文件描述符位置;
 44 // (从文件描述符开头偏移两个字节处,写入从(&bufData[10])开始的4个字符)
 45 // 两个write() 之间增加pwrite(),丝毫未影响write操作的文件描述符位置
 46     writeLen = 4;
 47     ret = pwrite(fd, bufData + 10, writeLen, 2);
 48     if (ret != writeLen) {
 49         printf("pwrite error: %s.\n", strerror(errno));
 50         return -1;
 51     }
 52 
 53     writeLen = 6;
 54     ret = write(fd, bufData + 10, writeLen);
 55     if (ret != writeLen) {
 56         printf("write error: %s.\n", strerror(errno));
 57         return -1;
 58     }
 59 
 60     close(fd);
 61     return 0;
 62 }
 63 

a. 第一次write() 写入10个字符:0——9

b. 第二次pwrite() 从文件头的第2个字符开始写入从&bufData[10] 开始的4个字母,即:asdf

c. 第三次write() 写入bufData的最后6个字符,即:asdfgh

如上代码执行结果:

        

从上结果可知:同一个进程中,write() 写操作时,其系统内部会自动统计文件描述符的当前位置,pwrite() 操作是不会影响write() 操作的文件描述符指针的;

pwrite 操作,其写入位置是一个相对于文件头的偏移量,是一个相对的偏移地址。

 

 

————————————————————————————————————————————————————————

如果带有O_APPEND标志的情况,在文件真正写入之前会调用generic_write_checks进行一些检查,在检查的时候如果发现带有O_APPEND标志就将offset设置为文件的大小。而这整个过程都是在加锁的情况下完成的,所以带有O_APPEND标志的情况下,文件的写入是原子的,多线程写文件是不会导致数据错乱的。

另外一种情况就是pwrite系统调用,pwrite系统调用通过让用户指定写入的offset,值得整个写入的过程天然的变成原子的了,在上文说到,整个写入的过程是因为获取offset和文件写入是两个独立的步骤,并没有加锁,通过pwrite省去了获取offset这一步,最终整个文件写入只有一步加锁的文件写入过程了。

 

最后一个问题是多个进程写同一个文件是否会造成文件写错乱,直观来说是多进程写文件不是原子的,这是很显而易见的,因为每个进程都拥有一个struct file对象,是独立的,并且都拥有独立的文件offset,所以很显然这会导致上文中说到的数据覆盖的情况,但是否会导致数据错乱呢?,答案是不会,虽然struct file对象是独立的,但是struct inode是共享的(相同的文件无论打开多少次都只有一个struct inode对象),文件的最后写入其实是先要写入到页缓存中,而页缓存和struct inode是一一对应的关系,在实际文件写入之前会加锁,而这个锁就是属于struct inode对象(见上文中的mutex_lock(&inode->i_mutex))的,所有无论有多少个进程或者线程,只要是对同一个文件写数据,拿到的都是同一把锁,是线程安全的,所以也不会出现数据写错乱的情况。
--------------------- 
作者:zhangyifei216 
来源:CSDN 
原文:https://blog.csdn.net/zhangyifei216/article/details/76653746 
版权声明:本文为博主原创文章,转载请附上博文链接!

 

 

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值