进程间通信
信号
kill -l
显示常用信号列表
SIGKILL
SIGSTOP
两个信号不能被忽略
SYNOPSIS
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
@signal:信号number
@handler: 传进去一个自定义处理函数,int参数是信号编号
- SIG_IGN:忽略
- SIG_DFL:默认处理,具体查看signal(7)
This signal() facility is a simplified interface to the more general sigaction(2) facility.
如果需要传进参数 可以使用sigaction(2)(如果需要使用更多特性)
RETURN
返回上一次的函数指针,如果失败则SIG_ERR会被返回,errno会被设定
Example
子进程退出,父进程 接受到信号,回收子进程。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
// 信号处理函数
void signal_fun(int a)
{
int status;
printf("收到信号\n");
//回收子进程,WNOHANG无阻塞
waitpid(-1,&status,WNOHANG);
if (WIFEXITED(status))
{
printf("子进程正常退出\n");
}
}
int main(int argc, char const *argv[])
{
// 子进程退出时候会向父进程发送SIGCHLD信号
if(signal(SIGCHLD,signal_fun)==SIG_ERR)
{
perror("signal failure");
exit(EXIT_FAILURE);
}
int pid=fork();
if (pid<0)
{
perror("fork failure"); exit(EXIT_FAILURE);
}
// 子进程
if (pid==0)
{
printf("this is son\n");
exit(EXIT_SUCCESS);
}
// 父进程
while(1)
{
sleep(1);
}
return 0;
}
简单处理方法:显示声明忽略SINGHLD信号
signal(SIGCHLD,SIG_IGN)
IPC对象
消息队列
一 IPC 对象 ---- 消息队列
IPC 对象命令
查看系统中IPC对象
- ipcs -a 显示所有的IPC对象
- ipcs -s/-q/-m
删除系统中的IPC对象
- ipcrm -q/-s/-m ID
第一步:获得key值
key_t ftok(const char *pathname, int proj_id);
参数:
@pathname 已经存在的文件路径
@proj_id 获取这个整数的低8bit
返回值:
成功返回 key值,失败返回-1
第二步:创建IPC对象
int msgget(key_t key, int msgflg);
参数:
@key IPC_PRIVATE(用户亲缘关系进程间通信) 或 ftok函数产生key值
@msgflg IPC_CREAT | 0666 或 IPC_CREAT | IPC_EXCL | 0666 (判断IPC对象是否存在)
返回值:
成功返回ID,失败返回-1
注意:
如果对应key值的IPC对象不存在,则创建,如果存在,直接返回IPC对象的ID
第三步.发送消息
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数
@msqid 消息队列ID
@msgp 需要发送的消息存放的地址
@msgsz 消息正文的大小
@msgflg 0:阻塞的方式发送 IPC_NOWAIT:非阻塞方式调用
返回值:
成功返回0,失败返回-1
结构体体
typedef struct{
long msg_type; //消息类型必须在第一个位置,
char mtxt[1024];
...
}msg_t;
第四步.接收消息
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
参数:
@msqid 消息队列ID
@msgp 存放接收到的消息
@msgsz 正文大小
@msgtyp 消息类型 , 0: 总是从消息队列中提取第一个消息
@msgflg 0:阻塞的方式接收 IPC_NOWAIT:非阻塞方式调用
返回值:
成功返回 接收消息正文大小,失败返回-1
第五步.删除消息队列
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数:
@msgqid 消息队列
@cmd IPC_RMID(删除消息队列) IPC_SET(设置消息队列的属性信息) IPC_STAT(获取消息队列属性信息)
@buf 存放消息队列属性
返回值:
成功返回0,失败返回-1
Example
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
static void error_judge(int ret,char* mes);
typedef struct msgtype
{
long msg_type;
char msg_data[512];
}MSGTYPE;
int main(int argc, char const *argv[])
{
MSGTYPE sen_mes={100,"hello zhaohe!"};
MSGTYPE rec_mes;
key_t key=ftok("./msg_ipc_client.c",'k');
error_judge(key,"ftok failure");
int msqid=msgget(key,IPC_CREAT|0666);
error_judge(msqid,"msgget failure");
printf("发送的信息是%s 编号是:%ld \n",sen_mes.msg_data,sen_mes.msg_type);
int ret=msgsnd(msqid,&sen_mes,sizeof(MSGTYPE)-sizeof(long),0);
error_judge(ret,"msgsnd failure");
ret=msgrcv(msqid,&rec_mes,sizeof(MSGTYPE)-sizeof(long),100,0);
error_judge(ret,"msgsnd failure");
printf("收到的信息是%s 编号是:%ld\n",rec_mes.msg_data,rec_mes.msg_type);
int msgctl(msqid,IPCRMID,NULL);
return 0;
}
static void error_judge(int ret,char* mes)
{
if (ret<0)
{
perror(mes);
exit(EXIT_FAILURE);
}
}
共享内存 :内核空间预留出来的一块内存,用于进程间通信
第一步
int shmget(key_t key, size_t size, int shmflg);
功能:获取共享内存段的ID
参数:
@key IPC_PRIVATE 或 ftok()
@size 申请的共享内存段大小 [4k的倍数]
@shmflgIPC_CREAT | 0666
或IPC_CREAT | IPC_EXCL
有则打开,没有则创建 返回shemid |第二标示表示 没有创建,但当文件存在的时候返回 -1并将errno设置为EEXIST
返回值:
成功返回ID,失败返回-1
第二步
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:映射共享内存到用户空间
参数:
@shmid 共享内存段ID
@shmaddr NULL:系统自动完成映射
@shmflg SHM_RDONLY:只读 0:读写
返回值:
成功返回映射后的地址,失败返回(void *)-1
其他
int shmdt(const void *shmaddr);
功能:撤销映射
参数:@shmaddr 共享内存映射的地址
注意:当一个进程结束的时候,它映射共享内存,会自动撤销映射
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:根据命令控制共享内存
参数:
@shmid 共享内存段的ID
@cmd
IPC_STAT
获取属性
IPC_SET
设置属性
IPC_RMID
删除IPC对象
@buf 保存属性
返回值:
成功返回0,失败返回 -1
注意:当我们调用shmctl删除共享内存的时候,并不会立即删除。只有当共享内存映射 次数为0,才会删除共享内存对象
Example
描述
两个进程一端发送信息,另一端接受信息,通过共享空间实现信息交互,使用两个char的变量模拟信号量进行同步。对模拟信号量的初始化,这里约定有空间空间的创建者进行初始化
写端代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include <sys/shm.h>
#include <string.h>
#define SHM_SIZE (4096*1)
#define BUFF_SIZE (SHM_SIZE-2*sizeof(char))
static void error_judge(int ret,char* mes);
typedef struct msgtype
{
char w_flag;
char r_flag;
char buff[SHM_SIZE-2*sizeof(char)];
}MSGTYPE;
int main(int argc, char const *argv[])
{
//构建一个地址,之后接受共享地址
MSGTYPE *sen_mes;
int flags=0;
// 1.首先创建一个key
key_t key=ftok("./msg_ipc_client.c",'a');
error_judge(key,"ftok failure");
// 2.向内核申请共享空间
int shmid=shmget(key,SHM_SIZE,IPC_CREAT|IPC_EXCL|0666);
if (shmid<0&&errno!=EEXIST)
{
perror("shmget failure");
exit(EXIT_FAILURE);
}
if (shmid<0&&errno==EEXIST)
{
shmid=shmget(key,SHM_SIZE,IPC_CREAT|0666);
flags=1;
}
// 3.获得共享空间的在用户空间的地址映射
sen_mes=shmat(shmid,NULL,0);
// 初始化信号量 这里是模拟信号量 用来同步两个进程之间的读写顺序 占用cpu资源不推荐
if (flags==0)
{
sen_mes->w_flag=1;
sen_mes->r_flag=0;
}
printf("shmid is %d w_flag is %d r_flag is %d\n",shmid,sen_mes->w_flag,sen_mes->r_flag);
// sen_mes=shmat(shmid,NULL,0);
// 4.之后就可以像操作地址空间一样操作共享内存
while(1)
{
if (sen_mes->w_flag==1)
{
fgets(sen_mes->buff,BUFF_SIZE,stdin);
sen_mes->w_flag=0;
sen_mes->r_flag=1;
if (strcmp(sen_mes->buff,"quit\n")==0)
{
break;
}
}
}
printf("程序结束\n");
// 5。释放映射
shmdt(sen_mes);
// 6.由创造者删除共享内存
if (flags==0)
{
shmctl(shmid,IPC_RMID,NULL);
}
return 0;
}
static void error_judge(int ret,char* mes)
{
if (ret<0)
{
perror(mes);
exit(EXIT_FAILURE);
}
}
读端代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include <sys/shm.h>
#include <string.h>
#define SHM_SIZE (4096*1)
#define BUFF_SIZE (SHM_SIZE-2*sizeof(char))
static void error_judge(int ret,char* mes);
typedef struct msgtype
{
char w_flag;
char r_flag;
char buff[SHM_SIZE-2*sizeof(char)];
}MSGTYPE;
int main(int argc, char const *argv[])
{
MSGTYPE *sen_mes;
int flags=0;
key_t key=ftok("./msg_ipc_client.c",'a');
error_judge(key,"ftok failure");
int shmid=shmget(key,SHM_SIZE,IPC_CREAT|IPC_EXCL|0666);
if (shmid<0&&errno!=EEXIST)
{
perror("shmget failure");
exit(EXIT_FAILURE);
}
if (shmid<0&&errno==EEXIST)
{
shmid=shmget(key,SHM_SIZE,IPC_CREAT|0666);
flags=1;
}
sen_mes=shmat(shmid,NULL,0);
if (shmid==0)
{
sen_mes->w_flag=1;
sen_mes->r_flag=0;
}
printf("shmid is %d w_flag is %d r_flag is %d\n",shmid,sen_mes->w_flag,sen_mes->r_flag);
while(1)
{
if (sen_mes->r_flag==1)
{
printf("I have read:%s",sen_mes->buff);
if (strcmp(sen_mes->buff,"quit\n")==0)
{
break;
}
sen_mes->w_flag=1;
sen_mes->r_flag=0;
}
}
printf("程序结束\n");
shmdt(sen_mes);
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
static void error_judge(int ret,char* mes)
{
if (ret<0)
{
perror(mes);
exit(EXIT_FAILURE);
}
}
信号灯
头文件
#include <sys/ipc.h>
#include <sys/sem.h>
(1)创建信号灯集
int semget(key_t key, int nsems, int semflg);
参数:
@key IPC_PRIVATE , ftok()
@nsems 信号灯集中信号灯的个数
@semflg 常用IPC_CREAT | 0666
,IPC_CREAT | IPC_EXCL| 0666
有则打开,没有则创建 返回shemid |第二标示表示 没有创建,但当文件存在的时候返回 -1并将errno设置为EEXIST
返回值:
成功返回ID,失败返回-1
(2)初始化信号灯集中信号灯的值
int semctl(int semid, int semnum, int cmd, ...);
参数:
@semid 信号灯集的ID
@semnum 信号灯的编号[编号从0开始]
@cmdSETVAL
[设置信号灯的值] ,GETVAL
(获取信号灯的值),IPC_RMID
[删除信号灯集]
返回值:
成功返回0,失败返回-1
(3)PV操作
int semop(int semid, struct sembuf *sops, unsigned nsops);
功能:完成PV操作
参数:
@semid 信号灯集的ID
@sops 操作方式结构体首地址
@nsops 操作信号灯的个数
返回值:
成功返回0,失败返回-1
struct sembuf
{
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
};
sem_num :
信号集信号编号
sem_op :
<1>
0
等待信号灯的值变成0
<2>1
释放资源,V操作
<3>-1
申请资源,P操作
sem_flg:
0
: 阻塞方式
IPC_NOWAIT
: 非阻塞方式调用
SEM_UNDO
: 进程结束的时候,它申请的资源自动释放
Example
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
static void error_judge(int ret,char* mes);
void p(int sem_id,int sem_num);
void v(int sem_id,int sem_num);
void release(int sem_id);
void prf_sem(int sem_id,int sem_num);
int main(int argc, char const *argv[])
{
key_t key=ftok("./msg_ipc_client.c",'a');
error_judge(key,"ftok failure");
// int semget(key_t key, int nsems, int semflg);
//根据key数值创建一个包含两个信号的信号集
int sem_id=semget(key,2, IPC_CREAT | 0666);
error_judge(sem_id,"semget failure");
// int semctl(int sem_id, int semnum, int cmd, ...);
// 将第一个信号的数字初始化为1
int ret=semctl(sem_id, 0, SETVAL,1);
error_judge(ret,"semctl failure");
prf_sem(sem_id,0);
// 将第一个信号的数字初始化为2
ret=semctl(sem_id, 1, SETVAL,2);
error_judge(ret,"semctl failure");
prf_sem(sem_id,1);
p(sem_id,0);
prf_sem(sem_id,0);
v(sem_id,0);
prf_sem(sem_id,0);
p(sem_id,1);
prf_sem(sem_id,1);
p(sem_id,1);
prf_sem(sem_id,1);
// p(sem_id,0);
printf("this is 信号集实验\n");
release(sem_id);
return 0;
}
void p(int sem_id,int sem_num)
{
struct sembuf sem;
sem.sem_num = sem_num;
sem.sem_op = -1;
sem.sem_flg = 0;
if(semop(sem_id,&sem,1) < 0)
{
release(sem_id);
error_judge(-1,"semop:fun_p");
}
}
void v(int sem_id,int sem_num)
{
struct sembuf sem;
sem.sem_num = sem_num;
sem.sem_op = 1;
sem.sem_flg = 0;
if(semop(sem_id,&sem,1) < 0)
{
release(sem_id);
error_judge(-1,"semop:fun_v");
}
}
static void error_judge(int ret,char* mes)
{
if (ret<0)
{
perror(mes);
exit(EXIT_FAILURE);
}
}
void prf_sem(int sem_id,int sem_num)
{
unsigned short sem_value=0;
sem_value=semctl(sem_id, sem_num, GETVAL,&sem_value);
printf("sem_id:%d num:%d value:%d\n",sem_id,sem_num,sem_value);
}
void release(int sem_id)
{
semctl(sem_id, 0,IPC_RMID);
}
网友评论