进程间通信

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

二、传统的进程间通信-管道
管道是UNIX系统最古老的进程间通信方式(基本不在使用),历史上的管道通常是半双工
(只允许单向数据流动),现在的系统大都可以全双工,数据可以双向流动
1、有名管道(创建实体文件) tmp
命令:mkfifo
函数:mkfifo
int mkfifo(const char *pathname, mode_t mode);
功能:创建管道文件
pathname:文件路径
mode:权限
返回值:成功返回0,失败返回-1

			编程模型:
				进程A						进程B
				创建管道(mkfifo)			....
				打开管道(open)			打开管道
				读/写管道(read/write)		读/写数据
				关闭管道(close)			关闭管道
				删除管道(unlink)			....
			
	2、无名管道(用于通过fork创建的父子
		int pipe(int pipefd[2]);
		功能:创建无名管道
		pipefd:用来存储内核返回的文件描述符
			pipefd[0];用于读操作
			pipdfd[1]:用于写操作
	
	练习1:使用有名管道进行通信,管道创建者读,对方写
	练习2:使用无名管道进程通信,父进程读,子进程写

三、XSI进程间通信
X/open组织为UNIX系统设计一套进程间通信机制,有共享内存、消息队列、信号量
1、IPC标识
内核会为每个XSI的进程间通信对象维护一个IPC对象(XSI对象)
该对象通过一个非负整数来引用(类似于文件描述符)
与文件描述符不同的是,每用一个IPC对象标识符就持续+1,达到最大值时再从零开始
IPC标识需要程序员自己创建(类似于创建文件)
2、IPC键值
#include <sys/types.h>
#include <sys/ipc.h>

	创建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对象

四、共享内存
#include <sys/ipc.h>
#include <sys/shm.h>

	共享内存就是内核中开辟一块由IPC对象管理内存,进程A和进程B都用自己的虚拟
地址与它映射,这样就共享了同一块内存,然后就可以通信了
	特点:
		1、不需要复制信息,是最快的一种进程间通信机制
		2、需要需要同步问题(必须借助其它的机制,如信号)
	编程模型:
	进程A:							进程B:
	生成IPC键值			ftok		生成IPC键			ftok
	创建共享内存		shmget		获取共享内存		shmget
	映射共享内存		shmat		映射共享内存		shmat
	使用共享内存		*ptr		使用共享内存		*ptr
	取消映射共享内存	shmdt		取消映射			shmdt
	删除共享内存		shmctl		.....

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:
		SHM_RDONLY	只读权限
		SHM_RND		当shmaddr不为空时自动选择一个地址映射
	返回值:映射成功后的虚拟地址,失败返回 (void*)-1
	
int shmdt(const void *shmaddr);
	功能:取消虚拟地址与共享内存的映射
	shmaddr:被映射过的虚拟地址

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
	功能:删除共享内存,获取/设置共享内存
	shmid:共享内存标识符
	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;    /* 对象id */
       };

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

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[1];    /* 消息内容 */
    };
msgsz:消息的长度,不包括消息类型,sizeof(msgbuf)-4
msgflg:
	0	阻塞,当消息队列满时,等待
	1	不阻塞,当消息队列满时,不等待
	------------------------------------
返回值:成功发送返回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[1];    /* 消息内容 */
    };
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	删除IPC消息队列
	
	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;    /* 最后一个接收消息的进程 */
       };
	 
	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;    /* 对象id */
       };
	
返回值:成功返回0,失败返回-1

六、信号量
内核维护的计数器,用于多进程之间共享资源
例如:有个变量n表示资源的数量,当有进程想要独占一个资源时,n的值要减1,如果n的值
等于0(不够减多个),则进程阻塞,直到n的值可以减再被唤醒,当资源使用完毕后n的值要加1
(可能加多个)

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_NOWAIT	当信号量不够减时,不阻塞
			SEM_NUDO	当进程结束时,信号量的值自动归还
	}
	
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		设置某个信号量的值
	union semun {
           int              val;    /* Value for SETVAL */
           struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
           unsigned short  *array;  /* Array for GETALL, SETALL */
           struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                       (Linux-specific) */
       };
	
	//
	struct ipc_perm {
           key_t          __key; /* Key supplied to semget(2) */
           uid_t          uid;   /* Effective UID of owner */
           gid_t          gid;   /* Effective GID of owner */
           uid_t          cuid;  /* Effective UID of creator */
           gid_t          cgid;  /* Effective GID of creator */
           unsigned short mode;  /* Permissions */
           unsigned short __seq; /* Sequence number */
       };

编程模型:
	进程A								进程B
	创建信号量	semget					获取信号量
	初始化信号量的值	semctl			......
	加减信号量	semop					加减信号量
	删除信号量	semctl					......
注意:信号量是用来计数的,一定要有资源对应	

七、IPC命令
显示IPC对象
ipcs -m
ipcs -q
ipcs -s
ipcs -a
删除IPC对象
ipcrm -m ID
ipcrm -q ID
ipcrm -s ID

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值