Linux进程间通信——管道

一、什么是管道

当从一个进程连接数据流到另一个进程时,我们使用术语管道(pipe)。我们通常是把一个进程的输出通过管道连接到另一个进程的输入。大多数Linux用户都使用过shell,不可避免地会使用管道进行一些数据的分析和处理,命令格式如下(通常通过符号“|"来使用管道):cmd1 | cmd2

shell负责安排两个命令的标准输入和标准输出,这样:

1.      cmd1的标准输入来自终端键盘

2.      cmd1的标准输出传递给cmd2,作为它的标准输入

3.      cmd2的标准输出连接到终端屏幕

shell所做的工作从效果上看是对标准输出和标准输出流进行了重新连接,使数据流从键盘输出通过两个命令最终输出到屏幕上。

例如:在shell中输入命令:ls -l | grep string,我们知道ls命令(其实也是一个进程)会把当前目录中的文件都列出来,但是它不会直接输出,而是把本来要输出到屏幕上的数据通过管道输出到grep这个进程中,作为grep这个进程的输入,然后这个进程对输入的信息进行筛选,把存在string的信息的字符串(以行为单位)打印在屏幕上。

2.进程管道

 最简单的两个程序之间的数据传递的方法就是使用popen和pclose函数。它们的原型如下:

#include <stdio.h>
FILE* popen (const char *command, const char *open_mode);
int pclose(FILE *stream_to_close);

一、popen函数

popen函数允许一个程序将另一个程序作为新进程来启动,并可以传递数据给它或者通过它接收数据。command字符串要运行的程序名和相应的参数。open_mode必须是”r”或者”w”( 管道是被定义成单向的,只能定义成 只读或者 只写, 不能是 两者同时, 结果流也相应的 是只读 或者 只写.)。

如果open_mode是”r”,被调用程序的输出就可以被调用程序使用,调用程序利用popen函数返回的FILE*文件流指针,就可以通过常用的stdio库函数(如fread)来读取被调用程序的输出。如果open_mode是”w”,调用程序就可以用fwrite调用向被调用命令发送数据,而被调用程序可以在自己的标准输入上读取这些数据。被调用的程序通常不会意识到自己正在从另一个进程读取数据,它只是简单在标准输入上读取数据,然后做出相应的操作。

每个popen调用都必须指定”r”或”w”,在popen函数的标准实现中不支持任何其他选项。这就意味着,我们不能调用另一个程序并同时对它进行读写操作。popen函数在失败时返回一个空指针。如果你想通过管道实现双向通信,最普通的解决方法是使用两个管道,每个管道负责一个方向的数据流。

二、pclose函数

用popen启动的进程结束时,我们可以用pclose函数关闭与之关联的文件流。pclose调用只在popen启动的进程结束后才返回。如果调用pclose时它仍在运行,pclose调用将等待该进程结束。

pclose调用的返回值通常是它所关闭的文件流所在的进程的退出码。如果调用进程在调用pclose之前执行一个wait语句,被调用进程的退出状态就会丢失,pclose将返回-1并设置errno为EHILD。

现在来看一个简单的popen和pclose示例程序:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
    FILE *read_fp = NULL;
    FILE *write_fp = NULL;
    FILE *fp;
    char buffer[BUFSIZ + 1];
    int chars_read = 0;
    
    //初始化缓冲区
    memset(buffer, '\0', sizeof(buffer));
    //打开ls和grep进程
    read_fp = popen("ls -l", "r");
    write_fp = popen("grep rwxr-xr-x", "w");
    fp = fopen( "test_popen.txt", "w+"); //新建一个可写的文件
    //两个进程都打开成功
    if(read_fp && write_fp && fp)
    {
        //读取一个数据块
        chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp);
        while(chars_read > 0)
        {
            //置字符串结束标记
            buffer[chars_read] = '\0';
            //printf("%s\n",buffer);
            //把数据写入grep进程
            fwrite(buffer, sizeof(char), chars_read, write_fp);  //写入grep进程(作为grep进程的输入)
            fwrite(buffer,sizeof(char), chars_read,fp);//将buffer中的数据写到FILE  *fp对应的流中,也是写到文件中
            //还有数据可读,循环读取数据,直到读完所有数据
            chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp);
        }
        //关闭文件流
        fclose(fp);
        pclose(read_fp);
        pclose(write_fp);
    }
    else
    {
        printf("error\n");
    }
    return 0;
}



