Linux学习之进程通信(有名管道与无名管道)

言之者无罪,闻之者足以戒。 ——《诗序》

ctrl+alt+t     打开终端

一、进程间通信和线程间通信:

进程通信:在用户空间实现进程通信是不可能的,通过Linux内核通信

线程通信:可以在用户空间就可以实现,可以通过全局变量通信

二、通信方式:

管道通信:无名管道、有名管道(文件系统中有名)

信号通信:信号(通知)通信包括:信号的发送、信号的接收和信号的处理

IPC(Inyer-Process Communication) 通信:共享内存、消息队列和信号灯。

以上是单机模式下的进程通信(只有一个Linux内核)

Socket通信:存在于一个网络中两个进程之间的通信(两个Linux内核)。

三、进程通信学习思路:

每一种通信方式都是基于文件IO的思想

open:功能:创建或打开进程通信对象。函数的形式不一样,有的是多个函数完成

write:功能:向进程通信对象中写入内容。函数形式可能不一样

read:功能:从进程通信对象中读取内容。函数形式可能不一样

close:功能:关闭或删除进程通信对象。形式可能不一样

四、无名管道:

通信原理:

管道文件是一个特殊的文件,是由队列来实现的。(队列一端入队,一端出队)

在文件IO中创建一个文件或打开一个文件是由open函数来实现的,但open函数不能创建管道文件

只能用pipe函数来创建管道。

函数形式:int pipe(int fd[2])

功能:创建管道,为系统调用:unistd.h

参数:就是得到的文件描述符。可见有两个文件描述符:fd[0]和fd[1],管道有一个读端fd[0]用来读和一个写端fd[1]用来写这个规矩不能改变

返回值:成功是0,出错是-1

注意:

管道中的东西,读完了就删除了,就像队列中的出对一样

如果管道中没有东西可读,则会出现读阻塞

如果管道已经被写满,还要写东西就会出现写阻塞

下面写一个程序实现:

(1)创建一个管道

(2)向管道中写数据

(3)读取管道中的数据

(4)验证管道的读阻塞

程序如下:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
        int fd[2];
        int ret;
        char writebuf[]="hello linux";
        char readbuf[128]={0};
        ret=pipe(fd);
        if(ret < 0)
        {
                printf("creat pipe failure\n");
                return -1;
        }
        printf("creat pipe sucess fd[0]=%d,fd[1]=%d\n",fd[0],fd[1]);

        write(fd[1],writebuf,sizeof(writebuf));

        read(fd[0],readbuf,128);
        printf("readbuf=%s\n",readbuf);
        //validate read block
        memset(readbuf,0,128);//clear readbuf

        read(fd[0],readbuf,128);
        printf("read again sucess\n");

        close(fd[0]);
        close(fd[1]);
        return 0;
}

命令:ps -axj 可以查看进程的状态

下面写一个程序实现:

(1)创建一个管道

(2)向管道中写数据

(3)验证写阻塞

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
        int fd[2];
        int ret;
        int i=0;
        char writebuf[]="hello linux";
        char readbuf[128]={0};
        ret=pipe(fd);
        if(ret < 0)
        {
                printf("creat pipe failure\n");
                return -1;
        }
        printf("creat pipe sucess fd[0]=%d,fd[1]=%d\n",fd[0],fd[1]);
        while(i < 5457)
        {
        write(fd[1],writebuf,sizeof(writebuf));
        i++;
        }
        printf("write is sucess\n");

        close(fd[0]);
        close(fd[1]);
        return 0;
}

通过程序的验证:我们发现了阻塞空间的大小,当i<=5456时不阻塞,当i>=5457时阻塞

