转http://blog.yufeng.info/archives/1485
Linux下pipe使用注意事项
作者:Yu Feng
原创文章,转载请注明: 转载自Erlang非业余研究
本文链接地址: 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搞定它.
祝玩得开心!
分享到:
相关推荐
linux平台下实现进程间通信的一种:pipe。以字符串为例。
当没有硬件或中断处理程序时(如示例scull字符设备模拟程序)我们可以使用一个缓冲区和另一个进程写进程来产生数据并唤醒读取进程;类似的,阻塞在缓冲区write调用上的写进程也可以有另一读进程唤醒。这就是实现类...
ibm提供的一个小工具,用来备份数据流(一个向tsm读写数据流的工具)
Linux下的无名管道pipe的设计.docx
BLOG_【故障处理】Oracle_lhr_TNS-12518、TNS-00517和 Linux Error:32:Broken pipe.pdfBLOG_【故障处理】Oracle_lhr_TNS-12518、TNS-00517和 Linux Error:32:Broken pipe.pdf
操作系统实验四:Linux下进程管道通信 任务1:使用Pipe创建管道,创建一个子进程,子进程向父进程发送消息“I am your son!”,父进程接收到子进程的消息后,显示在屏幕上,并向子进程发送“I am your father!”。子...
多个进程利用pipe连接构成pipe ring, 利用这个环进行pipe sorting
使用Big pipe提升浏览速度 使用Big pipe提升浏览速度
Linux上,创建管道使用pipe函数,当它执行后,会产生两个文件描述符,分别为读端和写端。单个进程中的管道几乎没有任何作用,通常会先调用pipe,然后调用fork,从而创建从父进程到子进程的IPC通道。 Linu
The PHY Interface for the PCI Express, SATA, and USB SuperSpeed Architectures (PIPE) is intended to enable the development of functionally equivalent PCI Express, SATA and USB SuperSpeed PHY's....
Petri网建模软件——PIPE4.3.0
USB3.0 PIPE总线协议 针对usb物理接口芯片,所使用的pipe 总线的详细解释
管道是一种把两个进程之间的标准输入和标准输出连接起来的机制,...参数数组包含pipe使用的两个文件的描述符。fd[0]:读管道,fd[1]:写管道。 必须在fork()中调用pipe(),否则子进程不会继承文件描述符。两个进程不共
pipe flow expert 教程 2013版本
在linux下介绍pipe常见的问题,并以代码的形式展现
在Linux系统中使用系统调用fork()创建两个子进程,使用系统调用pipe()建立一个管道,两个子进程分别向管道各写一句话: Child process 1 is sending a message! Child process 2 is sending a message! 而父进程则...
进程间通信的一种方式,Pipes:管道,分为无名管道:在父子进程间交换数据;有名管道:可在不同主机间交换数据,分为服务器方和客户方,在Win9X下只支持有名管道客户。 1、进程间通信 2、管道Pipe通信
pipe2.4
This simple example illustrates using the Pipe Control.This example also demonstrates the following...1) Modifying basic control properties at runtime