三、pipe调用(匿名管道)

匿名管道只能用于有亲缘关系的进程,如父进程和子进程,以及兄弟进程间的通信。

1.匿名管道的创建

 在看过高级的popen函数之后,我们再来看看底层的pipe函数。通过这个函数在两个程序之间传递数据不需要启动一个shell解释请求命令(创建一个匿名管道)。它同时还提供了对读写数据更多的控制。

 pipe函数原型如下所示:

#include <unistd.h>
int pipe(int file_descriptor[2]);

 pipe函数的参数是一个由两个整数类型的文件描述符组成的数组指针。该函数在数组中填上两个新的文件描述符后返回0,如果失败则返回-1并设置errno以表明失败原因。

管道的两端一般使用文件描述符fd[0],fd[1]来表示,两端的任务是固定的,其中一端只能进行读操作,称为管道的读端,用文件符fd[0]表示;另一端只能进行写操作,称为管道的写端,用文件描述符fd[1]来表示。如果进程试图从一个管道的读端写数据,或者向写端读数据都会发生错误。

2.管道的读写
对管道进行读写操作时,可以使用一般文件的I/O函数。从管道中读取数据时,如果管道的写端被关闭,则认为已经读到了数据的末尾,read函数返回读到的字节数为0,管道的写端存在时,如果请求的字节数大于PIPE_BUF,则read函数返回管道中现有数据的字节数,否则返回请求的字节数。

向管道进行写入数据时,系统不会保证写入的原子性,只要管道中有空闲区域,写进程就会向管道中写数据,如果读进程不读走数据,写操作就会一直阻塞,如果管道的读端被关闭,写进程将会收到内核传来的SIFPIPF信号,此时写进程可以选择处理该信号,也可以忽略该信号,默认情况下是终止进程。
  特别注意:这里使用的是文件描述符,而不是文件流,我们必须用底层的 read write 调用来访问数据,因为管道不是正规的文件,不能使用 fread fwrite
示例1:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#define BUFSIZE 256
int main()
{
    pid_t pid;
    int fd[2];       //定义管道描述符
    int status;
    char send[BUFSIZE]="Hello World\n";
    char rec[BUFSIZE];
    if(pipe(fd)<0)   //创建匿名管道
    {
        printf("pipe Error\n");
        exit(1);
    }
    pid=fork();     //创建子进程
    if(pid<0)       //如果子进程创建失败,输入错误信息并退出
    {
        printf("fork error\n");
        exit(1);
    }
    if(pid==0)      //子进程
    {
        close(fd[0]);//关闭管道的读端
        write(fd[1], send, sizeof(send));//向管道写数据
        exit(0);
    }
    else            //父进程
    {
        sleep(1);
        close(fd[1]); //关闭管道的写端
        read(fd[0], rec,sizeof(rec));//从管道读数据
        printf("Received message from child procedd:%s\n",rec);
        if(pid!=wait(&status))   //等待子进程结束
        {
            printf("Wait error\n");
            exit(1);
        }
    }
    return 0;
}



