《UNIX环境高级编程》笔记--管道

1.pipe

管道是UNIX系统IPC的最古老形式,并且所有的UNIX系统都提供此种通信机制。管道有下列两种局限性:

a.历史上,它们是半双工的,现在某些系统提供全双工管道,但是为了最佳的可移植性,我们决不应预先假定系统使用此

特性。

b.它们只能在具有公共祖先的进程之间使用。通常一个管道由一个进程创建,然后该进程调用fork,此后父子进程之间可以

使用该管道。

(FIFO没有第二种局限性,UNIX域套接字和命名流没有这两种局限性)

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

#include<unistd.h>
int pipe(int filedes[2]);
//成功则返回0,出错则返回-1.
经由参数filedes返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输入。

《UNIX环境高级编程》笔记--管道_第1张图片

调用fork之后做什么取决于我们想要有的数据流的方向,对于从父进程到子进程的管道,父进程关闭管道的读端fd[0],子

进程则关闭写端fd[1]。

调用fork之后的半双工管道:

《UNIX环境高级编程》笔记--管道_第2张图片

从父进程到子进程的管道:

《UNIX环境高级编程》笔记--管道_第3张图片

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

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

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

b.如果写一个读端已经被关闭的管道,则产生信号SIGPIPE。如果忽略该信号或者捕获该信号并从其处理程序返回,则

write返回-1,errno设置为EPIPE。

在写管道是,常量PIPE_BUF规定了内核中管道缓冲区的大小,如果对管道调用write,而且要求写的字节数小于等于

PIPE_BUF,则此操作不会与其他进程对同一管道的write操作穿插进行。但是,若有多个进程同时写一个管道,而且

进程要求写的字节数超过PIPE_BUF字节数时,写操作的数据可能穿插。


实践:

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

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

        if(pipe(fd) < 0){
                perror("pipe");
                return -1;
        }

        if((pid = fork()) < 0){
                perror("fork");
                return -1;
        }else if(pid > 0){ //parent
                close(fd[0]);
                if(fd[1] != STDOUT_FILENO){
                        dup2(fd[1],STDOUT_FILENO);
                }
                execl("/bin/ls","ls",(char*)0);
        }else{ //child
                close(fd[1]);
                while((n =read(fd[0],line,256)) > 0){
                        if(write(STDOUT_FILENO,line,n) != n){
                                perror("write");
                                exit(-1);
                        }
                }
                close(fd[0]);
        }
        return 0;
}

运行结果:

WinVNC.log
a.out
a.txt
b.txt
chlock
dtest
lock
lock.txt

在父进程中将管道描述符赋值为标准输出,所以在执行ls后,原本是写到标准输出的,现在写到fd[1]中。而子进程将读到

的内容显示到标准输出上。

2.popen和pclose函数

常见的操作是创建一个管道连接到另一个进程,然后读取其输出或向其输入端发送数据,为此,标准IO提供了两个函数

popen和pclose。这两个函数实现的操作是:创建一个管道,调用fork产生一个子进程管道的不使用端,执行一个shell

以运行命令,然后等待命令终止。

#include<stdio.h>
FILE *popen(const char* cmdstring, const char *type);  //若成功则返回文件指针,出错则返回NULL。
int pclose(FILE *fp); //返回cmdstring的终止状态,若出错则返回-1。
函数popen先执行fork,然后调用exec以执行cmdstring,并返回一个标准IO文件指针,如果type是r,则文件指针连接到

cmdstring的标准输出,如果type是w,则文件指针连接到cmdstring的标准输入。

pclose函数关闭标准IO流,等待命令执行结束,然后返回shell的终止状态。如果shell不能被执行,则pclose返回的终止状

态与shell已执行exit(127)一样。


实践:

#include<stdio.h>

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

        if((fpin = popen("/bin/ls","r")) == NULL){
                perror("popen");
                return -1;
        }

        while(fgets(line, 256, fpin) != NULL){
                if(fputs(line,stdout) == EOF){
                        perror("fputs");
                        return -1;
                }
        }

        return 0;
}
运行结果:

[root@yanPC apue]# ./a.out
a.out
abort.c
core.16354
core.16355
fork.c
popen.c
sigsetjmp.c
sigsuspend.c
sleep.c
template.c
vfork.c
wait.c

3.命名管道

FIFO被称为命名管道。匿名管道只能由相关进程使用,这些相关进程的共同祖先进程创建了管道,但是通过FIFO,不相关的

进程也能交换数据。

FIFO是一种文件类型。创建FIFO类似于创建文件,确实,FIFO的路径名存在于文件系统中。

#include<sys/stat.h>
int mkfifo(const char *pathname, mode_t mode); //成功则返回0,出错则返回-1.
mkfifo函数中mode参数的规格说明与open函数中的mode相同,一旦已经用mkfifo创建一个FIFO,就可用open打开它,其实,

一般的文件IO函数都可用于FIFO(close,read,write,unlink)。

实践:

#include<stdio.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>


#define MYFIFO "myfifo"


int main(void){
        char buffer[256];
        pid_t pid;
        int fd;


        unlink(MYFIFO);
        if(mkfifo(MYFIFO,0666) < 0){
                perror("mkfifo");
                return -1;
        }


        if((pid = fork())<0){
                perror("fork");
                return -1;
        }else if(pid > 0){
                char s[] = "hello world.";
                fd = open(MYFIFO,O_WRONLY);
                write(fd,s,sizeof(s));
                close(fd);
        }else{
                fd = open(MYFIFO,O_RDONLY);
                read(fd,buffer,256);
                printf("%s\n",buffer);
                close(fd);
                exit(0);
        }


        waitpid(pid,NULL,0);
        return 0;
}
运行结果:

[root@yanPC apue]# ./a.out
hello world.

你可能感兴趣的:(《UNIX环境高级编程》笔记--管道)