Linux系统编程中,进程通信常用的有六种:管道,信号,IPC通信中的共享内存、消息队列、信号量集
一、无名管道(只用于有亲缘关系的进程、并且无文件节点)
int pipe(int pipefd[2]) 创建一个无名管道
pipefd[0] 代表读 pipefd[1] 代表写
有读写阻塞
读阻塞:当一个进程试图从没有内容管道里面读取数据时,那么该进程被阻塞,直到管道里面具有内容。
写阻塞:管道具有一定缓存空间,当缓存被填满的时候,若进程试图往进程里面写数据,那么该进程会被阻塞,直到管道中的数据没有被填满。
二、有名管道(有文件节点)
1、可以通过mkfifo函数打开一个有名管道,也可以直接当作命令行来创建
2、只能通过文件io进行读写
3、有读写阻塞
三、标准流管道(只能通过标准io读写)
FILE *popen(const char *command,const char *type); 打开一个标准流管道
int pclose(FILE *stream) 关闭一个标准流管道
四、信号
kill -l 查看所有信号
信号9和信号19不能被更改
较常使用的信号:
SIGINT:用户按下组合 Ctrl 键+c 键时,向正在运行的进程发送一则信号。默认动作为终止进程。
SIGALRM:定时器超时,超时的时间由系统调用 alarm 设置。可以理解为闹钟,时间到了默认动作为终止进程。
jobs命令 可以查看现有暂停的任务
fg命令可以继续暂停的任务
//alarm()定时给自身发送闹钟信号 默认是结束进程
头文件:#include <unistd.h>
原型:unsigned int alarm(unsigned int senconds);
参数:unsigned int senconds ---》表示设定的时间(单位:秒)
若你设置为0,相当于取消设定的闹钟
sighandler_t signal(int signum,sighandler_t handler);信号处理函数
handler: 第一种:SIG_DFL 对信号采用默认处理方式
第二种:SIG_IGN 忽略该信号
第三种:信号处理函数的名称--void 函数名(int arg)
五、IPC通信
ipcs 显示所有的ipc对象
ipcs -q 消息队列
ipcs -m 共享内存
ipcs -s 信号量集
ipcrm -大写key -小写id 删除
(1)共享内存
基本单位是4k(4096bytes)
多进程使用共享内存的流程:
第一步:申请一块共享内存
第二步:多进程打开同一块共享内存
第三步:多进程将共享内存映射到自己的进程地址空间地址上。
第四步:多进程通过自己的映射区进行读写,完成通信(间接的使用物理空间的内存)
第五步:多进程取消共享内存在自身进程空间地址的映射。
第六步:删除共享内存
所需函数原型:
key_t ftok(const char *pathname,int proj_id);获取key值文件跟id可以随便写
int shmget(key_t key,size_t size,int shmflg);申请共享内存
成功则返回共享内存id
shmflg:IPC_CREAT | 0666 //创建并且设置读写权限,如果不给权限,返回的key值为0xffffffff没办法用
void *shmat(int shmid,const void *shmaddr,int shmflg); 内存映射到本进程的地址空间上
shmaddr:NULL 自动分配 shmflg:0 可读可写
返回内存应映射后的地址
int shmdt(const void *shmaddr);取消内存映射
int shmctl(int shmid,int cmd,struct shmid_ds *buf) 内存控制函数 现阶段主要用于删除内存映射
cmd:IPC_STAT(查看状态) IPC_SET(设置)IPC_RMID(删除)
需要取消内存映射后才可以删除
六、消息队列
有编号
先进先出
具有读阻塞
int msgget(key_t key,int msgflg);打开消息队列
msgflg:IPC_CREAT|0600
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); 消息队列发送函数
成功 返回消息队列的ID,失败的话 返回-1
msgflg:0(保留阻塞)|IPC_NOWAIT(无阻塞)|MAG_ERROR
第二个参数需要先定义:struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[20]; /* message data */
};
ssize_t msgrcv(int msqid , void *msgp,size_t msgsz,long msgtyp,int msgflg);消息队列接收函数
msgtyp:接收消息的编号
int msgctl(int msqid,int cmd,struct msqid_ds *buf);消息队列控制函数 现阶段主要用于删除
七、信号量集/信号灯
信号量是一个非负整数
PV操作是基于对信号量值进行的操作,当信号量值等于0,此进程再使用该信号量的时候会进入阻塞,
当信号量的值大于0时候,此进程使用该信号量才会继续执行。
int semget(key_t key, int nsems, int semflg);
int nsems :创建信号量集中的信号量数目,如果引用一个现存的信号量集合,
则将nsems 指定为0。
--也可以为当信号量集里面信号量的数量
int semflg:IPC_CREAT|0600
成功返回信号量集的id
int semctl(int semid, int semnum, int cmd, union semun arg);信号量集控制函数
.int semnum: 要操作的信号量集中的指定信号量编号即下标,编号从0开始,
当使用信号量集时才会被用到。
cmd:IPC_RMID:从系统中删除信号量。
IPC_GETVAL或GETVAL: 根据 semmun 指定的编号返回指定信号量的当前值,
此时该函数返回值就是你要获得的信号量的值,不再是 0 或-1)
IPC_SETVAL或SETVAL: 根据 semmun 指定的编号设定相应信号的值,修改的值需要使用第四个参数 union semun arg,然后要设置值放arg.val在)
简单来说就是将信号量值设置为arg的val值
GETALL(获取所有信号量的值,此时该函数第二个参数为0,
此时需要用到第四个参数union semun arg并且会将所有信号的值存入arg.array所指向的数组(unsigned short)
的各个元素中(每个元素都对应一个信号量的值))
SETALL(设置所有信号量的值,此时该函数第二个参数为 0,此时需要用到第四个参数union semun arg,
将 arg.array 指向的数组的所有元素的值一一对应设定到信号量时集中)
使用共用体时需要自己定义:
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) */
}
返回值:
成功,根据cmd的值的不同而返回不同的值。
cmd为IPC_STAT、IPC_SETVAL、IPC_RMID则函数返回0。
cmd为IPC_GETVAL则函数返回信号量的当前值。
错误: -1
int semop(int semid, struct sembuf *sops, unsigned nsops); 操作信号量集
第二个参数:struct sembuf
{
unsigned short sem_num; /* 信号量在信号量集中的编号 一般从0 开始,0表示第1个,1表示第2 个,nsems - 1表示最后一个。*/
short sem_op; /* 信号量变化值 PV操作*/
short sem_flg; /* 一组标志 直接给0值 */
sem_flg:信号量操作的属性标志 写0表示正常操作
}