示例2:
复制文件(将文件的读写操作分别放到两个进程中,然后利用进程通信的方式实现文件的逐行复制)。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>
int main()
{
    pid_t pid;
    FILE *fp;
    int fd[2]; //定义管道描述符
    int status;
    char buf[256]; //定义缓冲区
    if(pipe(fd)<0) //创建匿名管道
    {
        printf("pipe error\n");
        exit(1);
    }
    pid=fork();    //创建子进程
    if(pid<0)
    {
        printf("fork error\n");
        exit(1);
    }
    if(pid==0)    //子进程
    {
        fp=fopen("1.txt", "r");
        if(fp==NULL)
        {
            perror("open source file error");
            exit(1);
        }
        while (fgets(buf, sizeof(buf), fp)!=NULL) //逐行读取源文件内容
        {
            close(fd[0]);                       //关闭管道的读端
            write(fd[1], buf, sizeof(buf));     //将源文件内容写入管道
        }
        fclose(fp);  //关闭源文件
        strcpy(buf, "@"); //设置结束字符
        close(fd[0]);
        write(fd[1], buf, sizeof(buf));
    }
    else  //父进程
    {
        sleep(1);
        fp=fopen("2.txt", "w");
        if(fp==NULL)
        {
            perror("open destinationg file failed\n");
            exit(1);
            
        }
        close(fd[1]);  //关闭管道写端
        read(fd[0], buf, sizeof(buf)); //从管道读数据
        while (buf[0]!='@')
        {
            fputs(buf, fp); //将读出的内容写入文件
            close(fd[1]);
            read(fd[0], buf, sizeof(buf));
        }
        fclose(fp);
        if(pid!=wait(&status))  //等待子进程结束
        {
            printf("wait error\n");
            exit(1);
        }
        printf("Done\n");
    }
    return 0;
}
为了练习多进程编程,下面我把两个进程分开,调用execl函数来执行读源文件并写入管道的程序。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>
int main()
{
    pid_t pid;
    FILE *fp;
    int fd[2]; //定义管道描述符
    char buf[256]; //定义缓冲区
    if(pipe(fd)<0) //创建匿名管道
    {
        printf("pipe error\n");
        exit(1);
    }
    pid=vfork();    //创建子进程(用vfork)
    if(pid<0)
    {
        printf("fork error\n");
        exit(1);
    }
    if(pid==0)    //子进程
    {
        char buf2[256];            //通过buf,buf2向子进程传递fd[0],fd[1]
        sprintf(buf, "%d", fd[0]);
        sprintf(buf2, "%d", fd[1]);
        execl("2", "2",buf,buf2,NULL);
        
    }
    else  //父进程
    {
        fp=fopen("2.txt", "w");
        if(fp==NULL)
        {
            perror("open destinationg file failed\n");
            exit(1);
            
        }
        close(fd[1]);  //关闭管道写端
        read(fd[0], buf, sizeof(buf)); //从管道读数据
        while (buf[0]!='@')
        {
            fputs(buf, fp); //将读出的内容写入文件
            close(fd[1]);
            read(fd[0], buf, sizeof(buf));
        }
        fclose(fp);
        printf("Done\n");
    }
    return 0;
}

//读源文件并写入管道的程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>
int main(int argc,char *argv[])
{
    FILE *fp;
    int fd[2];
    char buf[256]; //定义缓冲区
    fp=fopen("1.txt", "r");
    if(fp==NULL)
    {
        perror("open source file error");
        exit(1);
    }
    sscanf(argv[1], "%d", &fd[0]);
    sscanf(argv[2], "%d", &fd[1]);
    while (fgets(buf, sizeof(buf), fp)!=NULL) //逐行读取源文件内容
    {
        close(fd[0]);                       //关闭管道的读端
        write(fd[1], buf, sizeof(buf));     //将源文件内容写入管道
    }
    fclose(fp);  //关闭源文件
    strcpy(buf, "@"); //设置结束字符
    close(fd[0]);
    write(fd[1], buf, sizeof(buf));
    return 0;
}

到此为止所示的所有管道都是半双工的即单向的,只提供一个方向的数据流,当需要一个双向数据流时,我们必须创建两个管道,每个方向一个,实际步骤如下:
(1)创建管道1(fd1[0]和fd1[1])和管道2(fd2[0]和fd2[1]);
(2)fork
(3)父进程关闭管道1的读出端(fd1[0]);
(4)父进程关闭管道2的写入端(fd2[1]);
(5)子进程关闭管道1的写入端(fd1[1]);
(6)子进程关闭管道2的读出端(fd2[0]);



现在使用管道实现一个客户-服务器例子,main函数创建两个管道并用fork生成一个子进程,客户然后作为父进程运行,服务器则作为子进程运行,第一个管道用于从客户端向服务器发送路径名,第二个管道用于从服务器向客户发送该文件的内容(或者一个出错消息)。



