Linux进程间通信

文章目录

  • 进程间通信介绍
    • 进程间通信的概念
    • 进程间通信目的
    • 进程间通信的发展
    • 进程间通信分类
  • 管道
    • 什么是管道
  • 匿名管道
    • pipe函数
    • 匿名管道使用步骤
    • 管道读写规则
    • 管道特点
    • 管道的四种特殊情况
    • 管道的大小
  • 命名管道
    • 创建命名管道
    • 在进程中创建命名管道
    • 用命名管道实现不同文件的通信
  • system V共享内存
    • 共享内存的基本原理
    • 共享内存函数
      • shmget函数
      • shmctl函数
      • shmat和shmdt函数
    • 用共享内存实现通信
  • System V消息队列
  • 进程互斥

进程间通信介绍

进程间通信的概念

进程通信是指在进程间传输数据(交换信息)

进程间通信目的

  • 数据传输: 一个进程需要将它的数据发送给另一个进程
  • 资源共享: 多个进程之间共享同样的资源
  • 通知事件: 一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
  • 进程控制: 有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

进程间通信的发展

  • 管道
  • System V进程间通信
  • POSIX进程间通信

进程间通信分类

管道

  • 匿名管道
  • 命名管道

System V IPC

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

POSIX IPC

  • 消息队列
  • 共享内存
  • 信号量
  • 互斥量
  • 条件变量
  • 读写锁

管道

什么是管道

管道是Unix中最古老的进程间通信的形式,我们把从一个进程连接到另一个进程的一个数据流称为一个“管道“

例:统计云服务器上登录的用户个数

Linux进程间通信_第1张图片

who和wc运行起来后变成了两个进程,who进程通过标准输出将数据打到“管道”当中,wc进程再通过标准输入从“管道”当中读取数据。

Linux进程间通信_第2张图片
who命令用于查看当前云服务器的登录用户(一行显示一个用户),wc -l用于统计当前的行数。

匿名管道

pipe函数

作用: 创建匿名管道
函数原型:

int pipe(int pipefd[2]);

pipe函数的参数是一个输出型参数,数组pipefd用于返回两个指向管道读端和写端的文件描述符:
pipefd[0]表示管道读端的文件描述符
pipefd[1]表示管道写端的文件描述符

pipe函数调用成功时返回0,调用失败时返回-1。

匿名管道使用步骤

  1. 父进程调用pipe函数创建管道。

Linux进程间通信_第3张图片

2、父进程创建子进程。

Linux进程间通信_第4张图片
3、父进程关闭写端,子进程关闭读端。

Linux进程间通信_第5张图片
注意:

  1. 管道只能够进行单向通信,因此当父进程创建完子进程后,需要确认父子进程谁读谁写,然后关闭相应的读写端。
  2. 从管道写端写入的数据会被内核缓冲,直到从管道的读端被读取。

站在文件描述符角度-深度理解管道

  1. 父进程调用pipe函数创建管道。

Linux进程间通信_第6张图片

  1. 父进程创建子进程。

Linux进程间通信_第7张图片

  1. 父进程关闭写端,子进程关闭读端。

Linux进程间通信_第8张图片

代码演示

#include
#include
#include
#include
#include

int main()
{
	int pipe_fd[2]={0};
	
	if(pipe(pipe_fd)<0){//使用pipe创建匿名管道
	    perror("pipe");
	    return 1;
	}
	
	pid_t id=fork();
	if(id<0){
	    perror("fork");
	    return 2;
	}
	else if(id==0){ // write24                 // child
		close(pipe_fd[0]); // 子进程关闭读端 
		
		const char *msg="hello parent,I am child\n";
		int count=5;
		while(count)
		{
			  //子进程向管道写入数据
		      write(pipe_fd[1],msg,strlen(msg));
		      sleep(1);
		      count--;
		}
		
		close(pipe_fd[1]);//子进程写入完毕,关闭文件
		exit(0);
	}
	else{  // read41                
		// parent
		close(pipe_fd[1]); //父进程关闭写端
		char buffer[64];
		//父进程从管道读取数据
		while(1){
			buffer[0]=0;
			ssize_t size=read(pipe_fd[0],buffer,sizeof(buffer)-1);
			if(size > 0){
				buffer[size]=0;
				printf("parent get message from child# %s",buffer);
			}
			else if(size==0){
				printf("pipe file close , child quit!\n");
				break;
			}
			else{
				break;
			}
		}
	
		int status=0;
		if(waitpid(id,&status,0)>0){
		        printf("child quit,wait success!\n");
		}
		close(pipe_fd[0]);
	}
	return 0;
}

