Chinaunix首页 | 论坛 | 博客
  • 博客访问: 259447
  • 博文数量: 74
  • 博客积分: 1470
  • 博客等级: 上尉
  • 技术积分: 793
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-25 21:01
文章分类

全部博文(74)

文章存档

2011年(1)

2010年(32)

2009年(32)

2008年(9)

我的朋友

分类: 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函数来支持管道
#include 
FILE *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:
<>

阅读(2323) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~