资讯专栏INFORMATION COLUMN

Linuxc - 未命名管道-pipe

ACb0y / 2082人阅读

摘要:未命名管道详情见环境高级编程小节管道试系统的最古老形式,也是最常用的。现在,某些系统提供全双工管道,但是为了最佳的可移植性,我们决定预先假定系统支持全双工管道。管道只能在具有公共祖先的两个进程之间使用。等所执行的命令都是协同进程。

linuxc-未命名管道-pipe

--详情见《unix环境高级编程》15.2小节

管道试UNIX系统IPC的最古老形式,也是最常用的IPC。

pipe管道的局限性:

历史上,它们是半双工的(即数据只能在一个方向上流动)。现在,某些系统提供全双工管道,但是为了最佳的可移植性,我们决定预先假定系统支持全双工管道。

管道只能在具有公共祖先的两个进程之间使用。通常,一个管道由一个进程创建,在进程调用fork之后,这个管道就能在父进程和子进程之间使用了。

相关函数
#include 
int pipe(int fd[2]);    //创建一个管道

#include 
//创建一个连接另一个进程的管道
FILE *popen(const char *cmdstring, const char *type); //s:FILE * err:NULL
int pclose(FILE *fp);                                  //s:终止状态 err:-1

popen和pclose函数的实现

#include "apue.h"
#include 
#include 
#include 

/*
 * Pointer to array allocated at run-time.
 */
static pid_t    *childpid = NULL;

/*
 * From our open_max(), {Prog openmax}.
 */
static int        maxfd;

FILE *
popen(const char *cmdstring, const char *type)
{
    int        i;
    int        pfd[2];
    pid_t    pid;
    FILE    *fp;

    /* only allow "r" or "w" */
    if ((type[0] != "r" && type[0] != "w") || type[1] != 0) {
        errno = EINVAL;
        return(NULL);
    }

    if (childpid == NULL) {        /* first time through */
        /* allocate zeroed out array for child pids */
        maxfd = open_max();
        if ((childpid = calloc(maxfd, sizeof(pid_t))) == NULL)
            return(NULL);
    }

    if (pipe(pfd) < 0)
        return(NULL);    /* errno set by pipe() */
    if (pfd[0] >= maxfd || pfd[1] >= maxfd) {
        close(pfd[0]);
        close(pfd[1]);
        errno = EMFILE;
        return(NULL);
    }

    if ((pid = fork()) < 0) {
        return(NULL);    /* errno set by fork() */
    } else if (pid == 0) {                            /* child */
        if (*type == "r") {
            close(pfd[0]);
            if (pfd[1] != STDOUT_FILENO) {
                dup2(pfd[1], STDOUT_FILENO);
                close(pfd[1]);
            }
        } else {
            close(pfd[1]);
            if (pfd[0] != STDIN_FILENO) {
                dup2(pfd[0], STDIN_FILENO);
                close(pfd[0]);
            }
        }

        /* close all descriptors in childpid[] */
        for (i = 0; i < maxfd; i++)
            if (childpid[i] > 0)
                close(i);

        execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
        _exit(127);
    }

    /* parent continues... */
    if (*type == "r") {
        close(pfd[1]);
        if ((fp = fdopen(pfd[0], type)) == NULL)
            return(NULL);
    } else {
        close(pfd[0]);
        if ((fp = fdopen(pfd[1], type)) == NULL)
            return(NULL);
    }

    childpid[fileno(fp)] = pid;    /* remember child pid for this fd */
    return(fp);
}

int
pclose(FILE *fp)
{
    int        fd, stat;
    pid_t    pid;

    if (childpid == NULL) {
        errno = EINVAL;
        return(-1);        /* popen() has never been called */
    }

    fd = fileno(fp);
    if (fd >= maxfd) {
        errno = EINVAL;
        return(-1);        /* invalid file descriptor */
    }
    if ((pid = childpid[fd]) == 0) {
        errno = EINVAL;
        return(-1);        /* fp wasn"t opened by popen() */
    }

    childpid[fd] = 0;
    if (fclose(fp) == EOF)
        return(-1);

    while (waitpid(pid, &stat, 0) < 0)
        if (errno != EINTR)
            return(-1);    /* error other than EINTR from waitpid() */

    return(stat);    /* return child"s termination status */
}
示例