运行结果:

Linux进程间通信_第9张图片

管道读写规则

  1. 当没有数据可读时

O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。
O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。

  1. 当管道满的时候

O_NONBLOCK disable: write调用阻塞,直到有进程读走数据
O_NONBLOCK enable:调用返回-1,errno值为EAGAIN

  1. 如果所有管道写端对应的文件描述符被关闭,则read返回0
  2. 如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程退出
  3. 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
  4. 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。

管道特点

  1. 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
  2. 管道提供流式服务

对于进程A写入管道当中的数据,进程B每次从管道读取的数据的多少是任意的,这种被称为流式服务,与之相对应的是数据报服务: 数据有明确的分割,拿数据按报文段拿。

  1. 一般而言,进程退出,管道释放,所以管道的生命周期随进程
  2. 一般而言,内核会对管道操作进行同步与互斥

同步: 两个或两个以上的进程在运行过程中协同步调,按预定的先后次序运行。比如,A任务的运行依赖于B任务产生的数据。
互斥: 一个公共资源同一时刻只能被一个进程使用,多个进程不能同时使用公共资源。

  1. 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道

单工通信(Simplex Communication): 单工模式的数据传输是单向的。通信双方中,一方固定为发送端,另一方固定为接收端。
半双工通信(Half Duplex): 半双工数据传输指数据可以在一个信号载体的两个方向上传输,但是不能同时传输。
全双工通信(Full Duplex): 全双工通信允许数据在两个方向上同时传输,它的能力相当于两个单工通信方式的结合。全双工可以同时(瞬时)进行信号的双向传输。

管道的四种特殊情况

  1. 写端进程不写,读端进程一直读,那么此时会因为管道里面没有数据可读,对应的读端进程会被挂起,直到管道里面有数据后,读端进程才会被唤醒。
  2. 读端进程不读,写端进程一直写,那么当管道被写满后,对应的写端进程会被挂起,直到管道当中的数据被读端进程读取后,写端进程才会被唤醒。
  3. 写端进程将数据写完后将写端关闭,那么读端进程将管道当中的数据读完后,就会继续执行该进程之后的代码逻辑,而不会被挂起。
  4. 读端进程将读端关闭,而写端进程还在一直向管道写入数据,那么操作系统会将写端进程杀掉。

我们可以通过以下代码看看第4种情况中写段收到了何种信号

#include 
#include 
#include 
#include 
#include 
#include 
int main()
{
	int fd[2] = { 0 };
	if (pipe(fd) < 0){ //创建匿名管道
		perror("pipe");
		return 1;
	}
	pid_t id = fork(); 
	if (id == 0){
		//child
		close(fd[0]); //子进程关闭读端
		//子进程向管道写入数据
		const char* msg = "hello father, I am child...";
		int count = 10;
		while (count--){
			write(fd[1], msg, strlen(msg));
			sleep(1);
		}
		close(fd[1]); //关闭写端
		exit(0);
	}
	//father
	close(fd[1]); //父进程关闭写端
	close(fd[0]); //父进程直接关闭读端(导致子进程被操作系统杀掉)
	int status = 0;
	waitpid(id, &status, 0);
	printf("child get signal:%d\n", status & 0x7F); //打印子进程收到的信号
	return 0;
}

写端收到的是13信号
Linux进程间通信_第10张图片

我们可以通过 kill -l 命令查看信号含义

Linux进程间通信_第11张图片

管道的大小

管道的容量时有限的,那么管道究竟能同时存储多少数据呢?

方法一:

使用ulimit -a命令,查看当前资源限制的设定。

Linux进程间通信_第12张图片
根据显示,管道的最大容量是 512 × 8 = 4096 字节。

