转自: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\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.