Linux下进程之间通信方式详解

本文深入解析进程间通信(IPC)的多种方式,包括简单的命令行参数、环境变量、信号、文件,以及更复杂的管道、共享内存、消息队列、信号量和网络通信。详细介绍了每种通信方式的实现原理、使用场景和编程模型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、基本概念

什么进程间通信(IPC,Interprocess communication):是指两人个或多个进程之间交换数据的过程叫进程间通信。
进程之间为什么需要通信?
当需要多个进程协同工作高效率完成任务时,因为每个进程都个独立的个体(资源单位),进程之间就需要进行通信。
进程间通信方式:
1、简单进程间通信:命令行参数,环境变量表、信号、文件
2、传统进程间通信:管道
3、XSI进程间通信:共享内存、消息队列、信号量
4、网络进程间通信:socket

二、传统的进程间通信-管道

管道是UNIX系统最古老的进程间通信方式(基本不再使用),历史上的管道通常是半双工(只允许单向数据流动),现在的系统大都可以全双工,数据可以双向流动。

1、有名管道(创建实体文件)

函数:int mkfifo(const char *pathname, mode_t mode);
  功能:创建管道文件
  pathname:文件路径
  mode:权限
  返回值:成功返回0,失败返回-1。

2、无名管道(用于通过fork创建的父子进程之间通信)

函数: int pipe(int pipefd[2]);
 功能:创建无名管道
 pipefd:用来存储内核返回的文件描述符
  pipefd[0] 用于读操作
  pipefd[1] 用于写操作
编程模型:
  进程A     			进程B
  创建管道(mkfifo)  		...
  打开管道(open)   		打开管道
  读/写数据 (read/write) 	读/写数据
  关闭管道(close)   		关闭管道
  删除管道(unlink)   		...

三、XSI进程间通信

X/open组织为UNIX系统设计一套进程间通信机制,有共享内存、消息队列、信号量。
1、IPC标识
内核会为每个进程间通信维护一个IPC对象(XSI对象)。
该对象通过一个非负整数来引用(类似于文件描述符)。
与文件描述符不同的是,每用一个IPC对象标识符就持续+1,达到最大值时再从零开始。
IPC标识需要程序员自己创建(类似于创建文件)。
2、IPC键值
创建IPC键值的依据(类似创建文件的文件名),也是一个非负整数。
1、自定义(不建议,可能会冲突)。
2、自动生成(项目路径,项目编号)。
key_t ftok(const char *pathname, int proj_id);
注意:项目路径一定要是有效路径,生成IPC键依靠的是路径而不是字符串。
3、IPC对象的创建用到的宏
IPC_PRIVATE 创建IPC对象时永远创建成功。
IPC_CREAT 对象存在则获取,不存在则创建。
IPC_EXCL 如果对象已经创建,则创建失败。
4、IPC对象销毁/控制用到的宏
IPC_STAT 获取IPC对象的属性
IPC_SET 设置IPC对象的属性
IPC_RMID 删除IPC对象

四、共享内存

共享内存就是内核中开辟一块由IPC对象管理内存,进程A和进程B都用自己的虚拟地址与它进程映射,这样他就共享了同一块内存,然后就可以通信了。
特点:
1、不需要复制信息,是最快的一种进程间通信机制。
2、需要考虑同步问题(必须借助其它的机制,如信号)。

int shmget(key_t key, size_t size, int shmflg)
 功能:创建/获取共享内存
 key:IPC键,由ftok函数生成
 size:共享内存的大小,最好是4096的整数倍,获取共享内存时,此值无效。
 shmflg:
  0 获取共享内存
  IPC_CREAT 创建
  IPC_EXCL 如果存在则创建失败
 返回值:成功返回共享内存标识(IPC标识),失败返回-1。
void* shmat(int shmid, const void *shmaddr, int shmflg);
 功能:映射共享内存
 shmid:共享内存标识符,shmget函数的返回值。
 shmaddr:进程提供的虚拟地址,与内核中的内存映射用的,也可以是NULL(内核会自动选择一个地址映射)。
 shmflg:
  	0 		自动分配
  	SHM_RDONLY 	只读权限
  	SHM_RND 	当shmaddr不为空时shmaddr向下取整页。
 返回值:映射成功后的虚拟地址。
int shmdt(const void *shmaddr);
功能:取消虚拟地址与共享内存的映射
shmaddr:被映射过的虚拟地址
int shmctl(int shmid, int cmd, struct shmid_ds* buf);
功能:删除共享内存,获取/设置共享内存的属性
shmid:共享内存标识符shmget的返回值
cmd:
  IPC_STAT 获取共享内存的属性
  IPC_SET 设置共享内存的的属性
  IPC_RMID 删除IPC共享内存
  
struct shmid_ds
 {
	  struct ipc_perm shm_perm;  // 内存所有者及权限
	  size_t          shm_segsz;  // 内存的大小
	  time_t          shm_atime;  // 最后的映射时间
	  time_t          shm_dtime;  // 最后的取消映射时间
	  time_t          shm_ctime;  // 最后修改时间
	  pid_t           shm_cpid;   // 创建者进程ID
	  pid_t           shm_lpid;   // 最后映射/取消映射的进程ID
	  shmatt_t   shm_nattch;      // 映射的次数
 }
	    
