目录
前面一篇讲述了进程间通信的两种方式(管道、信号),管道又分为有名管道换句话说就是在进程工作环境下可以看见一个创建的管道文件,而无名管道看不见这个管道文件。使用信号的方式有几种方式,一种是使用shell指令发送指定信号,如果没有使用更改执行状态一般默认操作。进程中可以使用kill函数给出指定的进程发送信号完成相关的动作,除了使用kill函数还可以使用raise函数给本进程发送信号,定时器发送信号等等
一、共享内存
(1)创建、打开共享内存
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size,int shmflg);
参数:
key:ftok得到的key值或者IPC_PRIVATE(创建私有的共享内存)
size:共享内存大小
shmflg:一般填IPC_CREAT | 0664
返回值:
成功返回共享内存ID,失败返回-1
(2)内存映射 -- shmat
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid,const void *shmaddr,int shmflg);
参数:
shmid:共享内存的ID
void *shmaddr:表示用户指定的映射地址,如果填NULL,表示该映射地址由系统自动分配
shmflg:SHD_RDONLY --- 只读
0 --可读可写
返回值:成功返回映射地址,失败返回-1
(3)取消内存映射
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
参数:
shmaddr:映射地址
返回值:
成功返回0,失败返回-1
(4)删除共享内存
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid,int cmd,struct shmid_ds*buf);
参数:
shmid:共享内存id
cmd:
IPC_STAT:获取共享内存信息
IPC_SET:设置共享内存
IPC_RMID:删除共享内存,第三个填NULL
返回值:
失败返回-1
例子:利用共享内存,一个进程向共享内存写入数据,另一个进程打印输出
/******************************
****************读取信息*******/
#include <stdio.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
key_t key = ftok(".", 'q');
if(key < 0)
{
perror("ftok");
exit(-1);
}
//1、创建、打开共享内存
int shmid = shmget(key, 512, IPC_CREAT|0664);
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
//2、内存映射
void *addr = shmat(shmid, NULL, 0);
if(addr == (void *)-1)
{
perror("shmat");
exit(-1);
}
//3、从共享内存中读取数据打印输出
while(1)
{
printf("%s", (char *)addr);
}
//4、取消映射
int ret = shmdt(addr);
if(ret < 0)
{
perror("shmdt");
exit(-1);
}
//5、删除共享内存
ret = shmctl(shmid, IPC_RMID, NULL);
if(ret < 0)
{
perror("shmctl");
exit(-1);
}
return 0;
}
//发送信息
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
key_t key = ftok(".", 'q');
if(key < 0)
{
perror("ftok");
exit(-1);
}
//1、创建、打开共享内存
int shmid = shmget(key, 512, IPC_CREAT | 0664);
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
//2、内存映射
void *addr = shmat(shmid, NULL, 0);
if(addr == (void *)-1)
{
perror("shmat");
exit(-1);
}
//3、向共享内存中写入数据
while(1)
{
fgets((char *)addr, 512, stdin);
}
//4、取消映射
int ret = shmdt(addr);
if(ret < 0)
{
perror("shmdt");
exit(-1);
}
return 0;
}
二、消息队列
消息队列可以根据消息类型,选择性的接收和发送
消息队列的操作流程
1.创建、打开消息队列 -----msgget
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
参数:
key:ftok得到的key值,或者填IPC_PRIVATE
msgflg:IPC_CREAT|0664
返回值:
成功返回消息队列id,失败返回-1;
2.发送消息 -------msgsnd
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数:
msqid:消息队列id
msgp:消息结构体的地址
msgsz:消息正文的大小
msgflg:
0 -- 以阻塞方式发送
IPC_NOWAIT -- 以非阻塞方式发送
返回值:
成功返回0,失败返回-1;
封装消息结构体:
typedef struct msgbuf
{
long mtype;
char mtext[64];
}MSG;
#define SIZE (sizeof(MSG)-sizeof(long))
MSG mbuf;
mbuf.mtype = 100;
fgets(buf, 64, stdin);
strcpy(mbuf.mtext, buf);
msgsnd(msgid, &mbuf, SIZE, );
3.接收消息队列 --------msggrcv
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
4、删除消息队列 ------msgctl
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
例子:
这里是利用消息队列创建一个接收和发送消息:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
typedef struct msgbuf //定义消息结构体
{
long mtype;
char mtext[64];
}MSG;
#define typeA 100
#define typeB 200
#define SIZE (sizeof(MSG)-sizeof(long))
int main(int argc, char *argv[])
{
MSG m_buf;
key_t key = ftok("/", 'a'); //获取key值
if(key < 0)
{
perror("ftok");
exit(-1);
}
int msgid = msgget(key, IPC_CREAT|0664); //创建或者打开消息队列
if(msgid < 0)
{
perror("msgget");
exit(-1);
}
int ret = 0;
char buf[64] = {0};
while(1)
{
m_buf.mtype = typeA; //填充消息结构体的类型
printf("input>>:");
fgets(buf, 64, stdin);
strcpy(m_buf.mtext, buf); //填充消息结构体的正文
ret = msgsnd(msgid, &m_buf, SIZE, 0); //发送消息
if(ret < 0)
{
perror("msgsnd");
exit(-1);
}
if(strcmp(buf, "quit\n") == 0)
{
printf("bye~\n");
break;
}
memset(&m_buf, 0, sizeof(MSG)); //清空消息结构体
ret = msgrcv(msgid, &m_buf, SIZE, typeB, 0); //接受消息类型为typeB的消息
if(ret < 0)
{
perror("msgrcv");
exit(-1);
}
if(strcmp(m_buf.mtext, "quit\n") == 0)
{
printf("bye~\n");
msgctl(msgid, IPC_RMID, NULL);
break;
}
printf("%s\n", m_buf.mtext); //打印接收内容
}
return 0;
}
三、信号灯集
1.创建、打开信号灯集---semget
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
参数:
key :和信号灯集关联的key值
nsems:信号灯集中包含的信号灯数目
semflg:信号灯集的访问权限,通常为IPC_CREAT | 0666
返回值:
成功:信号灯集ID
失败:-1
2.初始化信号灯集---semctl
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
参数:
semid:信号灯集ID
semnum:操作信号灯的编号,下标从0开始
cmd:操作信号灯的指令
SETVAL:设置信号灯的初值
例:
union semun
{
int val;
};
union semun my_un;
my_un.val = 1;
semctl(semid, 0, SETVAL, my_un);
3.p/v操作 --- semop
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
参数:
semid:信号灯集id
struct sembuf{
unsigned short sem_num; /* 要操作的信号灯编号 */
short sem_op; /*
//0:等待,知道信号灯的值变成0
//1:释放资源,V操作
//-1:分配资源,P操作
*/
short sem_flg; /* 操作标志:0,IPC_NOWAIT,SEM_UNDO */
};
nsops:要操作的信号灯个数
返回值:成功为0
失败为-1
4.删除信号灯集 ---semctl
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
参数:
semid:信号灯集D
semnum:要修改的信号灯编号
cmd: GETVAL:获取信号灯的值
SETVAL:设置信号灯的值
IPC_RMID:从系统中删除信号灯集合
semctl(semid, 0, IPC_RMID);
例子:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <signal.h>
union semun {
int val;
};
void sem_init(int semid, int num[], int n)
{
union semun mymun;
int i = 0;
for(i = 0; i < n; i++) //循环给信号灯集中的信号依次赋初始值
{
mymun.val = num[i];
semctl(semid, i, SETVAL, mymun);
}
}
void pv(int semid, int num, int op)
{
struct sembuf buf;
buf.sem_num = num;
buf.sem_op = op;
buf.sem_flg = 0;
semop(semid, &buf, 1);
}
int main(int argc, char *argv[])
{
int shmid, semid;
int num[2] = {0,1};
//key_t key;
pid_t pid;
char *shmaddr;
/*
if((key = ftok(".", 't')) < 0)
{
perror("ftok");
exit(-1);
}
*/
if((shmid = shmget(IPC_PRIVATE, 512, 0666 )) < 0) //创建私有的共享内存
{
perror("shmget");
exit(-1);
}
if((shmaddr = (char *)shmat(shmid, NULL, 0)) == (char *)-1) //共享内存映射
{
perror("shmat");
goto _error1;
}
if((semid = semget(IPC_PRIVATE, 2, 0666)) < 0) //创建私有的信号灯集
{
perror("semget");
goto _error1;
}
sem_init(semid, num, 2); //信号灯集的初始化
if((pid = fork()) < 0) //创建子进程
{
perror("fork");
goto _error2;
}
else if(pid == 0) //操作子进程
{
char *p, *q;
while(1)
{
pv(semid, 0, -1); //对第一个信号灯做P操做 此时第一个信号灯的值为0 所以阻塞在这一步,如果该信号灯的值为1 则会执行后面的程序
p = shmaddr;
q = shmaddr;
while(*p != '\0') //处理共享内存中字符串中的空格
{
if(*p != ' ')
{
*q++ = *p;
}
p++;
}
*q = '\0';
printf("%s", shmaddr); //打印共享内存中的内容
pv(semid, 1, 1); //对第二个信号灯做V操作, 让第二个信号灯的值+1
}
}
else //操作父进程
{
while(1)
{
pv(semid, 1, -1); //对第二个信号灯做P操作 此时第二个信号灯的初始值为1,执行之后第二个信号灯的值-1
printf("input > ");
fgets(shmaddr, 64, stdin); //向共享内存中写入数据
if(strcmp(shmaddr, "quit\n") == 0)
{
break;
}
pv(semid, 0, 1); //对第一个信号灯做V操作,此时第一个信号灯的初始值为0,此操作之后值+1
}
kill(pid, SIGUSR1); //向子进程发送SIGUSR1 这个信号的默认处理方式是结束该进程
}
_error2:
semctl(semid, 0, IPC_RMID);
_error1:
shmctl(shmid, IPC_RMID, NULL);
return 0;
}