方法二:

可以向管道中不断写数据,看最多能写多少数据

#include 
#include 
#include 
#include 
int main()
{
	int fd[2] = { 0 };
	if (pipe(fd) < 0){ //创建匿名管道
		perror("pipe");
		return 1;
	}
	pid_t id = fork();
	if (id == 0){
		//child 
		close(fd[0]); //子进程关闭读端
		char c = 'a';
		int count = 0;
		//子进程一直进行写入,一次写入一个字节
		while (1){
			write(fd[1], &c, 1);
			count++;
			printf("%d\n", count); //打印当前写入的字节数
		}
		close(fd[1]);
		exit(0);
	}
	//father
	close(fd[1]); //父进程关闭写端

	waitpid(id, NULL, 0);
	close(fd[0]);
	return 0;
}

运行结果:
Linux进程间通信_第13张图片

不同操作系统的管道大小不同,一切以实际为准。

命名管道

  • 管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
  • 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。命名管道是一种特殊类型的文件,两个进程通过命名管道的文件名打开同一个管道文件,此时这两个进程也就看到了同一份资源,进而就可以进行通信了。

注意:

  1. 普通文件很难做到通信,这里的命名管道是一种特殊类型的文件。
  2. 命名管道和匿名管道一样,都是内存文件,只不过命名管道在磁盘有一个简单的映像,但这个映像的大小永远为0,因为命名管道和匿名管道都不会将通信数据刷新到磁盘当中。

创建命名管道

我们可以使用 mkfifo 命令创建一个命名管道。

Linux进程间通信_第14张图片
使用这个命名管道文件,就能实现两个进程之间的通信了。

我们在一个进程(进程A)中用shell脚本每秒向命名管道写入一个字符串,在另一个进程(进程B)当中用cat命令从命名管道当中进行读取。

Linux进程间通信_第15张图片
当管道的读端进程退出后,写端进程再向管道写入数据就没有意义了,此时写端进程会被操作系统杀掉。我们终止掉读端进程后,因为写端执行的循环脚本是由命令行解释器bash执行的,所以此时bash就会被操作系统杀掉,我们的云服务器也就退出了。

Linux进程间通信_第16张图片

在进程中创建命名管道

在进程中创建命名管道需要用到mkfifo函数

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

参数:

pathname

  • 若pathname以路径的方式给出,则将命名管道文件创建在pathname路径下。
  • 若pathname以文件名的方式给出,则将命名管道文件默认创建在当前路径下。

mode

创建命名管道文件的默认权限。

返回值:

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

用命名管道实现不同文件的通信

我们先写一个服务端(server)和客户端(client)程序

服务端代码

#include
#include
#include
#include
#include

#define FIFO "./fifo"
int main()
{
        int ret=mkfifo(FIFO,0644); // 创建命名管道文件
        if(ret<0){
                perror("mkfilo");
                return 1;
        }

        int fd=open(FIFO,O_RDONLY);//以读的方式打开命名管道文件
        if(fd<0){
                perror("open");
                return 2;
        }

        char buffer[128];
        while(1){
                buffer[0]=0;
                //从命名管道当中读取信息到buffer中
                ssize_t s=read(fd,buffer,sizeof(buffer)-1);
                if(s>0){
                        buffer[s]=0;
                        printf("client# %s\n",buffer);//输出客户端发来的信息
                }
                else if(s==0){
                        printf("client quit...\n");
                        break;
                }
                else{
                        break;
                }

        }
        close(fd);//关闭命名管道文件
        return 0;
}

客户端代码

#include
#include
#include
#include
#include
#include

#define FIFO "./fifo"9 
int main()
{
        int fd=open(FIFO,O_WRONLY);
        if(fd<0){
                perror("open");
                return 2;
        }

        char buffer[128];

        while(1){
                printf("Please Enter# ");
                fflush(stdout);
                buffer[0]=0;
                ssize_t s=read(0,buffer,sizeof(buffer)-1);
                if(s>0){
                        buffer[s]=0;
                        write(fd,buffer,strlen(buffer));
                }
                else if(s==0){
                        printf("client quit...\n");
                        break;
                }
                else{
                        break;
                }

        }
        return 0;
 }