无名管道实现进程通信:

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
int main()
{
        pid_t pid;
        int fd[2];
        int ret;
        char process_inter=0;
        ret=pipe(fd);
        if(ret < 0)
        {
                printf("creat pipe failure\n");
                return -1;
        }
        printf("creat pipe sucess\n");
        pid = fork();
        if(pid==0)
        {
                int i=0;
                read(fd[0],&process_inter,1);
                while(process_inter==0);
                for(i=0;i<5;i++)
                {
                        printf("this is child process i=%d\n",i);
                        usleep(100);
                }
        }
        if(pid>0)
        {
                int i=0;
                for(i=0;i<5;i++)
                {
                        printf("this is parent process i=%d\n",i);
                        usleep(100);
                }
                process_inter=1;
                sleep(5);
                write(fd[1],&process_inter,1);
        }
        while(1);
        return 0;
}

无名管道的缺点:只能实现父子进程(有亲缘关系进程)之间的通信。

fork()函数:创建父子进程,父进程为1,子进程为0

五、有名管道:

正是由于无名管道的这一缺点,我们对无名管道进行改进:有名管道

所谓的有名,即文件系统中存在这样一个文件节点,每一个文件都有一个inode号,而且这是一个特殊的文件类型:p管道类型

1、创建这个文件节点,不可以通过open函数,open函数只能创建普通文件,不能创建特殊文件(管道-mkfifo、套接字-socket、字符设备文件-mknod、块设备文件-mknod、符号链接文件-ln -s、目录文件mkdir)

2、管道文件只有inode号,和套接字、字符设备文件、块设备文件一样都不占用磁盘空间。普通文件和符号文件以及目录文件,不仅有inode 号,而且还占用磁盘空间。

3、mkfifo  用来创建管道文件的节点,没有在内核中创建管道,只是通过open函数打开这个文件时才会在内核空间创建管道

4、mkfifo

函数形式:int mkfifo(const *filename , mode_t mode)

功能:创建管道文件

参数:管道文件名,权限,创建的文件权限仍然和umask有关系

返回值:创建成功返回0,创建失败返回-1

下面看一下mkfifo的用法:

include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
        int ret;
        ret=mkfifo("./myfifo",0777);
        if(ret < 0)
        {
                printf("creat myfifo failure\n");
                return -1;
        }
        printf("creat myfifo sucess\n");
        return 0;
}

下面我们就通过有名管道实现无亲缘关系进程间的通信:

(1)第一步:创建有名管道:

include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
        int ret;
        ret=mkfifo("./myfifo",0777);
        if(ret < 0)
        {
                printf("creat myfifo failure\n");
                return -1;
        }
        printf("creat myfifo sucess\n");
        return 0;
}

(2)第二步:创建第一个进程:

include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <fcntl.h>
int main()
{
        int fd;
        int i;
        char process_inter1=0;
        fd=open("./myfifo",O_WRONLY);
        if(fd < 0)
        {
                printf("open myfifo failure\n");
                return -1;
        }
        printf("open myfifo sucess\n");
        for(i=0;i<5;i++)
        {
                printf("this is first process i=%d\n",i);
                usleep(100);
        }
        process_inter1=1;
        sleep(5);
        write(fd,&process_inter1,1);
        while(1);
        return 0;
}

(3)第三步:创建第二个进程:
 

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <fcntl.h>
int main()
{
        int fd;
        int i;
        char process_inter2=0;
        fd=open("./myfifo",O_RDONLY);
        if(fd < 0)
        {
                printf("open myfifo failure\n");
                return -1;
        }
        printf("open myfifo sucess\n");
        read(fd,&process_inter2,1);
        while(process_inter2==0);
        for(i=0;i<5;i++)
        {
                printf("this is second process i=%d\n",i);
                usleep(100);
        }
        while(1);
        return 0;
}

(4)第四步:首先编译运行第一个进程,紧接着编译运行第二个进程;当我们运行第一个进程之后不会看到打印信息,那是因为只有读写两端都被启动,进程才会被启动,当我们运行第二个进程之后就可以看到第一个进程的打印信息,随后会看到第二个进程的打印信息(用两个终端运行两个进程)。

 

猜你喜欢

转载自blog.csdn.net/weixin_42994525/article/details/83036047