Linux 进程间通信

1.进程间通信

进程通信可以实现:数据传输、资源共享、通知事件、进程控制

进程具有独立性,如何通信?

前提:先得让不同进程看到同一份资源

  • 1.同一份资源:某种形式内存空间
  • 2.提供资源的:只能是操作系统

本地通信------同一个主机,同一个OS,不同进程之间的通信------1.syetem V 2.管道 3.POSIX标准

网络通信------不同主机之间通信

1.1进程间通信的分类

管道(古老、经典的通信方式)

  • 匿名
  • 命名

system V

  • 消息队列
  • 共享内存
  • 信号量

2.管道

管道式Unix中最古老的进程间通信的方式

我们把从一个进程连接到另一个进程的一个数据流称为一个管道

2.1匿名管道

IPC(Inter - Process Communication)本质:先让不同进程,看到同一份资源

那么要由OS来创建这个资源,可不可以实现一个纯内存级别的文件------>管道

匿名管道原理:先以读写方式打开,在由子进程继承管道,父子进程各关闭其中一个文件描述符

2.1.1一个管道只能进行单向通信

a.管道打开没有关闭? fd泄露 && 误操作

b.为什么要rw同时打开?先创建管道,再创建子进程(利用子进程继承的属性)

c.为什么叫做管道?只能单向通信?因为最开始只需求单向通信,不用设计额外数据结构,因为特性故命名为管道

b.匿名管道不需要路径,不需要名字

2.1.2系统调用接口

cpp 复制代码
//pipefd为输出型参数,pipefd[0]为读,pipefd[1]为写
//成功:返回0;失败:返回-1,失败码设置

重定向完整写法

bash 复制代码
./mypipe 2>log.txt
./mypipe 1>log.txt 2>log.txt		#这样可以分离出stdout 和 stderror

2.1.3管道保护共享资源

IPC本质:先让不同进程,看到同一份资源!------>共享资源------>数据不一致问题------>保护共享资源(临界资源)------>管道内部自己做了

所以,father在读 管道,管道没有数据的时候会阻塞

匿名管道有大小上限 Ubuntu------>64KB

现象:

1.管道正常 && 管道阻塞 ,read会阻塞[read是一个系统调用]

2.管道为满 && 管道正常,write会阻塞[write是一个系统调用]

(管道满时不会覆盖,覆盖存在信息不一致问题)

3.管道读写端关闭 && 读端读到0(返回值为0),标识读到文件结尾。

4.管道写端正常 && 读端关闭,OS会直接杀掉写入进程

(OS不会做浪费资源的事;OS会给目标进程发送信号:13)SIGPIPE;怎么证明:父进程处理僵尸进程)

2.1.4匿名管道特性

1.面向字节流

2.用来进行有血缘关系的进程,进行IPC,常用于父子

3.文件生命周期,随进程!管道也是!

4.单向数据通信(双向通信需开两个管道)

5.管道自带同步、互斥等保护机制!(对共享资源)

2.1.5管道通信的场景------进程池

1.什么是任务------任务码int

2.怎么派发任务------任务量差不多,负载均衡

  • a.轮询
  • b.随机
  • c.根据历史任务数
    3.怎么停止子进程,关闭对应信道写的管道,这样读端会自动关闭。

2.2命名管道

1.命名管道是真实存在的文件

文件属性为p

p:只使用内核文件缓冲区,不做刷新

为什么真实存在文件?为了命名

2.命名管道的原理的理解(匿名打通)

为什么叫命名管道?真正存在的文件------>路径+文件名(具有唯一性)------>先让不同进程,看到同一份资源------>让不同进程用同一个文件系统路径标志同一个资源

2.2.1系统调用

cpp 复制代码
//创建管道本质也是新建文件,mode是文件的权限
//1.文件会记录用户UID
//2.进程的task_struct->UID


使用mkfifo可以创建一个管道,unlink可以删除一个管道,unlink也是一条bash命令

