• 第十五章:进程间通信


    15.1:引言

     第8章说明了进程控制原语并且观察了如何调用多个进程。但是这些进程之间交换信息的方法只能经由fork或exec传送打开文件,或者通过文件系统。本章将说明进程之间相互通信的其他技术--IPC(InterProcess Communication)。

    过去,Unix系统IPC是各种进程间通信方式的统称,但是其中极少能在所有Unix系统实现中进行移植。随着Posix和open group(以前是X/Open)标准化的推进和影响的扩大,情况虽然已得到改善,但差别仍然存在。下图列出了本书讨论的四种实现所支持的不同形式的IPC。

    我们将IPC的讨论分为3章。本章讨论经典的IPC:管道、FIFO、消息队列、信号量以及共享存储器。下一章将观察使用套接字的网络IPC。第17章将考查IPC的某些高级特征。

    15.2:管道

    管道是由调用pipe函数而创建的:

    #include <unistd.h>
    int pipe(int filedes[2]);
    // 返回值:若成功则返回0,若出错则返回-1

    经由参数filedes返回的两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开。

    调用fork之后做什么取决于我们想要有的数据流的方向。对于从父进程到子进程的管道,父进程关闭管道的读端(fd[0]),子进程则关闭写端(fd[1])。下图显示了在此之后描述符的安排。

    为了构造从子进程到父进程的管道,父进程关闭fd[1],子进程关闭fd[0]。

    当管道的一端被关闭后,下列两条规则起作用:

    (1)当读一个写端已被关闭的管道时,在所有数据都被读取之后,read返回0,以指示达到了文件结束处。

    (2)如果写一个读端已被关闭的管道时,则产生信号SIGPIPE。如果忽略该信号或者捕捉该信号并从其处理程序返回,则write返回-1,errno设置为EPIPE。

    示例:经由管道父进程向子进程传送数据

    #include <unistd.h>
    #include <stdio.h>
    #include <errno.h>
    
    #define MAXLINE 256
    
    int main(void)
    {
        int n;
        int fd[2];
        pid_t pid;
        char line[MAXLINE];
    
        if (pipe(fd) < 0)
        {
            perror("pipe error");
        }
    
        if ((pid = fork()) > 0)
        { // parent 
            close(fd[0]);
            write(fd[1], "Hello world!", 12);
        }
        else if (pid == 0)
        { // child
            close(fd[1]);
            n = read(fd[0], line, MAXLINE);
            write(1, line, n);
        }
        else
        { // error
           perror("fork error"); 
        }
    
        return 0;
    }

    在上面的例子中,直接对管道描述符调用read和write。更好的方法是将管道描述符复制为标准输入和标准输出。在此之后通常子进程执行另一个程序,改程序或者从标准输入读数据,或者将数据写至其标准输出。

    试编写一个程序,其功能时每次一页显示已产生的输出。

    示例:将文件复制到分页程序

    #include <unistd.h>
    #include <errno.h>
    #include <sys/wait.h>
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define DEF_PAGER "/bin/more" // default pager program
    #define MAXLINE 256
    
    int main(int argc, char **argv)
    {
        int n;
        int fd[2];
        pid_t pid;
        char *pager, *argv0;
        char line[MAXLINE + 1];
        memset(line, 0, MAXLINE + 1);
        FILE *fp;
    
        if (argc != 2)
        {
            printf("Usage: a.out <pathname>
    ");
            return -1;
        }
    
        if ((fp = fopen(argv[1], "r")) == NULL)
        {
            perror("can't open file");
            return -1;
        }
    
        if (pipe(fd) < 0)
        {
            perror("pipe error");
            return -1;
        }
    
        if ((pid = fork()) > 0)
        { // parent
            close(fd[0]);
            while (fgets(line, MAXLINE, fp) != NULL)
            {
                n = strlen(line);
                if (write(fd[1], line, n) != n)
                {
                    perror("write error to pipe");
                }
            }
    
            if (ferror(fp))
            {
                perror("fgets error");
            }
            close(fd[1]);
            if (waitpid(pid, NULL, 0) < 0)
            {
                perror("waitpid error");
            }
    
            return 0;
        }
        else if (pid == 0)
        { // child
            close(fd[1]);
            if (fd[0] != STDIN_FILENO)
            {
                if (dup2(fd[0], STDIN_FILENO) != 0)
                {
                    perror("dup2 error to stdin");
                }
                close(fd[0]);
            }
            pager = getenv("PAGER");
    
            if ((pager = getenv("PAGER")) == NULL)
            {
                pager = DEF_PAGER;
            }
    
            if ((argv0 = strrchr(pager, '/')) != NULL)
            {
                argv0++;
            }
            else
            {
                argv0 = pager;
            }
    
            if (execl(pager, argv0, (char*)0) < 0)
            {
                perror("execl error for pager");
            }
        }
        else
        { // error
            perror("fork error");
            return -1;
        }
    
        return 0;
    }

    15.3:popen和pclose函数

    常见的操作是创建一个管道连接到另一个进程,然后读取其输出或者向其输入端发送数据,为此,标准I/O库提供了两个函数popen和pclose。这两个函数实现的操作是:创建一个管道,调用fork产生一个子进程,关闭管道的不使用端,执行一个shell以运行命令,然后等待命令终止。

  • 相关阅读:
    mongo与node的两种连接方式 mongoskin && mongoclient 的基本使用
    promise 源码 简单分析
    node之 glob模块 相对路径绝对路径浅析
    webpack 多入口打包配置示例
    webpack 多入口打包分析
    MyEclipse8.5 以debug模式启动tomcat6.0服务器 报错cannot connect to vm。
    java开发bug 在启动Tomcat 6.0时发现第一条信息便是
    java 开发, jdk 1.6 官方下载地址
    电脑故障
    myeclipse添加源码支持
  • 原文地址:https://www.cnblogs.com/lit10050528/p/4929553.html
Copyright © 2020-2023  润新知