分类: LINUX
2010-05-20 13:07:14
综述 -------------------------- 进程间通信(Interprocess Communication,IPC)有以下四中形式: 消息传递(管道,FIFO和消息队列) 同步(互斥量,条件变量,读写锁,文件和记录锁,信号量) 共享内存(匿名的和有名的) 远程过程调用(solaris门和Sun Rpc) 而消息传递是最常用的一种IPC方式,其中管道和FIFO(也叫有名管道)比较简单也最为常用。 本文不打算写几个管道/FIFO的例子来说明怎么用它们进行进程间通信,具体用法读者可以参考网上其他资料, 本文只是罗列出一些使用管道/FIFO进行进程间通信时需要注意的方面和一些高级话题,以及管道/FIFO的限制. 管道和FIFO使用 -------------------------- 0. 管道(pipe)只能在相关进程(related process)之间传递消息,而FIFO(有名管道)可以在不相关进程间传递信息。 1. pipe(int fd[2])函数的参数 int fd[2]中 fd[0]为读描述符,fd[1]为写描述符.所以如果想从父进程给子进程传递信息,那么fork后父进程关闭fd[0],子进程关闭fd[1] 2. 宏S_ISFIFO(m)可以判断一个文件描述符是管道还是FIFO,参数m是struct stat结构的st_mode成员 struct stat { dev_t st_dev; /* ID of device containing file */ ino_t st_ino; /* inode number */ mode_t st_mode; /* protection */ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device ID (if special file) */ off_t st_size; /* total size, in bytes */ blksize_t st_blksize; /* blocksize for filesystem I/O */ blkcnt_t st_blocks; /* number of blocks allocated */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last status change */ }; 3. 管道和FIFO一般都是半双工的,也就是说一个 管道/FIFO 一旦创建好,只能从一端读,从另外一端写,从某一端不能既读又写. 4. 有些版本的unix系统支持全双工管道,如下图所示: 创建全双工管道的另一个途径就使用socketpair函数 5. 管道是建立在内核中的,所以往管道里写数据就相当于和内核进行一次通信;从管道里读数据就相当于和内核进行另外一次通信.所有通过管道传递一次信息,总共和内核通信两次. 6. 标准I/O库也提供一个popen函数来支持管道 #includeFILE *popen(const char *command, const char *type); int pclose(FILE *stream); 如果popen函数中的参数type是"r",则调用进程可以通过read获得command命令标准输出的内容 如果popen函数中的参数type是"w",则调用进程可以通过write给command命令传递参数 7. FIFO(有名管道)的创建 #include #include int mkfifo(const char *pathname, mode_t mode); 创建时mode不能指定为O_RDWR,因为FIFO也是半双工的,不能既读又写. 8. 对于FIFO也不能使用lseek,如果使用lseek,会返回ESPIPE 9. close()只是关闭了对FIFO的一次引用,unlink()才会真正把FIFO从文件系统中删除 高级话题 ----------------------------------------- 1. 如果read要读的字节数比管道中现存的字节数多,那么实际读到多少字节read就返回多少,所以请注意read的返回值 2. 在阻塞的情况下,如果一次写的字节数少于或等于PIPE_BUF,那么这次写可用视为是原子的.
也就是说如果两个进程同时往管道里写,而且每个进程写的字节数都小于或等于PIPE_BUF,不管是哪个进程先写,都可用保证两次写是原子的,不混合在一块儿;
但如果一次write的数据量大于PIPE_BUF,那就不能保证这次写是原子的了. 3. 非阻塞O_NONBLOCK对写的原子性没有影响,所以write时唯一影响其原子性的就是一次写入的数据量是否小于等于PIPE_BUF. 如果需要写入的字节数小于PIPE_BUF: 那么write的返回值取决于要视写入的字节数和管道/FIFO的剩余字节数而定: 如果管道/FIFO剩余字节数大于或等于write要写入的字节数,那么所有数据将被写入并传输; 如果管道/FIFO剩余字节数小于write要写入的字节数,那么就会返回EAGAIN错误。因为当设置成O_NONBLOCK时,进程是不会睡眠的,而且内核不能既保证只发送部分数据又保证write的原子性,所以内核只能返回错误通知进程稍后重试。 如果需要写入的字节数大于PIPE_BUF: 如果管道/FIFO还要哪怕一个字节的剩余空间,那么管道/FIFO还可以容纳多少字节内核就传送多少字节,同时write就返回多少字节 如果管道/FIFO没有空间了,那么write就会立即返回EAGAIN错误 4. 如果向一个没有打开读端的 管道/FIFO write,那么会产生SIGPIPE信号: 如果进程没有忽略此信号而且没有设置此信号的处理函数,那么默认情况下进程会被关闭 如果进程忽略了此信号或设置了此信号的处理函数,那么write就会返回EPIPE信号 5. 如果读一个空管道/空FIFO,而且此前管道/FIFO没有打开写端,那么read会立即返回0 6. 如果读一个空管道/空FIFO,而且此前管道/FIFO打开了写端,那么如果是阻塞的情况下,read会一直阻塞直到管道中有数据或管道关闭写端;如果是非阻塞的情况下,read会返回EAGAIN错误。 7. 在阻塞的情况下,当以读方式打开一个FIFO,如果此前此FIFO已经以写方式打开过,则打开成功;如果此前此FIFO没有以写方式打开过,那么open就会阻塞,直到以写方式打开FIFO. 8. 在非阻塞的情况下,当以读方式打开一个FIFO,不管此前此FIFO是否已经以写方式打开过,打开都成功; 9. 在阻塞的情况下,当以写方式打开一个FIFO,如果此前此FIFO已经以读方式打开过,则打开成功;如果此前此FIFO没有以读方式打开过,那么open就会阻塞,直到以读方式打开FIFO. 所以,如果在一个进程中 open(fifo1,O_RDONLY,0) open(fifo2,O_WRONLY,0) 而在另外一个进程中 open(fifo2,O_RDONLY,0) open(fifo1,O_WRONLY,0) 就会引起死锁 10. 在非阻塞的情况下,当以写方式打开一个FIFO,如果此前此FIFO已经以读方式打开过,则打开成功;如果此前此FIFO没有以读方式打开过,那么open就会返回ENXIO错误. 管道和FIFO的限制 ----------------------------------------- 1. OPEN_MAX 一个进程最多能打开的文件描述符数,用ulimit -n可以查看,如果这个值是16,那么一个进程最多可以同时创建16个管道 可用setrlimit函数改变这个值 2. PIPE_MAX 管道最多可写入的字节数,这个比较重要。上面的高级话题部分也讨论了PIPE_MAX和管道/FIFO读写的关系.
PIPE_MAX的值通常定义在limits.h头文件里,但是POSIX认为这个值是路径的一个属性(针对PIPE而言,因为管道没有路径),因为不同的路径可能对应于不同的文件系统,而不同的文件系统会有不同的特点.
这个值可用pathconf函数或者fpathconf函数获得 reference: <>