//提供一个双向数据流的两个管道
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include<sys/types.h>
#include <string.h>
#include <fcntl.h>
#include<sys/stat.h>
#include <errno.h>
#define MAXLEN 128
void client(int ,int);
void server(int,int);
int main()
{
    int pipe1[2],pipe2[2];
    pid_t pid;
    pipe(pipe1);
    pipe(pipe2);
    if ((pid=fork())==0) //子进程
    {
        close(pipe1[1]);
        close(pipe2[0]);
        server(pipe1[0], pipe2[1]);
        exit(1);
    }
    close(pipe1[0]);
    close(pipe2[1]);
    client(pipe2[0], pipe1[1]);
    waitpid(pid, NULL, 0); //等待子进程结束
    exit(1);
}
void client(int readfd,int writefd)
{
    size_t len;
    ssize_t n;
    char buf[MAXLEN];
    fgets(buf, MAXLEN, stdin); //输入文件路径
    len=strlen(buf);
    if (buf[len-1]=='\n')
        len--;
    write(writefd, buf, len);  //发送
    while ((n=read(readfd, buf, MAXLEN))>0) //接收文件内容
    {
        write(STDOUT_FILENO, buf, n);  //输出
    }
}
void server(int readfd,int writefd)
{
    int fd;
    ssize_t n;
    char buf[MAXLEN];
    if ((n=read(readfd, buf, MAXLEN))==0)  //读取客服端发过来的“文件路径”
    {
        write(STDERR_FILENO, "error", 5);
    }
    buf[n]=0;
    
    //以只读的方式打开文件
    if ((fd=open(buf, O_RDONLY))<0)
    {
        //打开失败,得到失败信息字符串存到 buf
        snprintf(buf+n, sizeof(buf)-n, "can't open,%s\n",strerror(errno));
        n=strlen(buf);
        //发到客户端
        write(writefd, buf, n);
    }
    else
    {
        //打开成功,循环读取文件内容
        while ((n=read(fd, buf, MAXLEN))>0)
        {
            write(writefd, buf, n);
        }
        close(fd);
    }
}
./main 运行后输入文件路径名即可

四、命名管道
在实际的应用中,进程间的通信往往发生在无亲缘关系的进程之间,这种情况下,如果仍然使用管道进行通信,就必须使用命名管道
也称为FIFO文件。
1.命名管道的创建
创建命名管道与普通文件类似。函数的一般形式如下:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
参数pathname为要创建的命名管道的全路径名;
参数mode为访问权限。
如果要创建的命名管道已经存在,mkfifo函数会返回EEXIST错误码,这时直接调用打开FIFO的函数就可以了。
示例(使用mkfifo创建一个命名管道):
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
int main()
{
    mode_t mode=0750;   //定义文件的访问权限
    int status;
    status=mkfifo("ztgreat", mode);  //创建命名管道
    if (status<0)
    {
        perror("mkfifo error");
        exit(0);
    }
    else
    {
        printf("fifo creat success\n");
    }
    
    // unlink("ztgreat");  //命名管道的删除 
    return 0;
}



  我们可以像删除一个普通文件那样用 rm 命令删除 FIFO 文件,或者也可以在程序中用 unlink 系统调用来删除它。
