转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搞定它.
祝玩得开心!
分享到:
相关推荐
- **文件权限管理**:掌握chmod命令的应用场景及注意事项。 - **标准输入输出重定向**:利用>、<、>>等符号进行文件读写操作。 #### 5. 网络编程 - **TCP/IP协议栈**:理解网络层、传输层等各层的功能与作用。 - **...
在ECM拨号流程中,还强调了网络连接的生命周期管理,包括断开拨号的注意事项,这对于理解整个网络连接的过程以及可能出现的问题和解决方法有着重要的指导意义。 总而言之,这份FIBOCOM ECM拨号指南_Linux文档为...
错误处理与注意事项 - 当使用管道时,需要确保每个端口只被一个进程使用,即写端只用于写入,读端只用于读取。 - 在读取或写入管道之前,必须先关闭未使用的端口。 - 当一个进程向已关闭读端的管道写入数据时,会...
7. **注意事项**:提醒开发者在使用过程中应注意的事项,比如线程安全问题、内存管理等。 通过这个帮助文档,开发者可以快速找到所需的函数,并理解其工作原理,从而更高效地编写代码。相比于传统的man pages,CHM...
通过"Linux (C语言)函数库详解词典",你可以详细地了解每个函数的参数、返回值、使用示例和注意事项,从而提升在Linux环境下使用C语言进行系统级编程的能力。这份资料对于初学者和有经验的开发者都是宝贵的参考...
#### 五、注意事项 - 在使用管道时要注意资源管理和同步问题,避免数据竞争和死锁的情况发生。 - 由于管道只支持一对一的通信,不适合多对一或多对多的场景。 #### 六、总结 管道作为一种轻量级的进程间通信机制...
通过这份手册,开发者可以学习如何在Linux环境中正确使用C语言,了解各种函数的参数、返回值、使用场景和注意事项,从而编写出高效、稳定且符合规范的代码。同时,它还能帮助开发者解决在开发过程中遇到的问题,提高...
- 管道使用的注意事项。 **4.4 有名管道** - **概述**:介绍了有名管道(Named Pipe),即FIFO文件。 - **学习要点**: - FIFO文件的创建过程。 - FIFO文件的I/O使用方法。 - 使用FIFO文件时应注意的细节。 **...
这个文件很可能包含了详细的Linux命令参考,包括命令的基本用法、参数选项、实例演示以及相关的注意事项。用户可以通过搜索特定命令或关键词来快速查找所需信息。掌握这些命令有助于提高在Linux终端中的工作效率,...
- **内存管理**:`malloc()`, `calloc()`, `realloc()`, `free()`等函数以及内存映射(`mmap()`)在Linux下的具体实现和注意事项。 - **文件系统操作**:通过系统调用如`open()`, `close()`, `read()`, `write()`...
在Linux操作系统中,C语言是系统编程的基础,它提供了与硬件直接交互...通过阅读压缩包中的HTML文档,你可以获得更详细的信息,包括函数的具体用法、参数说明和注意事项,这对于提升Linux环境下的C编程技能非常有帮助。
手册详细阐述了这些过程及其注意事项。 5. **线程操作**:线程是现代多任务编程的基础。手册涵盖了线程创建(pthread_create)、线程同步(pthread_mutex_lock、pthread_cond_wait)和线程退出(pthread_exit)等...
Linux C 函数全集是开发者们不可或缺的参考资料,它涵盖了C语言在Linux环境下的众多核心函数,帮助程序员理解和使用这些功能强大的工具。 Linux C 函数全集中,我们可以找到以下主要的知识点: 1. **标准输入/输出...
同时,理解函数的工作原理和注意事项,有助于避免常见的编程错误,提升代码质量和效率。 在实际开发中,熟练掌握Linux C函数不仅可以让你编写出高效、稳定的系统级程序,也能更好地利用Linux提供的丰富资源和工具,...
- **守护进程的建立**:创建方法及注意事项。 #### 五、进程间通信(IPC) - **信号**: - **信号的处理**:如何捕捉、忽略或处理信号。 - **信号与系统调用的关系**:信号如何影响系统调用的行为。 - **信号...
这些只是部分涵盖在"Linux C 函数全集文档"中的内容,实际文档会更详细地介绍每个函数的参数、返回值、使用示例和注意事项,对于开发者来说,是不可或缺的学习和参考资源。通过学习和掌握这些函数,开发者可以编写...
学习Linux系统函数调用,不仅需要理解函数的用途,还要掌握其使用场景和注意事项,比如正确处理错误、管理内存、同步多线程等。结合Makefile的使用,可以提高程序编译和维护的效率,使项目管理更加有序。通过深入...
本文详细介绍了Linux进程间通信的基础概念和发展历程,重点分析了管道通信的具体实现方式和注意事项。通过理解这些基础知识,开发者可以更好地利用Linux提供的IPC机制来设计高效可靠的多进程应用程序。
书中会介绍这些函数的使用方法和注意事项。 8. **编译和调试**:在Linux下,使用gcc编译器进行C程序的编译和链接是常规步骤。书中会讲解如何使用gcc命令行选项,以及使用gdb进行程序调试。 9. **程序设计和调试...