Linux系统编程之进程间的通信方式(无名管道与命名管道)

进程间的五种通信方式(IPC):
1、管道(无名管道),速度慢,容量有限,只有父子进程能通讯;2、FIFO(命名管道),任何进程间都能通讯,但速度慢;3、消息队列,容量受到系统限制;4、信号量,不能传递复杂消息,只能用来同步;5、共享内存区。

一、管道:
管道:通常指无名管道,是UNIX系统中IPC最古老的形式。
1.特点:
它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

2.原型:

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

当一个管道被创建时,它会伴随着创建两个文件描述符,fd[0]为读而打开,fd[1]为写而打开。
如下图所示:
在这里插入图片描述
想关闭管道只要将两个文件描述符关闭即可。

3.用例说明:
单个进程中的管道几乎是没意义的,所以在调用pipe创建管道的时候,接着就调用函数fork()创建子进程,这样就实现了父进程与子进程之间的IPC方式。
(1)在使用fork()之后的半双工通信管道
在这里插入图片描述
(2)简单的从父进程到子进程之间的通信管道
在这里插入图片描述
直接上代码:

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

int main()
{
    
    
        //int pipe(int pipefd[2]);

        int fd[2];
        int pid;
        char *wbuf = "hello word!";
        char *rbuf = NULL;

        if(pipe(fd) == -1)
        {
    
    
                printf("creat pipe error!\n");
        }

        pid = fork();

        if(pid < 0)
        {
    
    
                printf("creat Process error!\n");
        }
        else if(pid > 0)
        {
    
    
                sleep(3);
                printf("this is father process!\n");
                close(fd[0]);
                write(fd[1],wbuf,strlen(wbuf));
                wait();
        }
        else
        {
    
    
                printf("this is child process!\n");
                close(fd[1]);
                rbuf = (char *)malloc(sizeof(char)*strlen(wbuf));
                read(fd[0],rbuf,strlen(wbuf));
                printf("read from father:%s\n",rbuf);
                exit(0);
        }

        return 0;
}              

结果:
在这里插入图片描述
按道理来说应该是父进程先执行的,但是在父进程那里执行了睡眠三秒,所以到子进程下面执行到read的时候发生了阻塞,直到父进程醒来写了东西进管道,子进程的read才继续执行。

二、FIFO(命名管道)
FIFO,也叫做命名管道,它是一种文件类型。
特点:

1.FIFO可以在无关进程之间交换数据,与无名管道不同。
2.FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

FIFO原型:

 #include <sys/types.h>
 #include <sys/stat.h>

 int mkfifo(const char *pathname, mode_t mode);

返回值:

成功返回0,失败返回-1

温馨提示:如果文件已经存在即(EEXIST),这时候再重新创建文件的话也会是提示失败的。

其中的 mode 参数与 open 函数中的 mode 相同。一旦创建了一个 FIFO,就可以用一般的文件 I/O 函数操作它。

当 open 一个 FIFO 时,是否设置非阻塞标志(O_NONBLOCK)的区别:

其中的 mode 参数与 open 函数中的 mode 相同。一旦创建了一个 FIFO,就可以用一般的文件 I/O 函数操作它。

当 open 一个 FIFO 时,是否设置非阻塞标志(O_NONBLOCK)的区别:

若没有指定 O_NONBLOCK(默认),只读 open 要阻塞到某个其他进程为写而打开此 FIFO。类似的,只写 open 要阻塞到某个其他进程为读而打开它。
若指定了 O_NONBLOCK,则只读 open 立即返回。而只写 open 将出错返回 -1 如果没有进程已经为读而打开该 FIFO,其 errno 置 ENXIO。

上代码:
demo2.c

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>

int main()
{
    
    
        int ret;

        ret = mkfifo("./file1",0600);
        if(ret == 0)
        {
    
    
                printf("mkfifo pipe success!\n");
        }else
        {
    
    
                printf("mkfifo pipe failed!\n");
                perror("why");
        }

        return 0;
}

这样写的话当管道存在时,也会提示创建管道失败。

以下这样写就可以有效的避免打印多余的信息。
demo3.c

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>

int main()
{
    
    
        int ret;

        ret = mkfifo("./file1",0600);

        if(ret == -1 && errno != EEXIST)
        {
    
    
                printf("mkfifo pipe failed!\n");
                perror("why");
        }else
        {
    
    
                printf("mkfifo pipe success!\n");
                perror("why");
        }

        return 0;
}

上面两部分的代码只是FIFO管道的创建的部分,真正的读端跟写端在以下的代码里体现:
read.c

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main()
{
    
    
        int ret;
        int fd;
        int n_read;
        char *readBuf = NULL;

        ret = mkfifo("./file1",0600);

        if(ret == -1 && errno != EEXIST)
        {
    
    
                printf("Sorry,creat pipe error!\n");
        }
        else
        {
    
    
                printf("Congratulation,You creat pipe success!\n");
        }

        fd = open("./file1",O_RDONLY);

        while(1){
    
    
          readBuf =(char *)malloc(sizeof(char)*128);
          n_read = read(fd,readBuf,128);
          sleep(1);
          printf("read from the write %d byte,the content is:%s\n",n_read,readBuf);
        }

        close(fd);
        return 0;
}                                                  

以上代码是读端代码,在while循环里表示每隔一秒读一次,此时的读端open是以O_RDONLY只读的方式打开。这时候没有指定 O_NONBLOCK即不阻塞操作,所以当没有写端写入时,这时候读端到read时会被执行阻塞直到有写端的执行。

write.c

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main()
{
    
    
        int ret;
        int cnt = 0;
        int fd;
        int n_write;
        char *writeBuf = "hello  I Love You";

        ret = mkfifo("./file1",0600);

        if(ret == -1 && errno != EEXIST)
        {
    
    
                printf("Sorry,creat pipe error!\n");
        }
        else
        {
    
    
                printf("Congratulation,You creat pipe success!\n");
        }

        fd = open("./file1",O_RDWR);
        while(1){
    
    
          n_write = write(fd,writeBuf,strlen(writeBuf));
         printf("write %d byte to the file1,the content is:%s\n",n_write,writeBuf);
          sleep(1);
          cnt++;
          if(cnt == 3)
          {
    
    
             break;
          }
       }

        close(fd);
        return 0;
}

这时候的读端的意思是每隔一秒写一次,直到写三次后退出循环,这时候的open是以只写的方式O_WRONLY打开,这时候没有指定 O_NONBLOCK即不阻塞操作,所以当没有读端写入时,这时候写端写到write时会被执行阻塞直到有读端的执行。

学习笔记,仅供参考!

猜你喜欢

转载自blog.csdn.net/weixin_51976284/article/details/124578190