2.命名管道的打开
与匿名管道不同,在对命名管道进行读写操作之前,需要调用open函数打开命名管道。如果进程是为读取数据而打开命名管道时,同时已有相应进程为写入数据而打开该命名管道,则打开操作会成功返回。否则,如果打开操作设置了阻塞标志,进程会阻塞直到有相应进程为写入数据而打开该命名管道。同样,如果进程是为了写入数据而打开命名管道时,同时已有相应进程为读取数据而打开该命名管道,则打开操作会成功返回,否则,如果打开操作设置了阻塞标志,进程会阻塞直到有相应进程为读取数据而打开该命名管道。
3.命名管道的读写
从命名管道中读取数据时,对于设置了阻塞标志的读操作,如果当前命名管道中没有数据,而且没有进程为写入数据而打开该命名管道时,read函数会一直阻塞,直到有新的数据写入。对于没有设置阻塞标志的读操作,read函数的返回值为-1,同时将errno设置为EAGAIN,提醒用户以后再试。
向命名管道中写入数据时,对于设置了阻塞标志的写操作,如果要写入的字节数不大于PIPE_BUF( 它由 #define PIPE_BUF 定义,在头文件 limits.h ),Linux会保证写入的原子性,此时如果管道中的空闲区域不足以容纳要写入的数据,write函数会阻塞,知道管道中有足够的空闲区域,才开始进行一次性写操作,如果要写入的字节数大于PIPE_BUF,Linux将不再保证写入的原子性。此时管道中一旦有空闲区域,写进程就会试图往管道中写入数据,写入所有的数据后返回。
对于没有设置阻塞标志的写操作,如果要写入的字节数不大于PIPE_BUF,Linux会保证写入的原子性,此时如果管道中的空闲区域能够容纳要写入的数据,write函数写写入数据后会成功返回;如果管道中的空闲区域不足以容纳要写入的数据,则返回-1,同时将errno设置为EAGAIN,提醒用户以后再试,如果要写入的字节数大于PIPE_BUF,Linux将不再保证写入的原子性,写满管道中的空闲区域后即返回。
示例:编写两个非亲缘关系的进程,通过命名管道来实现它们间的通信。
发送消息进程:
//进程通信--命名管道(非亲缘关系的进程通信)发消息进程(阻塞方式)
//发消息进程(阻塞方式)
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string>
#define BUFSIZE 256
int main()
{
    int status;
    int fd;
    
    char buf[BUFSIZE];          //定义缓冲区
    status=mkfifo("zt", 0750);  //创建命名管道
    if (status<0)
    {
        perror("mkfifo error\n");
        exit(0);
    }
    fd=open("zt", O_WRONLY);   //打开命名管道,默认为阻塞方式
    if (fd<0)
    {
        perror("open error\n");
        exit(0);
    }
    printf("Server:\n");
    printf("input the message:");
    fgets(buf, sizeof(buf),stdin); //从键盘输入要发送的消息
    write(fd, buf, strlen(buf)-1); //将消息写入命名管道中
    printf("send!\n");
    unlink("zt");                  //删除命名管道
    return 0;
}
接收消息进程:
//进程通信--命名管道(非亲缘关系的进程通信)收消息进程(阻塞方式)
//收消息进程(阻塞方式)
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#define BUFSIZE 256
int main()
{
    int status;
    int fd;
    char buf[BUFSIZE];        //定义缓冲区
    fd=open("zt", O_RDONLY);  //打开命名管道,默认为阻塞方式
    if (fd<0)
    {
        perror("open error\n");
        exit(0);
    }
    printf("Clint:\n");
    read(fd, buf, sizeof(buf));  //读管道里面的数据
    printf("received message:%s\n",buf);
    return 0;
}



在上面的程序中,打开命名管道时都采用了默认的阻塞方式,下面将接收进程改为非阻塞方式:
//进程通信--命名管道(非亲缘关系的进程通信)收消息进程(非阻塞方式)
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#define BUFSIZE 256
int main()
{
    int fd;
    int num;
    char buf[BUFSIZE];
    fd=open("zt", O_RDONLY|O_NONBLOCK); //以非阻塞方式打开命名管道
    if (fd<0)
    {
        perror("open error\n");
        exit(0);
    }
    printf("Client:\n");
    while (1)
    {
        num=read(fd, buf, sizeof(buf));  //从命名管道读出数据
        if (num==-1)
        {
            if (errno==EAGAIN)
            {
                printf("NO data avlaible\n");
            }
        }
        else
        {
            printf("real read bytes:%d\n",num);  //输出实际读到的字节数
            printf("received message:%s",buf);
            break;
        }
        sleep(1);
    }
    
    return 0;
}



我们重新编写前面的客户-服务器程序,这次改变用两个FIFO代替两个管道,client和server函数保持不变,所有变动都在main函数上。



#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include<sys/types.h>
#include <string.h>
#include <fcntl.h>
#include<sys/stat.h>
#include <errno.h>
#define MAXLEN 128
#define FIFO1 "/tmp/fifo.1"
#define FIFO2 "/tmp/fifo.2"
#define FIFL_MODE S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH
void client(int ,int);
void server(int,int);
int main()
{
 
    int readfd,writefd;
    pid_t pid;
    if ((mkfifo(FIFO1, FIFL_MODE)) && errno!=EEXIST)
    {
        perror("can't create FIFO1");
    }
    if ((mkfifo(FIFO2, FIFL_MODE)) && errno!=EEXIST)
    {
        unlink(FIFO1);
        perror("can't create FIFO2");
    }
    if ((pid=fork())==0)
    {
        readfd=open(FIFO1, O_RDONLY,0);
        writefd=open(FIFO2, O_WRONLY,0);
        server(readfd, writefd);
        exit(0);
    }
    //注意下面两个open顺序
    writefd=open(FIFO1, O_WRONLY,0);
    readfd=open(FIFO2, O_RDONLY,0);
    
    client(readfd, writefd);
    waitpid(pid, NULL, 0);
    close(readfd);
    close(writefd);
    unlink(FIFO1);
    unlink(FIFO2);
    return 0;
}
void client(int readfd,int writefd)
{
    size_t len;
    ssize_t n;
    char buf[MAXLEN];
    fgets(buf, MAXLEN, stdin); //输入文件路径
    len=strlen(buf);
    if (buf[len-1]=='\n')
        len--;
    write(writefd, buf, len);  //发送
    while ((n=read(readfd, buf, MAXLEN))>0) //接收文件内容
    {
        write(STDOUT_FILENO, buf, n);  //输出
    }
}
void server(int readfd,int writefd)
{
    int fd;
    ssize_t n;
    char buf[MAXLEN];
    if ((n=read(readfd, buf, MAXLEN))==0)  //读取客服端发过来的“文件路径”
    {
        write(STDERR_FILENO, "error", 5);
    }
    buf[n]=0;
    
    //以只读的方式打开文件
    if ((fd=open(buf, O_RDONLY))<0)
    {
        //打开失败,得到失败信息字符串存到 buf
        snprintf(buf+n, sizeof(buf)-n, "can't open,%s\n",strerror(errno));
        n=strlen(buf);
        //发到客户端
        write(writefd, buf, n);
    }
    else
    {
        //打开成功,循环读取文件内容
        while ((n=read(fd, buf, MAXLEN))>0)
        {
            write(writefd, buf, n);
        }
        close(fd);
    }
}

(1)创建两个FIFO
在/tmp文件系统中创建两个FIFO,这两个事先存在与否无关紧要,常值:
#define FIFL_MODE S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH
这允许用户读,用户写,组成员读和其他用户读,这些权限为会被当前进程的文件模式创建掩码修正。
(2)fork
调用fork后,子进程调用我们的server函数,父进程调用我们的client函数,在执行这些调用前,父进程打开第一个FIFO来写,打开 第二FIFO来读,子进程打开第一个FIFO来读,打开第二个FIFO来读,这与我们的管道的例子类似。
创建一个管道只需调用pipe,创建并打开一个FIFO则需要调用mkfifo后再调用open。
管道在所有进程最终都关闭后自动消失,FIFO的名字则只有通过调用unlink才从文件系统中删除。
FIFO需要额外调用的好处是:FIFO在文件系统中有一个名字,该名字允许某个进程创建一个FIFO,与它无亲缘关系的另一个进程来打开这个FIFO,对于管道来说,这是不可能的。
注意:没有正确使用FIFO的程序会发生微妙的问题,如果我们交换父进程中两个open调用的顺序,该程序就不工作了,其原因在于,如果前面尚没有任何进程打开某个FIFO来写,那么打开该FIFO来读的进程将会阻塞,交换父进程中两个open调用的顺序后,父子进程将都打开FIFO来读,然后当时没有任何进程已打开FIFO来写,于是父子进程都阻塞,形成死锁了。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值