Linux:进程间通信——匿名管道

Linux:进程间通信——匿名管道

  • 进程间通信
  • 匿名管道
    • 匿名管道的创建
    • 管道的读写规则

进程间通信

因为每一个进程都拥有自己的独立的进程虚拟地址空间,导致了进程的独立性。进程间的通信技术是为了让各个进程之间可以很好的交换数据或者进程控制而产生的。
常用的进程间通信的方式有管道、共享内存、消息队列和信号量。

匿名管道

管道有分为匿名管道和命名管道两种,接下来主要介绍管道通信中的匿名管道。匿名管道就是指在内核中创建出来的一块没有标识符的一段缓冲区。

匿名管道的创建

在Linux系统中,可以用pipe函数来创建匿名管道
函数原型:

#include 
int pipe(int fd[2]);
参数解释:
	fd[2]:
	(1)本质上是一个出参,fd[0],fd[1]当中的值是pipe函数赋予的
	(2)fd[2]数组中有两个元素,保存的都是文件描述符,fd[0]表示写端,fd[1]表示读端
	返回值:
		成功返回0,失败返回-1

例子:

 #include                                                   
 #include 
 #include 
 #include 

 int main()
 {
   int fd[2];
   char buf[100];
   int len;
   int pipeid = pipe(fd);
 
   if(pipeid < 0)
   {

     perror("pipe");
     return -1;
   }
 
   while(fgets(buf,100,stdin))
   {
     len = strlen(buf);
     if(write(fd[1],buf,len) != len)
     {
      perror("write to pipe!\n");
      break;
     }
     memset(buf,0x00,sizeof(buf));
 
     if((len = read(fd[0],buf,100)) == -1 )
     {
       perror("read from pipe!\n");
       break;
     }
     if(write(1,buf,len) != len)
     {
       perror("writer to stfout!\n");
       break;                                                        
     }
   }
 }

