linux中管道(pipe)一谈

/***********************************************
  管道(pipe)是Linux上进程间通信的一种方式,其是半双工(数据流只能在一个方向上流动(还需要经过内核),及要么是接收,要么是发送),并且只能在父子进程 或 具有公共祖先的两个子进程间通信。
  管道(pipe)一般是在调用fork函数之前调用pipe函数创建,这样的话,fork后子进程将得到父进程的两个管道描述符副本(相当于调用了两次dup)。

/***********************************************
相关函数:
  #include <unistd.h>
  int pipe(int fd[2])
                        返回值:成功返回0,失败返回-1
***********************************************/
注意: 函数执行成功后为参数返回两个描述符,fd[0]为读而打                    开,fd[1]为写而打开

很多系统在stat结构体st_size成员中储存管道中可用于读写字节数。但这是不可移植的。(对于struct stat不了解的请百度)


使用管道有几点注意事项:
1.当读(read)一个写(write)端已经关闭的管道,程序(read)将读完管道中所有数据后返回0。
2.当写(write)一个读(write)端已经关闭的管道,将产SIGPIP信号,程序(write)返回-1,并将错误码置为EPIPE
3.如果管道中数据为空,则(read)将永久堵塞直到有数据或写(write)端关闭。
4.如果管道已被数据填满,则(write)将会堵塞直到管道中有空与空间 或 写端(read)关闭,(管道容量可用fpathconf函数得到,使用_PC_PIPE_BUF参数)。
***********************************************/


实例1:
    使用管道实现 ls -l | wc -c 操作

分析:
    执行ls -l后其结果将会被打印到终端(即使用到STDOUT_FILENO)标准输出,wc -c(统计单词数量)是从标准输入中得到内容(即使用到STDIN_FILENO)然后进行统计。

思路:
    子进程中调用exec函数族执行ls -l命令,并调用dup2函数将子进程中的fd[1]描述符复制为标准输出(STDOUT_FILENO),这用的话,但向标准输出中写数据是就等同于向管道中写数据。
    对于父进程则调用exec函数族执行wc -c命令,然后调用dup2将fd[0]复制为标准输入,

实例代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

int main(int argc, char **argv)
{
    pid_t pid;
    int fd[2];

    /**创建管道**/
    if (pipe(fd) < 0) {
        perror("pipe error");
        return EXIT_FAILURE;
    }

    /**调用fork函数创建子进程**/
    if ((pid = fork()) < 0) {
        perror("fork error");
        return EXIT_FAILURE;
    } else if (0 == pid) {  
        close(fd[0]); /**关闭不必要的描述符**/
        if (fd[1] != STDOUT_FILENO) {
            if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
                perror("dup2 error to stdout");
                return EXIT_FAILURE;
            }
            close(fd[1]);
        }

        execlp("ls", "ls", "-l", NULL);
        exit(EXIT_SUCCESS);
    } 

    //sleep(2);
    close(fd[1]);
    if (fd[0] != STDIN_FILENO) {
        if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) {
            perror("dup2 error to stdin");
            return EXIT_FAILURE;
        }
        close(fd[0]);
    }

    execlp("wc", "wc", "-c", NULL);

    /**回收子进程的退出状态,避免产生僵死进程**/
    if (waitpid(pid, NULL, 0) < 0) {
        perror("waitpid error");
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}
实例2:
    实现分页功能 即more命令

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#define PAGE "/bin/more"
#define MAXLINE 1024

int main(int argc, char **argv)
{
    pid_t pid;
    int fd[2];
    char line[MAXLINE];
    char *page = NULL, *temp = NULL, *arg = NULL;
    FILE *fp = NULL;

    if (argc != 2) {
        printf("Usage: %s <filename>\n", argv[0]);
        return EXIT_FAILURE;
    }

    if ((fp = fopen(argv[1], "r")) == NULL) {
        perror("fopen error");
        return EXIT_FAILURE;
    }

    if (pipe(fd) < 0) {
        perror("pipe error");
        return EXIT_FAILURE;
    }

    if ((pid = fork()) < 0) {
        perror("fork error");
        return EXIT_FAILURE;
    } else if (0 == pid) {
        close(fd[0]);

        int len;
        while (fgets(line, MAXLINE, fp) != NULL) {
            len = strlen(line);

            int offset = 0, n = 0;
            while (len) {
                n = write(fd[1], line+offset, len);
                offset += n;
                len -= n;
            }
        }

        if (ferror(fp)) {
            perror("fgets errro");
            return EXIT_FAILURE;
        }

        fclose(fp);
        close(fd[1]);
    }


    close(fd[1]);

    if (fd[0] != STDIN_FILENO) {
        if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) {
            perror("dup2 error to stdin");
            return EXIT_FAILURE;
        }

        close(fd[0]);
    }

    /***得到PAGE对应的环境参数***/
    if ((page = getenv("PAGE")) != NULL) {
        arg = page;
    } else {
        arg = PAGE;
    }

    /**
    *strrchr函数判断'\'字符,
    *在arg字符串中最后一次出现的位置
    **/
    if ((temp = strrchr(arg, '/')) != NULL) {
        temp++;
    } else {
        temp = arg;
    }

    execl(arg, temp, NULL);

    if (waitpid(pid, NULL, 0) < 0) {
        perror("waitpid error");
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章