创建一个从父进程到子进程的管道,并且父进程经由该管道向子进程传送数据。

//pipe
#include "apue.h"

int
main(void)
{
    int        n;
    int        fd[2];
    pid_t    pid;
    char    line[MAXLINE];

    if (pipe(fd) < 0)
        err_sys("pipe error");
    if ((pid = fork()) < 0) {
        err_sys("fork error");
    } else if (pid > 0) {        /* parent */
        close(fd[0]);
        write(fd[1], "hello world
", 12);
    } else {                    /* child */
        close(fd[1]);
        n = read(fd[0], line, MAXLINE);
        write(STDOUT_FILENO, line, n);
    }
    exit(0);
}

编写一个程序,功能是调用已有的分页程序每次一页地显示已产生的输出。

//pipe
#include "apue.h"
#include 

#define    DEF_PAGER    "/bin/more"        /* default pager program */

int
main(int argc, char *argv[])
{
    int        n;
    int        fd[2];
    pid_t    pid;
    char    *pager, *argv0;
    char    line[MAXLINE];
    FILE    *fp;

    if (argc != 2)
        err_quit("usage: a.out ");

    if ((fp = fopen(argv[1], "r")) == NULL)
        err_sys("can"t open %s", argv[1]);
    if (pipe(fd) < 0)
        err_sys("pipe error");

    if ((pid = fork()) < 0) {
        err_sys("fork error");
    } else if (pid > 0) {                                /* parent */
        close(fd[0]);        /* close read end */

        /* parent copies argv[1] to pipe */
        while (fgets(line, MAXLINE, fp) != NULL) {
            n = strlen(line);
            if (write(fd[1], line, n) != n)
                err_sys("write error to pipe");
        }
        if (ferror(fp))
            err_sys("fgets error");

        close(fd[1]);    /* close write end of pipe for reader */

        if (waitpid(pid, NULL, 0) < 0)
            err_sys("waitpid error");
        exit(0);
    } else {                                        /* child */
        close(fd[1]);    /* close write end */
        if (fd[0] != STDIN_FILENO) {
            if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO)
                err_sys("dup2 error to stdin");
            close(fd[0]);    /* don"t need this after dup2 */
        }

        /* get arguments for execl() */
        if ((pager = getenv("PAGER")) == NULL)
            pager = DEF_PAGER;
        if ((argv0 = strrchr(pager, "/")) != NULL)
            argv0++;        /* step past rightmost slash */
        else
            argv0 = pager;    /* no slash in pager */

        if (execl(pager, argv0, (char *)0) < 0)
            err_sys("execl error for %s", pager);
    }
    exit(0);
}
//popen
#include "apue.h"
#include 

#define    PAGER    "${PAGER:-more}" /* environment variable, or default */

int
main(int argc, char *argv[])
{
    char    line[MAXLINE];
    FILE    *fpin, *fpout;

    if (argc != 2)
        err_quit("usage: a.out ");
    if ((fpin = fopen(argv[1], "r")) == NULL)
        err_sys("can"t open %s", argv[1]);

    if ((fpout = popen(PAGER, "w")) == NULL)
        err_sys("popen error");

    /* copy argv[1] to pager */
    while (fgets(line, MAXLINE, fpin) != NULL) {
        if (fputs(line, fpout) == EOF)
            err_sys("fputs error to pipe");
    }
    if (ferror(fpin))
        err_sys("fgets error");
    if (pclose(fpout) == -1)
        err_sys("pclose error");

    exit(0);
}

实现进程竞争的函数:TELL_WAIT、TELL_PARENT、TELL_CHILD、WAIT_PARENT和WAIT_CHILD。

#include "apue.h"

static int    pfd1[2], pfd2[2];

void
TELL_WAIT(void)
{
    if (pipe(pfd1) < 0 || pipe(pfd2) < 0)
        err_sys("pipe error");
}

void
TELL_PARENT(pid_t pid)
{
    if (write(pfd2[1], "c", 1) != 1)
        err_sys("write error");
}

void
WAIT_PARENT(void)
{
    char    c;

    if (read(pfd1[0], &c, 1) != 1)
        err_sys("read error");

    if (c != "p")
        err_quit("WAIT_PARENT: incorrect data");
}

void
TELL_CHILD(pid_t pid)
{
    if (write(pfd1[1], "p", 1) != 1)
        err_sys("write error");
}

void
WAIT_CHILD(void)
{
    char    c;

    if (read(pfd2[0], &c, 1) != 1)
        err_sys("read error");

    if (c != "c")
        err_quit("WAIT_CHILD: incorrect data");
}

过滤程序:它变换运行命令的输入或输出。(就是改变另一个程序的输入或输出)

操作过滤程序。它将标准输入复制到标准输出,在复制时将大写字符变换为小写字符。

//popen1.c
#include "apue.h"
#include 

int
main(void)
{
    char    line[MAXLINE];
    FILE    *fpin;

    if ((fpin = popen("myuclc", "r")) == NULL)
        err_sys("popen error");
    for ( ; ; ) {
        fputs("prompt> ", stdout);
        fflush(stdout);
        if (fgets(line, MAXLINE, fpin) == NULL)    /* read from pipe */
            break;
        if (fputs(line, stdout) == EOF)
            err_sys("fputs error to pipe");
    }
    if (pclose(fpin) == -1)
        err_sys("pclose error");
    putchar("
");
    exit(0);
}
//myuclc.c
#include "apue.h"
#include 

int
main(void)
{
    int        c;

    while ((c = getchar()) != EOF) {
        if (isupper(c))
            c = tolower(c);
        if (putchar(c) == EOF)
            err_sys("output error");
        if (c == "
")
            fflush(stdout);
    }
    exit(0);
}

协同进程:UNIX系统过滤程序从标准输入读取数据,向标准输出写数据。几个过滤程序通常在shell管道中线性连接。当一个过滤程序既产生某个过滤程序的输入,又读取该过滤程序的输出时,它就变成了协同进程(coprocess)。bash、cshell、sh等所执行的命令都是协同进程。

注意:

协同进程中的io缓冲为全缓冲。当无法改变协同进程中的缓冲方式时,就要使协同进程认为它的标准输入输出都被连接到了一个终端,才能正常显示。(见19章-伪终端)

协同进程中的stderr输出。有些实时刷新的程序经常会将输出放到stderr上。

//pipe4.c
#include "apue.h"

static void    sig_pipe(int);        /* our signal handler */

int
main(void)
{
    int        n, fd1[2], fd2[2];
    pid_t    pid;
    char    line[MAXLINE];

    if (signal(SIGPIPE, sig_pipe) == SIG_ERR)
        err_sys("signal error");

    if (pipe(fd1) < 0 || pipe(fd2) < 0)
        err_sys("pipe error");

    if ((pid = fork()) < 0) {
        err_sys("fork error");
    } else if (pid > 0) {                            /* parent */
        close(fd1[0]);
        close(fd2[1]);

        while (fgets(line, MAXLINE, stdin) != NULL) {
            n = strlen(line);
            if (write(fd1[1], line, n) != n)
                err_sys("write error to pipe");
            if ((n = read(fd2[0], line, MAXLINE)) < 0)
                err_sys("read error from pipe");
            if (n == 0) {
                err_msg("child closed pipe");
                break;
            }
            line[n] = 0;    /* null terminate */
            if (fputs(line, stdout) == EOF)
                err_sys("fputs error");
        }

        if (ferror(stdin))
            err_sys("fgets error on stdin");
        exit(0);
    } else {                                    /* child */
        close(fd1[1]);
        close(fd2[0]);
        if (fd1[0] != STDIN_FILENO) {
            if (dup2(fd1[0], STDIN_FILENO) != STDIN_FILENO)
                err_sys("dup2 error to stdin");
            close(fd1[0]);
        }

        if (fd2[1] != STDOUT_FILENO) {
            if (dup2(fd2[1], STDOUT_FILENO) != STDOUT_FILENO)
                err_sys("dup2 error to stdout");
            close(fd2[1]);
        }
        if (execl("./add2", "add2", (char *)0) < 0)
            err_sys("execl error");
    }
    exit(0);
}

static void
sig_pipe(int signo)
{
    printf("SIGPIPE caught
");
    exit(1);
}
//add2.c
#include "apue.h"

int
main(void)
{
    int        int1, int2;
    char    line[MAXLINE];
    
    if (setvbuf(stdin, NULL, _IOLBF, 0) != 0)//改变in/out的缓冲方式
        err_sys("setvbuf error");
    if (setvbuf(stdout, NULL, _IOLBF, 0) != 0)
        err_sys("setvbuf error");
    
    while (fgets(line, MAXLINE, stdin) != NULL) {
        if (sscanf(line, "%d%d", &int1, &int2) == 2) {
            if (printf("%d
", int1 + int2) == EOF)
                err_sys("printf error");
        } else {
            if (printf("invalid args
") == EOF)
                err_sys("printf error");
        }
    }
    exit(0);
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/63169.html

相关文章

  • Linuxc - posix信号量的应用(生产者-消费者)

    摘要:信号量和互斥锁的区别互斥锁只允许一个线程进入临界区,而信号量允许多个线程同时进入临界区。其中表示缓冲区的空单元数,初始值为表示缓冲区非空单元数,初始值为是互斥信号量,初始值为当然也可以用互斥锁来实现互斥操作。 linuxc-posix信号量的应用(生产者-消费者) 在unix环境高级编程中提到了两种信号量,两种信号量作用和应用场景基本相同,但是posix信号量更常用,跨平台能力强,性能...

    marek 评论0 收藏0
  • Linuxc - 进程竞争条件

    摘要:进程竞争条件当多个进程都企图对共享数据进行某种处理,而最后的结果又取决于进程运行的顺序时,我们认为发生了竞争条件。详情见环境高级编程小节解决方法等条件轮询进程间通讯,主要实现个函数。 linuxc-进程竞争条件 当多个进程都企图对共享数据进行某种处理,而最后的结果又取决于进程运行的顺序时,我们认为发生了竞争条件(race condition)。详情见《unix环境高级编程》8.9小节 ...

    eternalshallow 评论0 收藏0
  • Linuxc - 消息队列

    摘要:用于控制当前消息队列满或队列消息到达系统范围的限制时将要发生的事情返回值成功,消息数据的一分副本将被放到消息队列中,并返回,失败时返回用来从一个消息队列获取消息是由函数返回的消息队列标识符。 linuxc-消息队列 ​ --详情见《unix环境高级编程》15.7小节 ​ 在《unix环境高级编程》15.7小节最后,总结了消息队列的优缺点。结论是,在新的应用程序中不应当再使用...

    lansheng228 评论0 收藏0
  • Linuxc - 共享内存

    摘要:数据或代码页会根据需要在物理内存与磁盘之间移动。因此,采用共享内存的通信方式效率是最高的。必须控制同一时刻只有一个进程对共享内存区域写入数据,否则会造成数据的混乱。 linuxc-共享内存 ​ --详细概念见《unix环境高级编程》15.9小节 ​ 共享内存可以说是Linux 下最快速、最有效的进程间通信方式。两个不同进程A 、B 共享内存的意思是,同一块物理内存被映射到进...

    Jeffrrey 评论0 收藏0
  • Linux 的进程间通信:管道

    摘要:微博微信公众号系统技术前言管道是环境中历史最悠久的进程间通信方式。用一个图来说明这个程序的状态就是这样的一个进程自己给自己发送消息这当然不叫进程间通信,所以实际情况中我们不会在单个进程中使用管道。子进程关闭管道的写端,只读管道。 本文由云+社区发表作者:邹立巍 版权声明: 本文章内容在非商业使用前提下可无需授权任意转载、发布。 转载、发布请务必注明作者和其微博、微信公众号地址,以...

    shuibo 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<