管道的问题在于它们只能在具有共同祖先(指父子进程关系)的进程间使用,不过该问题已随有名管道(FIFO)的引入而解决。
管道和FIFO都是随进程持续的:一直存在到打开着该对象(管道或FIFO)的最后一个进程关闭该对象为止。
管道没有名字,因此不能用于无亲缘关系的进程间通信。
FIFO有一个在文件系统中的Unix路径名作为其标识符,因此可用于无亲缘关系的进程间通信。
管道和FIFO都可以用通常的read和write函数访问。
1.管道
所有的Unix都提供管道。它由pipe函数创建,提供一个单向数据流。
#include <unistd.h>
int pipe(int pipefd[2]);
/**
*函数描述: pipe()创建一个单向的数据通道用于进程间通信
*pipefd[0]返回管道的读取端描述字
*pipefd[1]返回管道的写入端描述字
*
*返回值: 成功返回0;失败返回-1,错误原因存放errno中
*
*错误:
* EMFILE 进程已用完文件描述词最大量
* ENFILE 系统已无文件描述词可用
* EFAULT 参数 filedes 数组地址不合法
*/
范例
1#include <sys/wait.h>
2#include <assert.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <unistd.h>
6#include <string.h>
7
8int main(int argc, char *argv[])
9{
10 int pipefd[2];
11 pid_t cpid;
12 char buf;
13
14 assert(argc == 2);
15
16 if(pipe(pipefd) == -1)
17 {
18 perror("pipe");
19 exit(EXIT_FAILURE);
20 }
21
22 cpid = fork();
23 if(cpid == -1)
24 {
25 perror("fork");
26 exit(EXIT_FAILURE);
27 }
28
29 if(cpid == 0)
30 {
31 //子进程关闭写入端描述字
32 close(pipefd[1]);
33
34 while(read(pipefd[0], &buf, 1) > 0)
35 {
36 write(STDOUT_FILENO, &buf, 1);
37 }
38
39 write(STDOUT_FILENO, "\n", 1);
40 close(pipefd[0]);
41 _exit(EXIT_SUCCESS);
42 }
43 else
44 {
45 //父进程关闭读取端描述字,将命令行参数2传送给子进程
46 clese(pipefd[0]);
47 write(pipefd[1], argv[1], strlen(argv[1]));
48 close(pipefd[1]); //读取端将读取EOF
49 wait(NULL); //等待子进程结束
50 exit(EXIT_SUCCESS);
51 }
52}
53
编译运行:
xxx@linux:~/ipcs> gcc -Wall pipe1.c -o pipe1
xxx@linux:~/ipcs> ./pipe1 hellopipe
hellopipe
popen()/pclose()函数
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(*stream);
描述
popen() 函数用创建管道的方式启动一个进程, 并调用shell. 因为管道是被定义成单向的, 所以type 参数只能定义成只读或者 只写, 不能是 两者同时, 结果流也相应的是只读或者只写.
command 参数是一个字符串指针, 指向的是一个以null 结束符结尾的字符串, 这个字符串包含一个shell 命令. 这个命令被送到/bin/sh 以 -c 参数 执行, 即由shell 来执行. type 参数也是一个指向以null 结束符结尾的字符串的指针, 这个字符串必须是'r' 或者'w’ 来指明是读还是写.
popen() 函数的返回值是一个普通的标准I/O流, 它只能用pclose() 函数来关闭, 而不是fclose()函数. 向这个流的写入被转化为 对command 命令的标准输入; 而command 命令的标准输出则是和调用popen(), 函数的进程相同,除非这个被command命令 自己改变. 相反的, 读取 一个“被popen了的” 流, 就相当于读取 command 命令的标准输出, 而 command 的标准输入则是和 调用popen, 函数的进程相同.
注意, popen 函数的输出流默认是被全缓冲的.
pclose函数等待相关的进程结束并返回一个 command 命令的退出状态, 就像 wait4 函数一样.
返回值
如果 fork(2) 或者 pipe(2) 调用失败, 或者它分配不到内存, popen 函数返回 NULL .
如果 wait4 返回一个 错误, 或者 其他什么错误 发生, pclose 函数返回一个 -1.
错误
如果内存空间开辟失败, popen 函数并不设置 errno . 如果内部的 fork() 函数或者pipe() 函数失败, errno 则会被适当的设置.
如果type 参数是不可用的, 而且这个状态被侦测到, errno 将被设置成 EINVAL.
如果pclose() 函数不能侦测到子进程的状态, errno 将被设置成 ECHILD.
BUGS
因为 command 命令 读取的 标准输入 和 调用 popen() 函数 的 进程 共享 一个 “搜索偏移量”, 所以, 如果 原进程 已经 完成了 一个 缓冲的读取, 那么 command 命令的 输入位置 将是 不可预料的. 相似的, command 命令的 输出 会和 原进程的输出 混杂在一起. 后者 可以 在调用 popen. 函数前 调用 fflush(3) 函数 来避免.
范例
1#include <stdio.h>
2#include <stdlib.h>
3
4int main(int argc, char *argv[])
5{
6 char buf[128];
7 FILE *pp;
8
9 if((pp = popen("ls -l", "r")) == NULL)
10 {
11 perror("popen");
12 exit(EXIT_FAILURE);
13 }
14
15 while(fgets(buf, sizeof(buf), pp))
16 {
17 printf("%s", buf);
18 }
19
20 pclose(pp);
21
22 exit(EXIT_SUCCESS);
23}
24
2.FIFO
管道没有名字,无亲缘关系的两个进程无法创建一个彼此间的管道并用于IPC(不考虑使用描述字传递)。
FIFO指代先进先出,Unix中的FIFO类似于管道。它是一个单向(半双工)数据流。不同于管道的是,每个FIFO有一个路径名与之关联,从而允许无亲缘关系的进程访问同一个FIFO。FIFO也称为有名管道(named pipe)
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
其中pathname是一个普通的Unix路径名,它是该FIFO的名字。
mode参数指定文件权限位,类似open的第三个参数。
成功返回0 ,失败返回-1;
错误代码
EACCESS 参数pathname所指定的目录路径无可执行的权限
EEXIST 参数pathname所指定的文件已存在。
ENAMETOOLONG 参数pathname的路径名称太长。
ENOENT 参数pathname包含的目录不存在
ENOSPC 文件系统的剩余空间不足
ENOTDIR 参数pathname路径中的目录存在但却非真正的目录。
EROFS 参数pathname指定的文件存在于只读文件系统内。
范例
posted on 2009-02-28 18:12
nichii 阅读(414)
评论(1) 编辑 收藏 引用