进程间通信--匿名管道

进程通信的目的

  1. 数据传输:进程间需要数据的传输
  2. 资源共享:两个或多个进程共享相同资源
  3. 通知事件:当事件发生时需要通知另一个进程
  4. 进程控制:一个进程需要控制另一个进程运行时,如调试程序

进程间通信的方式

管道:匿名管道和有名管道 
system V IPC: system 共享内存,system 消息队列,system 信号量
posix IPC:消息队列,共享内存,信号量,信号量集,条件变量, 读写锁

匿名管道

匿名管道特性:
匿名管道是一个伪文件,实质上是一在Linux内核借助内核缓冲区,使用环形队列实现。
匿名管道由两个文件描述符引用,返回一个文件描述符数组fd[2],其中fd[0] 为读端,fd[1]为写端。
匿名管道是半双工通信,管道中数据只能从一个进程流向另一个进程 。
匿名管道中数据遵从先入先出,并且读取后管道中数据会消失。
匿名管道所传输的是无格式的字节流。这就需要管道输入方和输出方事先约定好数据格式。
匿名管道的大小有限制。
匿名管道因为没有名字,需要通过文件描述符进行操作,因为只有具有亲缘关系的进程拷贝文件描述符表进行操作,因此只能在具有亲缘关系进程间通信(如父子进程,兄弟进程进程)
pipe函数,创建一个匿名管道
函数原形:int pipe(int fd[2]);
参数:fd: 文件描述符数组,其中fd[0]为读端,fd[1]为写端。和流的描述符一直0为读1为写
返回值:成功返回0,失败返回错误码
pipe管道读写特性:
当管道设置为阻塞属性时(默认为阻塞)
当写端文件描述符没有关闭,read 函数进行读操作时,如果管道有数据,将返回实际读取的字节数,当管道没有数据时, read函数将被阻塞,直到管道中被写入数据进行读取返回,
当所有写端文件描述符被关闭,read 函数进行读操作时,如果管道有数据,将返回实际读取的字节数,当管道没有数据时,read函数将返回0
当读端文件描述符没有关闭,当write进行写操作时,如果管道没有被写满就进行写入并返回实际写入字节数,如果管道已经被写满write将被阻塞,直到管道数据被读出有空间,才会写入数据返回写入的字节数。
当所有读端文件描述符被关闭,当write进行写操作时,会收到SIGPIPE信号
当管道设置为非阻塞属性时
当写端文件符没有关闭时,read函数进行读操作时,如果管道有数据,将返回实际读取的字节数,管道无数据时直接返回
当写端关闭时同阻塞
当读端没有关闭时,write进行写操作时,如果管道有空间将进行写入返回写入字节数,当管道满时,
当所有读端都关闭时,write进行写操作时收到SIGPIPE信号
设置非阻塞io方法:fcntl(fd, F_SETFL, O_NONBLOCK);

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

int main()
{
    int fd[2];
    if(pipe(fd) < 0)
    {   
        perror("pipe error");
    }
    //管道必须在创建子进程之前进行创建,只有这样才能在创建子进程时
    //拷贝父进程文件描述符表,使得父子进程都拥有两个分别指向读写的描述符
    //并且指向同一管道
    pid_t pid = fork();
    if(pid < 0)
    {   
        perror("creat child process failure");
    }
    else if(pid == 0)
    {   
        printf("this is read process\n");
        close(fd[1]);

        char buff[1024] = { 0 };
        int ret = read(fd[0], buff, 1023);
        printf("ret:%d\nbuff:%s\n", ret, buff);

      //memset(buff, 0, 1024);
      //ret = read(fd[0], buff, 1023); 
      //printf("ret:%d\nbuff:%s\n", ret, buff);

        close(fd[0]);
        printf("read exit\n");
        _Exit(1);
    }
    else if(pid > 0)
    {
        pid_t pid2 = fork();
        if(pid2 == 0)
        {
            printf("this is write process\n");
            close(fd[0]);

            write(fd[1], "hello!!", 7);
            sleep(2);
            printf("write\n");
            write(fd[1], "world", 5);

            close(fd[1]);
            printf("write exit\n");
            _Exit(0);
        }else
        {
            //父进程进行检查子进程状态
            int status;
            close(fd[0]);
            close(fd[1]);
            printf("this is parent\n");

            while(1)
            {
                int ret = waitpid(-1, &status, WNOHANG);
                if(ret < 0)
                {
                    break;
                }
                else if(ret > 0)
                {
                    if(status && 0xFF)
                    {
                        printf("sig code:%d\n", status & 0x7F);
                    }
                }
            }
        }
    }
    return 0;
}

上面程序建立两个子进程(一个写进程一个读进程)之后关闭读端写端,写进程写入管道数据,读进程读取管道数据之后关闭读端,这时写进程进行写入数据时会收到SIGPIPE信号终止进程。

猜你喜欢

转载自blog.csdn.net/Jocker_D/article/details/83830275