zoukankan      html  css  js  c++  java
  • Linux下pipe使用注意事项

    转自:http://blog.yufeng.info/archives/1485

    Linux下的pipe使用非常广泛, shell本身就大量用pipe来粘合生产者和消费者的. 我们的服务器程序通常会用pipe来做线程间的ipc通讯. 由于unix下的任何东西都是文件,只要是文件,在读取的时候,,就会设置last access time, 所以pipe也不例外., 但是这个时间对我们没有意义 如果pipe使用的非常频繁的时候会碰到由于设置访问时间导致的性能问题. 这个开销远比pipe读写的本身开销大. 相比文件读写的开销, atime微不足道,但是对pipe来讲就不同了.
    这个事情是上次和多隆同学在把玩他的网络框架的时候,无意发现的.

    我们来分析下pipe的这部分代码:

    //pipe.c:L349
    static ssize_t
    pipe_read(struct kiocb *iocb, const struct iovec *_iov,
                   unsigned long nr_segs, loff_t pos)
    {
    ...
       if (ret > 0)
            file_accessed(filp);
        return ret;
    }

    我们可以看到在pipe读的时候要设置 file_accessed时间的,接着:

    //fs.h:L1761
    extern void touch_atime(struct vfsmount *mnt, struct dentry *dentry);
    static inline void file_accessed(struct file *file)
    {
            if (!(file->f_flags & O_NOATIME))
                    touch_atime(file->f_path.mnt, file->f_path.dentry);
    }

    如果文件没设置 O_NOATIME就真正动手设置atime,接着:

    //inode.c:L1493
    void touch_atime(struct vfsmount *mnt, struct dentry *dentry)
    {
            struct inode *inode = dentry->d_inode;
            struct timespec now;
     
            if (inode->i_flags & S_NOATIME)
                    return;
            if (IS_NOATIME(inode))
                    return;
            if ((inode->i_sb->s_flags & MS_NODIRATIME) && S_ISDIR(inode->i_mode))
                    return;
     
            if (mnt->mnt_flags & MNT_NOATIME)
                    return;
            if ((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode))
                    return;
     
            now = current_fs_time(inode->i_sb);
     
            if (!relatime_need_update(mnt, inode, now))
                    return;
     
            if (timespec_equal(&inode->i_atime, &now))
                    return;
     
            if (mnt_want_write(mnt))
                    return;
     
            inode->i_atime = now;
            mark_inode_dirty_sync(inode);
            mnt_drop_write(mnt);
    }

    我们可以看出上面的流程还是比较复杂的,开销也很大.
    我们来演示下:

    cat > pipe_test.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <assert.h>
    #include <pthread.h>
    #include <fcntl.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <linux/unistd.h>
     
    static int fds[2];
    static pthread_t rp;
     
    static void *rp_entry(void *arg) {
      char c[1];
      while (1 == read(fds[0], c, 1)) {
        if (*c == 'Q'break;
      }
      fprintf(stderr, "pipe read ok ");
      return NULL;
    }
     
    int main(int argc, char *argv[]) {
      long i, n;
      int rc;
      if (argc < 2) {
        fprintf(stderr, "usage: pipe_test NNNNNN ");
        return -1;
      }
      n = atol(argv[1]);
      pipe(fds);
      //fcntl(fds[0], F_SETFL, O_NOATIME);
      pthread_create(&rp, NULL, rp_entry, NULL);
      fprintf(stderr, "pipe write %ld...", n);
      for (i = 0; i < n; i++) {
        write(fds[1], "A", 1);
      }
      write(fds[1], "Q", 1);
      fprintf(stderr, "ok ");
      pthread_join(rp, NULL);
      close(fds[0]);
      close(fds[1]);
      return 0;
    }
    CTRL+D
    $ gcc -D_GNU_SOURCE pipe_test.c -lpthread
    sudo opcontrol --setup --vmlinux=/usr/lib/debug/lib/modules/2.6.18-164.el5/vmlinux
    sudo opcontrol --init && sudo opcontrol --reset && sudo opcontrol --start
    $ ./a.out 10000000
    pipe write 10000000...ok
    pipe read ok
    sudo opcontrol --shutdown
    $ opreport -l|less            
    samples  %        app name                 symbol name
    378654   92.7742  vmlinux                  .text.acpi_processor_idle
    12978     3.1797  vmlinux                  current_fs_time
    2530      0.6199  vmlinux                  thread_return
    2345      0.5745  vmlinux                  touch_atime
    2253      0.5520  vmlinux                  .text.acpi_safe_halt
    1597      0.3913  vmlinux                  timespec_trunc
    1368      0.3352  vmlinux                  file_update_time
    1253      0.3070  vmlinux                  __mark_inode_dirty
    901       0.2208  vmlinux                  pipe_writev
    768       0.1882  vmlinux                  __mutex_lock_slowpath
    763       0.1869  vmlinux                  try_to_wake_up
    270       0.0662  vmlinux                  copy_user_generic_unrolled
    254       0.0622  vmlinux                  acpi_set_register
    254       0.0622  vmlinux                  system_call
    233       0.0571  vmlinux                  pipe_readv
    188       0.0461  vmlinux                  dnotify_parent
    167       0.0409  vmlinux                  mutex_unlock
    ...

    我们可以看到touch_atime的开销很大,远比pipe的读写大.
    这次把这行注释去掉: fcntl(fds[0], F_SETFL, O_NOATIME); 指示pipe在读的时候不更新atime,看下效果:

    $ opreport -l|less
    samples  %        app name                 symbol name
    599018   95.2466  vmlinux                  .text.acpi_processor_idle
    4140      0.6583  vmlinux                  .text.acpi_safe_halt
    3281      0.5217  vmlinux                  thread_return
    2812      0.4471  vmlinux                  current_fs_time
    2615      0.4158  vmlinux                  file_update_time
    1790      0.2846  vmlinux                  __mutex_lock_slowpath
    1657      0.2635  vmlinux                  timespec_trunc
    1341      0.2132  vmlinux                  try_to_wake_up
    1281      0.2037  vmlinux                  mutex_unlock
    1080      0.1717  vmlinux                  mutex_lock
    1001      0.1592  vmlinux                  pipe_readv
    925       0.1471  vmlinux                  pipe_writev

    这下看不到touch_atime了,开销省了,对于高性能服务器是很重要的.
    小结: 细节很重要,记得开文件open的时候设置O_NOATIME或者用fcntl搞定它.
    祝玩得开心!

    Post Footer automatically generated by wp-posturl plugin for wordpress.

  • 相关阅读:
    shift and/or 算法
    FFT求解字符串匹配
    hdu 6981/ 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)1009 Rise in Price(剪枝,dp合并)
    Rancher监控指标一文干到底(workload metrics)
    RocketMQ详解(二)启动运行原理
    新人如何快速剖析源码
    RocketMQ详解(三)核心设计原理(待完善)
    windows powershell下载文件的4种方式
    windows 调用bat脚本时pause处理
    python3 中的b''解析
  • 原文地址:https://www.cnblogs.com/dirt2/p/6186717.html
Copyright © 2011-2022 走看看