当服务端和客户端代码运行起来后,我们再客户端输入信息再服务端能够接收到。

Linux进程间通信_第17张图片

system V共享内存

system V IPC提供的通信方式有以下三种:

  1. system V共享内存
  2. system V消息队列
  3. system V信号量

system V共享内存和system V消息队列是以传送数据为目的的,而system V信号量是为了保证进程间的同步与互斥。

共享内存的基本原理

共享内存为了让不同进程看到同一份资源,在物理内存中申请一块内存空间,然后将这块内存空间分别与各个进程各自的页表之间建立映射。在进程虚拟地址空间当中开辟空间并将虚拟地址填充到各自页表的对应位置,使得虚拟地址和物理地址之间建立起对应映射关系,这样不同进程就看到了同一份物理内存,即共享内存。

Linux进程间通信_第18张图片
共享内存数据结构

在系统当中可能会有大量的进程在进行通信,因此系统当中就可能存在大量的共享内存,那么操作系统必然要对其进行管理,所以共享内存除了在内存当中真正开辟空间之外,系统一定还要为共享内存维护相关的内核数据结构。

struct shmid_ds {
	struct ipc_perm     shm_perm;   /* operation perms */
	int         shm_segsz;  /* size of segment (bytes) */
	__kernel_time_t     shm_atime;  /* last attach time */
	__kernel_time_t     shm_dtime;  /* last detach time */
	__kernel_time_t     shm_ctime;  /* last change time */
	__kernel_ipc_pid_t  shm_cpid;   /* pid of creator */
	__kernel_ipc_pid_t  shm_lpid;   /* pid of last operator */
	unsigned short      shm_nattch; /* no. of current attaches */
	unsigned short      shm_unused; /* compatibility */
	void            *shm_unused2;   /* ditto - used by DIPC */
	void            *shm_unused3;   /* unused */
};

共享内存函数

shmget函数

功能: 创建共享内存
函数原型:

int shmget(key_t key, size_t size, int shmflg);

返回值:

  • 调用成功返回该共享内存段的标识码
  • 调用失败返回-1

参数说明:

  • 第一个参数key,表示要创建共享内存在系统当中的唯一标识(类似哈希表)。
  • 第二个参数size,表示要创建共享内存的大小。(建议4KB整数倍)
  • 第三个参数shmflg,表示创建共享内存的方式。

注意: 系统分配共享内存是按4KB整数倍分配的,如果创建共享内存的大小不为4KB整数倍会造成空间浪费。

注意: 传入shmget函数的第一个参数key,需要我们使用ftok函数进行获取

ftok函数原型

key_t ftok(const char *pathname, int proj_id);

ftok函数的作用就是,将一个已存在的路径名pathname和一个整数标识符proj_id转换成一个key值,称为IPC键值,在使用shmget函数获取共享内存时,这个key值会被填充进维护共享内存的数据结构当中。需要注意的是,pathname所指定的文件必须存在且可存取。

shmflg

传入shmget函数的第三个参数shmflg,常用的组合方式有以下两种:

  1. IPC_CREAT : 如果内核中不存在键值与key相等的共享内存,则新建一个,如果存在直接返回该共享内存。
  2. IPC_CREAT | IPC_EXCL : 如果内核中不存在键值与key相等的共享内存,则新建一个,如果存在直接报错。

shmget函数的使用

我们用shmget函数创建共享内存,用ipcs命令查看相关信息。

ipcs指令可以查看进程间通信的有关信息

Linux进程间通信_第19张图片

我们可以在ipcs后加上选项来查看指定通信设施的信息

  • -q:列出消息队列相关信息。
  • -m:列出共享内存相关信息。
  • -s:列出信号量相关信息。


我们可以用 ipcs -m 来查看共享内存相关信息。

Linux进程间通信_第20张图片

ipcs命令输出的每列信息的含义如下:

标题 含义
key 系统区别各个共享内存的唯一标识
shmid 共享内存的d
owner 共享内存的拥有者
perms 共享内存的权限
bytes 共享内存的大小
nattch 关联共享内存的进程数
status 共享内存的状态

