IPC--管道

IPC--管道
管道的问题在于它们只能在具有共同祖先(指父子进程关系)的进程间使用,不过该问题已随有名管道(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
 8 int  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
 4 int  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指定的文件存在于只读文件系统内。

范例


 

你可能感兴趣的:(IPC--管道)