UNIX-Linux进程间通信

基本概念

进程间通信

进程间通信是指两个或多个进程之间交换数据的过程。

进程间为什么需要通信

当需要多个进程协同工作高效率完成任务时,因为每个进程都是独立个体(进程是资源单位),进程之间需要进行通信。

进程间通信方式的分类

1、简单进程间通信:命令行参数、环境变量表、信号、文件
2、传统进程间通信:管道
3、XSI进程间通信:共享内存、消息队列、信号量
4、网络进程间通信:socket

传统的进程间通信——管道

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

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

命令:mkfifo
函数:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
功能:创建管道文件
pathname:文件路径
mode:权限
返回值:成功返回0失败返回-1.
编程模型:
进程A					进程B
创建管道(mkfifo)			...
打开管道(open)			打开管道 
读/写数据(read)			读写数据
关闭管道(close)			关闭管道 
删除管道(unlink)			...

无名管道

#include <unistd.h>
int pipe(int pipefd[2]);
功能:创建无名管道
pipefd:用来存储内核返回的文件描述符。
pipefd[0]:用于读操作
pipefd[1]:用于写操作

XSI进程间通信

x/open组织位UNIX系统设计一套进程间通信机制,有共享内存、消息队列、信号量

IPC标识

  • 内核会为每个XSI进程通信对象维护一个IPC对象(XSI对象)。
  • 该对象通过一个非负整数来引用(类似文件描述符)。
  • 与文件描述符不同的是,每用一个IPC对象标识符就持续+1,达到最大值是再从零开始。
  • IPC标识需要程序员自己创建。

IPC键值

创建IPC键值的依据(类似创建文件时的文件名),也是一个非负整数。
1、自定义(不建议,可能会冲突)
2、自动生成。

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
注意:项目路径一定要是有效路径,生成IPC键值依靠的是路径而不是字符串。

IPC对象的创建用到的宏

IPC_PRIVATE	创建IPC对象时永远创建成功。
IPC_CREAT	对象存在则获取,不存在则创建
IPC_EXCL	如果对象已经存在则创建失败

IPC对象销毁/控制用到的宏

IPC_STAT	获取IPC对象的属性
IPC_SET		设置IPC对象的属性
IPC_RMID	删除IPC对象

共享内存

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

进程A								进程B
生成IPC 键值	ftok			生成IPC键值ftok
创建共享内存	shmget	获取共享内存
映射共享内存	shmat		映射共享内存
使用共享内存	*ptr			使用共享内存
取消映射			shmdt		取消映射
删除共享内存	shmctl		...

#include <sys/types.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
功能:创建或获取共享内存
key:IPC键值,由ftok函数生成
size:共享内存的大小,最好是4096的整数倍,获取共享内存时,此值无效。
shmflg:
	0 获取共享内存
	IPC_CREAT	创建
	IPC_EXCL	如果存在则创建失败
返回值:成功返回共享内存标识,失败返回-1

void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:映射共享内存
shmid:共享内存标识符,shmget函数的返回值。
shmaddr:进程提供的虚拟地址,与内核中的内存映射用的,也可以是NULL(内核会自动选择一个地址映射)。
shmflg:
	SHM_RDONLY	只读权限
	SHM_RND		当shmaddr不为空时自动选择一个地址映射
返回值:映射成功后的虚拟地址失败返回-1。

int shmdt(const void *shmaddr);
功能:取消虚拟地址与共享内存的映射
shmaddr:被映射过的虚拟地址
返回值:成功返回0失败返回-1

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:删除共享内存,获取/设置共享内存的属性
shmid:共享内存标识符shmget的返回值
cmd:
	IPC_STAT	获取共享内存的属性
	IPC_SET		设置共享内存的属性
	IPC_RMID	删除共享内存
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 */
       };

消息队列

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

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
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)-sizeof(msgbuf.mtype).
msgflg:
	0 阻塞,当消息队列满时,等待
	IPC_NOWAIT=1 不阻塞,当消息队列满时,不等待
	
返回值:成功发送返回0失败返回-1(返回-1的两种情况1、id有问题。2、队列满了)

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
功能:从消息队列中按消息类型获取消息获取消息
msqid:消息队列标识
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 (gcc -D_GNU_SOURCE b.c)
返回值:成功接收消息的字节数

int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:删除消息队列,设置或获取消息队列的属性
msqid:消息队列标识
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;    /* 最后一次接收消息的进程 */
    };
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 */
       };

信号量

信号量是内核维护的计数器,用于管理多进程之间共享资源。
编程模型:

进程A										进程B
创建信号量				semget	获取信号量
初始化信号量的值	semctl		...
加减信号量				semop		加减信号量
删除信号量				semctl		...

注意:信号量是用来计数的,一定要与资源对应。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值