若读端打开文件时,写端还没打开,读端对用的open就会阻塞

若写段打开文件时,读端还没打开,写端在open也会阻塞

3.System V

3.1System V共享内存

1.开辟空间

2、3:将共享内存挂接到进程地址空间中

3.1.1系统调用

共享内存在任何时刻,可以在OS内部同时存在很多个。

那OS如何管理共享内存?共享内存要有唯一的标识符key

参数解释

shmflg:是共享内存打开的方式,有IPC_CREAT、IPC_EXCL

IPC_CREAT:单独使用IPC_CREAT,若shm不存在,则创建。若存在,获取它并返回。(保证调用进程能拿到共享内存,无论新老)

IPC_CREAT | IPC_EXCL:若shm不存在,就创建它。若存在,出错并返回。(只要成功,一定是新的共享内存,不使用老的共享内存)

key:

1.必须由用户输入

2.你怎么保证shm的唯一性?怎么保证不同进程看到的是同一个共享内存?

使用shmid标识。

3.这个key为什么要用户传入?内核自己生成不就行了?

内核提供就不能看到同一份资源了。(因为用户看不到无法给进程指定资源)

key_t key随便设置?冲突了怎么办,使用如下库函数,通过公共的路径+公共的项目ID(用户指定)生成一个key

那么Linux是如何在应用层面,保证不同进程看到同一份资源的?

路径+项目ID

3.1.2共享内存的管理

共享内存的生命周期:随内核!故:

1.用户必须让OS释放------a.指令释放 b.代码

2.OS重启

共享内存的管理指令及系统调用

bash 复制代码
# 查看
ipcs -m
# 删除
ipcrm -m [shmid]
cpp 复制代码
//                       指令         输出型参数
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

//将共享内存挂接到地址空间中        
//                      用户指定挂接到什么位置(一般不用)
void *shmat(int shmid, const void *shmaddr, int shmflg);
//成功时返回对应地址,失败返回-1

//datach去关联 将共享内存从地址空间上去关联
int shmdt(const void *shmaddr);

共享内存也有权限,与文件一样

挂接失败:shmget时权限设置的问题,在shmflg处 | mode

3.1.3共享内存的特点

1.通信速度是最快的

因为共享内存没有缓冲区和拷贝的使用,所以速度快。

管道从管道中读写数据,需要在缓冲区进行拷贝。

共享内存因为挂接到进程地址空间上,可以直接由地址找到其中的数据。

2.让两个进程在各种自的用户空间共享内存块,但是没有加任何保护机制

3.共享内存,保护机制,需要用户自己完成保护------信号量、命名管道

·为什么需要保护机制进程具有独立性------>通信------>不同进程看到同一份资源------>数据不一致问题------>临界&&临界资源&&加锁&&同步

cpp 复制代码
	//共享内存也可以获取结构化的内存(结构体)
    //直接指向共享内存,向共享内存中写入
    struct Data *image = (struct Data *)addr;
	
    while (true)
    {

        strcpy(image->status, "最新");
        strcpy(image->lasttime, GetCurrentTime().c_str());
        strcpy(image->image, "***********");
        // 使用管道向读端发命令表示可读
        fifo.Sginal();
        sleep(1);
    }

3.2System V消息队列

消息队列本质:一个进程向另一个进程发送有数据类型数据块的方法。

系统调用接口

cpp 复制代码
//创建消息队列
int msgget(key_t key, int msgflg);
//控制接口
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
//发送消息
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
//接收消息
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

bash指令

bash 复制代码
ipcs -q

因为都是System V标准,共享内存、消息队列、信号量接口非常相似

3.3System V信号量

系统调用接口:

cpp 复制代码
//                    信号量个数
int semget(key_t key, int nsems, int semflg);
//               
int semctl(int semid, int semnum, int cmd, ...);

bash指令

bash 复制代码
ipcs -s