Linux下pipe使用注意事项
November 9th, 2011
原创文章,转载请注明: 转载自系统技术非业余研究
本文链接地址: Linux下pipe使用注意事项
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\n"); return NULL; } int main(int argc, char *argv[]) { long i, n; int rc; if (argc < 2) { fprintf(stderr, "usage: pipe_test NNNNNN\n"); 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\n"); 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.
又学到了东西,感谢下霸哥,每一篇文章都是精心编写的
Yu Feng Reply:
November 9th, 2011 at 10:58 pm
谢谢支持:)
有容量对比数据么?
Yu Feng Reply:
November 9th, 2011 at 11:50 pm
无需呀,在这个场景下,pipe是用来传通知的。。。
猛赞
哇哦,学习了,分析得清晰透彻啊
换成fdevent 吧
Yu Feng Reply:
November 29th, 2011 at 4:06 pm
我们内核大多数是2.6.18的
记得挂载文件系统的时候,也有禁止ATime更新的选项的?
Yu Feng Reply:
January 2nd, 2012 at 8:00 pm
文件系统有的,但是pipe不是普通文件
599018 95.2466 vmlinux .text.acpi_processor_idle
这个 占了 95%, 这个正常吗?
touch_atime 只占了 0.5745
但是 vmlinux .text.acpi_processor_idle 占了 92.7742
我想问下,这时候优化 touch_atime 有意义吗?
我这么问,是因为最近项目里的一台机器也发现 acpi_processor_idle 很高很高
这让我很困惑,不知道这个是干什么的,不知道大牛们有么有线索
Yu Feng Reply:
March 13th, 2014 at 4:22 pm
高性能服务是一点点扣出来的,单这个点分量不是很大,多个改进累计起来就有竞争力了。