shmget函数创建共享内存

comm.h文件

#pragma once

#include

#define PATH_NAME "/home/nzb/lesson20"
#define PROJ_ID 0x6666

#define SIZE 4097

server.c文件

#include"comm.h"
#include
#include

int main()
{
        key_t k=ftok(PATH_NAME,PROJ_ID);
        if(k<0){
                perror("ftok");
                return 1;
        }
        printf("key:%x\n",k);

        int shmid=shmget(k,SIZE,IPC_CREAT | IPC_EXCL);// 共享内存不存在创建,>    存在报错
        if(shmid<0){
                perror("shmget");
                return 2;
        }
        printf("shmid:%d\n",shmid);

        return 0;
}

运行结果
Linux进程间通信_第21张图片

从图中我们看到 server 进程退出后,新建的共享内存并没有被删除,这是因为
所有的ipc资源都是随内核的,不随进程

那么如何释放ipc资源呢?

  1. 进程退出时调用函数释放,或用指令删除。
  2. 重启操作系统。

Linux进程间通信_第22张图片
那么如何在进程中删除的,这里就要引入shmctl函数。

shmctl函数

功能: 删除共享内存

函数原型:

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数:

  • 第一个参数shmid,表示由shmget返回的共享内存标识码。
  • 第二个参数cmd,表示将要采取的动作(有三个可取值)。
  • 第三个参数buf,是指向一个保存着共享内存的模式状态和访问权限的数据结构。

返回值:

  • shmctl删除成功,返回0。
  • shmctl删除失败,返回-1。

shmctl函数的第二个参数传入的常用的选项有以下三个:

命令 说明
IPC_STAT 将shmid_ds结构中的设置为共享内存的当前关联值
IPC_SET 在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值
IPC_RMID 删除共享内存段

例:
下面代码中创建共享内存,2秒后释放掉。

#include"comm.h"
#include
#include
#include

int main()
{
        // 创建key
        key_t k=ftok(PATH_NAME,PROJ_ID);
        if(k<0){
                perror("ftok");
                return 1;
        }
        printf("key:%x\n",k);

        // 申请共享内存
        int shmid=shmget(k,SIZE,IPC_CREAT | IPC_EXCL);// 共享内存不存在创建,存在报错
        if(shmid<0){
                perror("shmget");
                return 2;
        }
        printf("shmid:%d\n",shmid);

        sleep(2);
        // 释放共享内存
        shmctl(shmid,IPC_RMID,NULL);
        printf("delete shm!\n");
        return 0;
}

我们可以在程序运行时,使用以下监控脚本时刻关注共享内存的资源分配情况:

while :; do ipcs -m;echo "###################################";sleep 1;done

运行结果
Linux进程间通信_第23张图片

shmat和shmdt函数

shmat函数

功能: 将共享内存连接到进程地址空间

函数原型:

void *shmat(int shmid, const void *shmaddr, int shmflg);

参数:

  • 第一个参数shmid,共享内存标识
  • 第二个参数shmaddr,指定共享内存映射到进程地址空间的某一地址,通常设置为NULL,表示让内核自己决定一个合适的地址位置。
  • 第三个参数shmflg,表示关联共享内存时设置的某些属性。

返回值:

  • shmat调用成功,返回共享内存映射到进程地址空间中的起始地址。
  • shmat调用失败,返回(void*)-1。

shmflg传入的常用的选项有以下三个

选项 作用
SHM_RDONLY 关联共享内存后只进行读取操作
SHM_RND 若shmaddr不为NULL,则关联地址自动向下调整为SHMLBA的整数倍。公式:shmaddr-(shmaddr%SHMLBA)
0 默认为读写权限

shmdt函数

功能: 取消共享内存与进程地址空间之间的关联

函数原型:

int shmdt(const void *shmaddr);

参数

  • 待去关联共享内存的起始地址,即调用shmat函数时得到的起始地址。

返回值

  • shmdt调用成功,返回0。
  • shmdt调用失败,返回-1。

使用演示