struct ipc_perm
 {
	  key_t          __key;   // IPC键值
	  uid_t          uid;     // 有效用户ID 
	  gid_t          gid;     // 有效组ID
	  uid_t          cuid;    // 创建者的用户ID
	  gid_t          cgid;    // 创建者组ID
	  unsigned short mode;    // 权限
	  unsigned short __seq;   // IPC标识
}
编程模型:
 进程A     			进程B
 生成IPC键值 ftok  		生成IPC键 ftok
 创建共享内存 shmget  		获取共享内存 shmget
 映射共享内存 shmat 		映射共享内存 shmat
 使用共享内存 *ptr 		使用共享内存 *ptr
 取消映射  shmdt  		取消映射  shmdt
 删除共享内存 shmctl

五、消息队列

消息队列就是由内核负责管理的一个管道,可以按顺序发送消息包(消息类型+消息内容),可以全双工工作,可以不按消息的顺序接收消息。

int msgget(key_t key, int msgflg);
 功能:创建/获取消息队列
 key:IPC键值,由ftok函数自动生成
 msgflg:
  	0 		获取消息队列
  	IPC_CREAT 	创建消息队列
  	IPC_EXCL 	如果存在则创建失败
 返回值:消息队列标识
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
 功能:向消息队列发送消息
 msqid:消息队列标识,msgget函数的返回值
 msgp:结构指针
   struct msgbuf 
   {
    	long mtype;     //消息类型
        char mtext[n];  //消息内容  
   };
 msgsz:消息的长度,不包括消息类型,sizeof(msgbuf)-sizeof(long)。
 msgflg:
  	0 阻塞,当消息队列满时,等待。
  	IPC_NOWAIT 不阻塞,当消息队列满时,不等待。
 返回值:成功发送返回0,失败返回-1。
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
功能:从消息队列中按类型获取消息
msqid:消息队列标识,msgget函数的返回值
msgp:
struct msgbuf 
{
            long mtype;     //消息类型
            char mtext[n];  //消息内容  
};
msgsz:要接收一消息的长度,可以长一些。
msgtyp:要接收的消息类型
	0 接收任意类型的消息(接收队列中第一个消息)。
        >0 只接收msgtyp类型的消息
        <0 接收消息队列中小于等于msgtyp绝对值的消息,取小的那个。
msgflg:
     0 阻塞,消息队列中是否有对应类型的消息,没有则等待。
     1 不阻塞,消息队列中没有对应类型的消息,则返回。
     ----------------------------------------
     MSG_NOERROR:
      	消息类型正确,而消息的实际长度大于msgsz,则不接收消息并返回-1。
      	如果msgflg带MSG_NOERROR标志,则把多余的消息截取,成功接收。
     IPC_NOWAIT:如果消息队列没有要接收的消息,则不等待,返回-1。
     MSG_EXCEPT:接收消息队列中第一个消息类型不是msgtyp的消息,编译时添加-D_GNU_SOURCE参数。
     
返回值:成功接收到消息的字节数
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:删除消息队列,设置或获取消息队列属性
msqid:消息队列标识,msgget函数的返回值
cmd:
  	IPC_STAT 获取消息队列的属性
  	IPC_SET 设置消息队列的的属性
 	IPC_RMID 删除消息队列	
返回值:成功返回0,失败返回-1。
 
struct msqid_ds 
{
  struct ipc_perm msg_perm;   // 权限
  time_t          msg_stime;  // 最后一个消息发送时间
  time_t          msg_rtime;  // 最后一次消息接收时间
  time_t          msg_ctime;  // 最后一次修改时间
  unsigned long __msg_cbytes; // 消息队列中的字节数
  msgqnum_t       msg_qnum;   // 消息队列中消息的个数
  msglen_t        msg_qbytes; // 消息队列中容纳的最大字节数
  pid_t           msg_lspid;  // 最后一次发送消息进程
  pid_t           msg_lrpid;  // 最后一次接收消息进程
}

六、信号量

int semget(key_t key, int nsems, int semflg);
 功能:创建/获取信号量
 key: IPC键值
 nsems: 信号量的数量
 semflg:
  	0 获取信号量
  	IPC_CREAT 创建信号量
  	IPC_EXCL 如果已经存在则创建
 返回值:信号量的标识
int semop(int semid, struct sembuf *sops, unsigned nsops);
 功能:操作信号量
 semid: 信号量标识,semget的返回值
 sops:结构体数组
 nsops:数组的的长度
 struct sembuf
 {
   	unsigned short sem_num; 信号量的下标
   	short sem_op; 操作
   	short sem_flg; 标记
   	IPC_NOEAIT 当信号量不够时,不阻塞
   	SEM_UNDO 当进程结束时,信号量的值自动归还
 }
int semtimedop(int semid, struct sembuf *sops, unsigned nsops,struct timespec *timeout);
 功能:带时间限制的操作信号量
 struct timespec
 {
  	__time_t tv_sec; 秒
  	long int tv_nsec; 纳秒
 }
int semctl(int semid, int semnum, int cmd, …);
 功能:初始化信号量的值、删除信号量、获取、设置信号量的属性
 cmd: 
  IPC_STAT 获取信号量的属性
  IPC_SET 设置信号量的属性
  IPC_RMID 删除信号量的属性
  IPC_INFO 获取信号量的信息
  SEM_INFO 设置信号量的信息
  GETALL 获取所有信号量
  GETNCNT 
  GETVAL
  SETALL 设置所有信号量的值
  SETVAL 设置某个信号量的值
编程模型:
 进程A      			进程B
 创建信号量 semget   		获取信号量
 初始化信号量的值 semctl  	加减信号量
 加减信号量 semop   
 删除信号量 semctl

七、IPC命令

显示IPC对象
ipsc -m
ipcs -q
ipcs -s
ipcs -a
删除IPC对象
ipcrm -m ID
ipcrm -q ID
ipcrm -s ID

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值