Linux:进程间通信——匿名管道_第1张图片
管道是半双工通信,这就意味着数据流向只能是从写端流向读端Linux:进程间通信——匿名管道_第2张图片
查看进程ID
在这里插入图片描述
用ll /proc/进程号/fd命令查看
Linux:进程间通信——匿名管道_第3张图片
关闭进程,再次查看,发现找不到该目录
在这里插入图片描述
说明管道的生命周期是跟随进程的。
匿名管道是没有标识符的,就像一个人没有名字一样,我们也不认识他,所以在茫茫人海中,我们就无法通过名字找到这个人,这也就意味着进程无法通过标识符来找到这个管道,但是进程之间又要进行通信,这时候我们就联想到子进程的创建了,子进程创建的原理是复制父进程的PCB,代码共享数据独享,因此我们可以通过创建子进程的方法来找到这个管道实现进程间的通信。(注意:一定要先创建管道在创建子进程)
Linux:进程间通信——匿名管道_第4张图片例子:

 #include                                                   
 #include 
 #include 
 #include 
 
 int main()
 {
   int fd[2];
   int ret = pipe(fd);
   char buf[1024] = {0};
   if(ret < 0)
   {
     perror("pipe");
     return -1;
   }
   pid_t pid = fork();
   if(pid < 0)
   {
     perror("fork");
     return -1;
   }
   else if(0 == pid)
   {
       ssize_t ret = read(fd[0],buf,sizeof(buf) - 1);
       if(ret < 0)
       {
         perror("read");
       }
       printf("i am child ,buf is:%s\n",buf);
       sleep(1);
   }
   else 
    {
     printf("i am fsther,I will write:");
     fgets(buf,100,stdin);
     int  len = strlen(buf);
     if(write(fd[1],buf,len) != len)
     {
       perror("write");
       sleep(1);                                                     
   }
   return 0;
 }

管道的读写规则

1.进程操作写端往匿名管道当中写的时候,如果写入的字节数量小于4k,则保证当前写入操作的原子性
首先我们先用代码验证一下管道能存储多大的数据:
代码:

#include 
#include 

int main()
{
    int fd[2];
    int ret = pipe(fd);
    if(ret < 0)
    {
        perror("pipe");
        return -1;
    }

    int count = 0;
    while(1)
    {
        write(fd[1], "h", 1);
        count++;
        printf("count:%d\n",count);
    }
    return 0;
}

运行结果:
Linux:进程间通信——匿名管道_第5张图片
可以看到管道能存储的最大数据为65536字节,也就是6K,其实中间还有一个关键值于PIPE_BUF(4K),当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性,就有可能会出现如下情况
Linux:进程间通信——匿名管道_第6张图片
数据中途配打断,失去了原有的含义,被破坏。

2.将写端文件描述符设置成非阻塞属性,读端文件描述符不管
(1)读端文件描述符关闭,调用writer的进程会收到管道破裂的信号SIGPIPE,导致当前调用writer的进程会退出掉(如果是子进程调用writer,可能会产生僵尸进程,如果是父进程会产生孤儿进程)
Linux:进程间通信——匿名管道_第7张图片
代码:

#include 
#include 
#include 
#include 

int main()
{
    int fd[2];
    int ret = pipe(fd);
    if(ret < 0)
    {
        perror("pipe");
        return -1;
    }

    int flag = fcntl(fd[1], F_GETFL);
    fcntl(fd[1], F_SETFL, flag | O_NONBLOCK);
    int pid  = fork();
    if(pid < 0)
    {
        perror("fork");
        return -1;
    }
    else if(pid == 0)
    {
        close(fd[0]);
        int count = 0;
        while(1)
        {
            ssize_t ret = write(fd[1], "l", 1);
            printf("ret : %d\n", ret);
            if(w_size <= 0)
            {
                perror("write");
                break;
            }
            count++;
            printf("write : %d\n", count);
        }
    }
    else
    {
        //father
        close(fd[0]);
        while(1)
        {
            printf("i am father!\n");
            sleep(1);
        }
    }
    return 0;
}

Linux:进程间通信——匿名管道_第8张图片
(2)读端文件描述符不关闭,但是也不读,写端一直写,直到将管道写满,则再次调用writer函数,会返回-1,报错:资源不可用
Linux:进程间通信——匿名管道_第9张图片
代码:

#include 
#include 
#include 
#include 

int main()
{
    int fd[2];
    int ret = pipe(fd);
    if(ret < 0)
    {
        perror("pipe");
        return -1;
    }
    
    int flag = fcntl(fd[1], F_GETFL);
    fcntl(fd[1], F_SETFL, flag | O_NONBLOCK);

    int pid  = fork();
    if(pid < 0)
    {
        perror("fork");
        return -1;
    }
    else if(pid == 0)
    {
        while(1)
        {
        int count = 0;
        while(1)
        {
            ssize_t ret = write(fd[1], "l", 1);
            printf("ret : %d\n", ret);
            if(write <= 0)
            {
                perror("write");
                break;
            }
            count++;
            printf("write : %d\n", count);
        }
    }
    else
    {
        while(1)
        {
            printf("i am father!\n");
            sleep(1);
        }
return 0}

运行结果:
Linux:进程间通信——匿名管道_第10张图片
3.将读端文件描述符设置成非阻塞属性,写端文件描述符不管
(1)写端文件描述符关闭,读端一直读,直到将管道的内容读完,再次调用read函数,返回0,表示没有读到任何内容
Linux:进程间通信——匿名管道_第11张图片
代码:

#include 
#include 
#include 
#include 

int main()
{
    int fd[2];
    int ret = pipe(fd);
    if(ret < 0)
    {
        perror("pipe");
        return -1;
    }

    int flag = fcntl(fd[0], F_GETFL);
    fcntl(fd[0], F_SETFL, flag | O_NONBLOCK);
    int pid  = fork();
    if(pid < 0)
    {
        perror("fork");
        return -1;
    }
    else if(pid == 0)
    {
        //child
        close(fd[1]);
        while(1)
        {
            char buf[1024] = {0};
            ssize_t ret = read(fd[0], buf, sizeof(buf) - 1);
            printf("ret = %d\n", ret);
            if(ret < 0)
            {
                perror("read");
                break;
            }
            sleep(1);
        }
    }
    else
    {
        close(fd[1]);
        while(1)
        {
            printf("i am father\n");
            sleep(1);
        }
    }
    return 0;
}

Linux:进程间通信——匿名管道_第12张图片
(2)写端文件描述符不关闭,但是也不写,读端调用read函数,则read函会返回-1,报错:资源不可用
Linux:进程间通信——匿名管道_第13张图片
代码:

#include 
#include 
#include 
#include 

int main()
{
    int fd[2];
    int ret = pipe(fd);
    if(ret < 0)
    {
        perror("pipe");
        return -1;
    }

    int flag = fcntl(fd[0], F_GETFL);
    fcntl(fd[0], F_SETFL, flag | O_NONBLOCK);
    int pid  = fork();
    if(pid < 0)
    {
        perror("fork");
        return -1;
    }
    else if(pid == 0)
    {
        //child
        //close(fd[1]);
        while(1)
        {
            char buf[1024] = {0};
            ssize_t ret = read(fd[0], buf, sizeof(buf) - 1);
            printf("ret = %d\n", ret);
            if(ret < 0)
            {
                perror("read");
                break;
            }
            sleep(1);
        }
    }
    else
    {
        //close(fd[1]);
        while(1)
        {
            printf("i am father\n");
            sleep(1);
        }
    }
    return 0;
}

Linux:进程间通信——匿名管道_第14张图片
第一次尝试用动态图,有的可能不太正确,望各位大佬指正。

你可能感兴趣的:(Linux,linux)