#include"comm.h"
#include
#include
#include
int main()
{
        // 创建key
        key_t k=ftok(PATH_NAME,PROJ_ID);
        if(k<0){
                perror("ftok");
                return 1;
        }
        printf("key:%x\n",k);
        // 申请共享内存
        int shmid=shmget(k,SIZE,IPC_CREAT | IPC_EXCL | 0644);// 共享内存不存在创建,存在>    报错
        if(shmid<0){
                perror("shmget");
                return 2;
        }
        printf("shmid:%d\n",shmid);
        sleep(1);
        // 将当前进程好共享内存关联
        char* start=(char*)shmat(shmid,NULL,0);
        printf("server already attach on shared memory!\n");
        // 可以使用共享内存通信了
        sleep(1);
        // 将当前共享内存去关联
        shmdt(start);
        printf("server already dattch off shared memory!\n");
        sleep(1);
        // 释放共享内存
        shmctl(shmid,IPC_RMID,NULL);
        printf("delete shm!\n");
        return 0;
}

运行结果
Linux进程间通信_第24张图片

注意:

shmat函数将共享内存连接到进程地址空间需要共享内存有相应的权限,我们在使用shmget函数创建共享内存时,需要在其第三个参数处设置共享内存创建后的权限,权限的设置规则与设置文件权限的规则相同。

int shmid=shmget(k,SIZE,IPC_CREAT | IPC_EXCL | 0644);

用共享内存实现通信

了解了共享内存的相关函数后我们可以尝试实现共享内存间的通信了

头文件 comm.h

#pragma once

#include

#define PATH_NAME "/home/nzb/lesson20"
#define PROJ_ID 0x6666

#define SIZE 4097

server.c 文件

#include"comm.h"
#include
#include
#include
int main()
{
        // 创建key
        key_t k=ftok(PATH_NAME,PROJ_ID);
        if(k<0){
                perror("ftok");
                return 1;
        }
        printf("key:%x\n",k);
        // 申请共享内存
        int shmid=shmget(k,SIZE,IPC_CREAT | IPC_EXCL | 0644);// 共享内存不存在创建,存在>    报错
        if(shmid<0){
                perror("shmget");
                return 2;
        }
        printf("shmid:%d\n",shmid);
        // 将当前进程好共享内存关联
        char* start=(char*)shmat(shmid,NULL,0);
        printf("server already attach on shared memory!\n");
        // 可以使用共享内存通信了
  		while (1){
				printf("%s\n", start);
				sleep(1);
		}
        // 将当前共享内存去关联
        shmdt(start);
        printf("server already dattch off shared memory!\n");

        // 释放共享内存
        shmctl(shmid,IPC_RMID,NULL);
        printf("delete shm!\n");
        return 0;
}

client.c 文件

#include"comm.h"
#include
#include
#include
#include

int main()
{       
        // 获取同一个key
        key_t k=ftok(PATH_NAME,PROJ_ID);
        if(k<0){
                perror("ftok");
                return 1;
        }
        printf("%x\n",k);        
        //不需要自己创建shm,获取共享内存
        int shmid=shmget(k,SIZE,IPC_CREAT);
        if(shmid<0){
                perror("shmget");
                return 2;
             }  
        // 关联共享内存
        char* start=(char*)shmat(shmid,NULL,0);
		//客户端不断向共享内存写入数据
		int i = 0;
		while (1){
			start[i] = 'A' + i;
			i++;
			sleep(1);
		}                  
        // 去关联
        shmdt(start):               
        return 0;
}

运行结果
Linux进程间通信_第25张图片

System V消息队列

消息队列实际上就是在系统当中创建了一个队列,队列当中的每个成员都是一个数据块,这些数据块都由类型和信息两部分构成,两个互相通信的进程通过某种方式看到同一个消息队列,这两个进程向对方发数据时,都在消息队列的队尾添加数据块,这两个进程获取数据块时,都在消息队列的队头取数据块。

Linux进程间通信_第26张图片

进程互斥

  • 由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为进程的互斥。
  • 系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源。
  • 在进程中涉及到互斥资源的程序段叫临界区
  • IPC资源必须删除,否则不会自动清除,除非重启,所以system V IPC资源的生命周期随内核

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