当使用管道进行进程的通信时,进程往管道中写入的内容每次都会被添加在管道缓冲区的结束位置。而另一个进程在读取数据的时候,每次都从缓冲区的头部读取数据。
由于管道只能在父子进程或兄弟进程间创建,管道只能用于父子进程或兄弟进程间的通信。非父子进程或兄弟进程间的通信可以使用命名管道或消息队列。
int pipe(int filedes[2]);
pipe函数将创建一对指向管道inode节点的文件描述符,并将其保存在filedes数组中。filedes[0]用于读取管道中的数据,而filedes[1]用于往管道中写入数据。
在父子进程共享文件描述符的程序中,不关闭不需要的文件描述符,有可能导致在读取数据时无法获得结束位置。
在某些版本的UNIX中,管道是全双工的。在Linux中,管道是工作在半双工模式下的。要实现Linux下的双向通信,必须创建两个管道:一个作为读取数据的管道,一个作为写入数据的管道。
FILE *popen(const char *command, const char *command, const char *type);
popen函数通过调用pipe函数创建一个单向通信的管道,并调用fork函数创建新的子进程。最后会启动shell,在shell中执行参数command中的命令。参数type用于指定是读取管道中的数据,还是向管道中写入数据。不能在读取管道数据的同时,又向管道写入数据。popen返回的指向FILE的指针必须使用pclose函数关闭,不能使用fclose函数关闭。
int pclose(FILE *stream);
pclose函数将等待使用管道的结束,然后关闭文件流。
为了实现非亲缘关系的进程间的通信,可以使用命名管道。
命名管道,又被称为FIFO(first-in, first-out),作为特殊的设备文件存在于文件系统中。进程可以像访问设备文件一样,使用open或close函数来打开或关闭命名管道。命名管道严格遵循先进先出的原则,写入FIFO的数据将被添加在结尾位置;而读取FIFO中的数据时,会从开始处返回数据。虽然FIFO以文件形式存在,但写入FIFO的数据是由Linux内核传递和存储的。写入FIFO的数据并没有直接写到文件系统中。
int mkfifo(const char *pathname, mode_t mode);
mkfifo函数用于创建FIFO。参数pathname用于指定创建出的FIFO文件名称。参数mode用于指定创建出的文件权限,最终的权值是由(mode & ~umask)决定的。
当创建完FIFO文件后,可以像访问正常文件一样访问创建出的FIFO文件。如果打开FIFO文件进行数据读取时,没有其它进程向FIFO中写入数据,进程将处于阻塞状态。同样,在写入数据的同时,没有进程从FIFO中读取数据,进程也会处于阻塞状态。