Linux学习笔记之六(管道通信)

目录

  • 1、管道通信
    • 1.1、无名管道
    • 1.1、有名管道

1、管道通信

管道通信是一个设备中进程与进程之间通信的一种方式,分为无名管道和有名管道两种。前者只能用于有亲缘关系的进程之间的通信,如父子进程等,后者则没有亲缘限制。此外,管道通信从根本上还是通过内核的一种半双工的通信方式。

1.1、无名管道

无名管道又称匿名管道,即通信双方无需知道对方的pid号,仅通过一些管道提供的描述符便可往管道缓冲区读写数据。在具体使用上,管道应比子进程更先被创建,因为而后被创建的子进程会拷贝父进程的地址空间,从而保证父子进程使用的是同一条管道。当然,管道通信并不限制进程数量。
pipe( )是创建无名管道的一个重要函数,我们可以查看其函数说明:

man 2 pipe

Linux学习笔记之六(管道通信)_第1张图片

  • int pipefd[2]:该数组是用于存放从pipe()取出的管道描述符。其中,pipefd[0]是读取数据的描述符,pipefd[1]是写入数据的描述符。
  • int flags:不常用,不说。
  • 返回值:成功返回0,反之返回-1。

再看个例子便知道怎么用了:
该例子完成的任务:父进程从终端中读取用户输入的字符并写入管道,然后子进程从管道中读取数据之后将字符一个个打印到终端中来。
注意的点:管道读取是一种阻塞式读取,即如果某一进程想从本没有数据的管道中读取数据,就会一直阻塞在那里,等到有数据进入管道缓冲区。并且这种阻塞只发生在最开始的时候,如果中途管道没有数据了,它还是可以正常读取,只不过返回值是0.

#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
        int pipe_fd[2];
        char buf;
        if(argc != 2)
        {
                printf("Usage :%s \n", argv[0]);
                return -1
        }    
        pipe(pipe_fd);			//create a pipe
        pid_t cpid = fork();	//create a child process	
        if(cpid == 0)           //enter the child process
        {
                close(pipe_fd[1]);
                while(read(pipe_fd[0], &buf, 1)) 
                        write(1, &buf, 1);
                printf("\n");
                close(pipe_fd[0]);
                _exit(0);       //recommend to use the _exit() to exit in the child process
        }
        else                    //enter the parent process
        {
                close(pipe_fd[0]);
                write(pipe_fd[1], argv[1], strlen(argv[1]));
                close(pipe_fd[1]);
                wait(NULL);     //waiting for his child to  exit
                exit(0);
        }
        return 0;
}

1.1、有名管道

有名管道相较于无名管道,可以用于无亲缘关系进程之间的通信。并且它的逻辑也更为清晰,使用的时候,我们可以在文件夹中创造一个管道文件,然后其他进程便可往这个管道文件读写数据,最终实现进程通信的目的。
下面看看具体操作步骤:
第一步,在文件夹中创建一个管道文件(以test_pipe为例)。

mkfifo test_pipe

Linux学习笔记之六(管道通信)_第2张图片
除了在终端中创建一个FIFO文件夹,我们还可以在程序中创建文件夹。同样是用mkfifo函数,参考

man 3 mkfifo

第二步,创建一个进程往test_pipe写入数据。

#include 
#include 
#include 
#include 
#include 

int main()
{
    int fd = open("./test_pipe", O_WRONLY);		//open the pipe file
    write(fd, "hello", 5);						//write something into the file
    close(fd);									//close the file, don't forget!
    
    return 0;
}

第三步,创建一个新的进程往test_pipe写入数据。

#include 
#include 
#include 
#include 
#include 

int main()
{  
    int fd = open("./test_pipe", O_WRONLY); 
    if(fd == -1)
    {
        printf("an error occured opening the pipe file\n");
        return -1;
    }
    while(1)
    {
        write(fd, "hello!", 6);
        printf("writing successfully!\n");
        sleep(1);
    }

    close(fd);
    return 0;
}

第四步,创建一个新的进程从test_pipe读出数据。注意在读取操作的时候要考虑读取延时问题,y因为如果读的速度比写的速度还慢,那可能会读到的不仅仅是这一次写入的数据,还可以能拼接一些上一次的数据。

#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
    int fd = open("./test_pipe", O_RDONLY);
    char buf[6];
    while(1)
    {
        //sleep(1);
        read(fd, buf, 6);
        printf("what i read is: %s\n", buf);
        memset(buf, 0 ,sizeof(buf));			//clear the buf       
    }
    
    close(fd);
    return 0;
}

通过以上步骤便可以实现两个没有亲缘关系之间的通信了。但在我学习过程中,还遇到很多问题,这也一一列举出来吧。
第一,有名管道的通信是读写阻塞的,但它阻塞不是阻塞在read和write函数上,而是阻塞在open函数上。这似乎意味着,一名管道的通信始于两个进程同时访问该管道。
第二,但写进程终止之后,读进程还是可以继续运行,只是读出的数据是0而已。但读进程终止,写进程就无法继续了。个人感觉问题出现在write函数上,即write函数检测到没有进程读数据,它就会停止,我写了程序验证了一下也大抵如此。

你可能感兴趣的:(Linux,linux,学习,笔记)