免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: alwaysR9
打印 上一主题 下一主题

[C] linux write 函数 是否是 线程安全的? [复制链接]

论坛徽章:
1
程序设计版块每日发帖之星
日期:2015-09-23 06:20:00
31 [报告]
发表于 2015-09-20 08:03 |只看该作者
write() 线程安全是指,多个线程执行write的时候,write()函数访问的共享变量有同步保护,write的数据本身不在此范畴之内,文件current offset是属于被保护的共享变量。

论坛徽章:
1
2015亚冠之阿尔艾因
日期:2015-08-24 15:46:57
32 [报告]
发表于 2015-09-21 09:18 |只看该作者
回复 30# alwaysR9
ubuntu 15.04, i5.

   

论坛徽章:
2
程序设计版块每日发帖之星
日期:2015-12-23 06:20:00每日论坛发贴之星
日期:2015-12-23 06:20:00
33 [报告]
发表于 2015-09-21 09:26 |只看该作者
irp 发表于 2015-09-20 08:03
write() 线程安全是指,多个线程执行write的时候,write()函数访问的共享变量有同步保护,write的数据本身不 ...


我感觉文件的offset没有被保护起来, 以下是sys_write的源码:
  1. SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf, size_t, count)
  2. 579 {
  3. 580         struct fd f = fdget_pos(fd);
  4. 581         ssize_t ret = -EBADF;
  5. 582
  6. 583         if (f.file) {
  7. 584                 loff_t pos = file_pos_read(f.file);            // 获得文件offset的位置
  8. 585                 ret = vfs_write(f.file, buf, count, &pos); // 写文件
  9. 586                 if (ret >= 0)                                              // 接下来3行,  更新文件offset
  10. 587                         file_pos_write(f.file, pos);
  11. 588                 fdput_pos(f);
  12. 589         }
  13. 590
  14. 591         return ret;
  15. 592 }
复制代码
很明显 获得offset, 写文件, 更新offset 三个操作是非原子的

假设线程A, B写同一个文件, 线程A先获得了offset值, 此时线程A被挂起; 线程B开始执行,B获得offset值, 从offset处写入一段字符, 挂起; 线程A从与B相同的offset处写入字符(线程A将B写入的字符覆盖)....
上面的情况可能会出现, 我做的多线程写文件实验确实出现了这种情况.  所以我认为write函数是非线程安全的, 不知道我的理解对不对 ?

论坛徽章:
1
程序设计版块每日发帖之星
日期:2015-09-23 06:20:00
34 [报告]
发表于 2015-09-21 10:12 |只看该作者
write(), thread safety, and POSIX
[Posted April 18, 2006 by corbet]
Dan Bonachea recently reported a problem. It seems that he has a program where multiple threads are simultaneously writing to the same file descriptor. Occasionally, some of that output disappears - overwritten by other threads. Random loss of output data is not generally considered to be a desirable sort of behavior, and, says Dan, POSIX requires that write() calls be thread-safe. So he would like to see this behavior fixed.
Andrew Morton quickly pointed out the source of this behavior. Consider how write() is currently implemented:

    asmlinkage ssize_t sys_write(unsigned int fd, const char __user *buf,
                                 size_t count)
    {
        struct file *file;
        ssize_t ret = -EBADF;
        int fput_needed;

        file = fget_light(fd, &fput_needed);
        if (file) {
            loff_t pos = file_pos_read(file);
            ret = vfs_write(file, buf, count, &pos);
            file_pos_write(file, pos);
            fput_light(file, fput_needed);
        }

        return ret;
    }
There is no locking around this function, so it is possible for two (or more) threads performing simultaneous writes to obtain the same value for pos. They will each then write their data to the same file position, and the thread which writes last wins.

Putting some sort of lock (using the inode lock, perhaps) around the entire function would solve the problem and make write() calls thread-safe. The cost of this solution would be high, however: an extra layer of locking when almost no application actually needs it. Serializing write() operations in this way would also rule out simultaneous writes to the same file - a capability which can be useful to some applications.

So some developers have questioned whether this behavior should be fixed at all. It is not something which causes problems for over 99.9% of applications, and, for those which need to be able to perform this sort of simultaneous write, there are other options available. These include user-space locking or using the O_APPEND option. So, it is asked, why add unnecessary overhead to the kernel?

Linus responds that it is a "quality of implementation" issue, and that if there is a low-cost way of getting the system to behave the way users would like, it might as well be done. His proposal is to apply a lock to the file position in particular. His patch adds a f_pos_lock mutex to the file structure and uses that lock to serialize uses of and changes to the file position. This change will have the effect of serializing calls to write(), while leaving other forms (asynchronous I/O, pwrite()) unserialized.

The patch has not drawn a lot of comments, and it has not been merged as of this writing. Its ultimate fate will probably depend on whether avoiding races in this obscure case is truly seen to be worth the additional cost imposed on all users.



不知道这个问题后面怎么处理的。

论坛徽章:
0
35 [报告]
发表于 2015-09-21 11:50 |只看该作者
回复 34# irp

manpages 上写了在 3.14 版修复。 github.com/torvalds/linux/commit/9c225f2655e36a470c4f58dbbc99244c5fc7f2d4
   

论坛徽章:
0
36 [报告]
发表于 2015-09-21 11:53 |只看该作者
回复 33# alwaysR9


    很明显你没有注意到 fdget_pos 有加锁动作,而 fdput_pos 有解锁动作。因此 write 是原子的。

论坛徽章:
2
程序设计版块每日发帖之星
日期:2015-12-23 06:20:00每日论坛发贴之星
日期:2015-12-23 06:20:00
37 [报告]
发表于 2015-09-21 17:05 |只看该作者
giantchen 发表于 2015-09-21 11:53
回复 33# alwaysR9


是的, 这个函数里确实有上锁操作, 源码:
  1. unsigned long __fdget_pos(unsigned int fd)
  2. 741 {
  3. 742         unsigned long v = __fdget(fd);
  4. 743         struct file *file = (struct file *)(v & ~3);
  5. 744
  6. 745         if (file && (file->f_mode & FMODE_ATOMIC_POS)) {
  7. 746                 if (file_count(file) > 1) {
  8. 747                         v |= FDPUT_POS_UNLOCK;
  9. 748                         mutex_lock(&file->f_pos_lock);
  10. 749                 }
  11. 750         }
  12. 751         return v;
  13. 752 }
复制代码
上锁的条件是: (file->f_mode & FMODE_ATOMIC_POS), 不清楚普通文件和socket是否满足这个条件, 我也没查到如何获取f_mode的函数

假设write对普通文件上锁了, 那多线程写文件时的覆盖现象是什么原因呢?

论坛徽章:
1
程序设计版块每日发帖之星
日期:2015-09-23 06:20:00
38 [报告]
发表于 2015-09-21 19:57 |只看该作者
本帖最后由 irp 于 2015-09-21 21:15 编辑

自己搭一个kernel调试的环境吧。

论坛徽章:
0
39 [报告]
发表于 2015-09-21 23:57 |只看该作者
回复 37# alwaysR9


    你的 Linux 版本太低。manpages 上写了在 3.14 版修复。

论坛徽章:
0
40 [报告]
发表于 2016-01-04 00:21 |只看该作者
write本身是线程安全的,多线程下应该注意文件当前位置错乱的问题。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP