消息队列
消息队列:默认发送端将信息放在前一个信息的后面,接收消息端可以指定接受哪一个消息。
1、msgget:创建和打开一个消息队列
int msgget(key_t key, int msgflg)
键值,这个键值就可以创建不同进程的消息对列。
参数1:键值;参数2:权限有关
ftok获得特定的键值。
key_t ftok(const char *pathname, int proj_id);
参数1:路径;参数2:至少是一个char的子符(非零)
2、msgsnd发送消息到消息队列
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数1:消息队列id;参数2:消息发送的信息结构体;参数3:大小(); 参数4:阻塞(0)或非阻塞(IPC_NOWAIT)
struct msgbuf
{
long mtype; /* message type, must be > 0 */
char mtext[1]; /* message data */
}
3、msgrcv 接受消息从消息队列
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
有用的信息将来会放在msgp这个结构体的元素(数组)里。
msgflag 也是阻塞读取,意思就是有有消息就一直读
msgtyp,选择0,就是默认先读取第一个消息。
4、消息队列是在内核中维护的,用完之后,自己清理
msgctl
参数1:qid;参数2:IPC_RMID;参数:NULL(不关心详细的队列特性)
题目:进程A 和 进程B,通过共享内存进行通信。
PS:shmget注意是创建还是打开(共享内存)
//完成了发送端的程序
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define SIZE 200
struct msgbuf
{
//消息的一个类型(>0)
long mtype;
//真实的消息
char mtext[SIZE];
};
int main(void)
{
key_t key = ftok(".", 'a');
struct msgbuf msg = {0};
struct msgbuf msg1 = {123, "computer"};
int ret = -1;
if (key < 0)
{
perror("ftok");
exit(0);
}
else
{
printf("创建键值成功.\n");
}
//创建和打开一个消息队列
int qid = msgget(key, IPC_CREAT | 0666);
if (qid < 0)
{
perror("msgget");
exit(0);
}
printf("打开了一个消息队列qid = %d.\n", qid);
while (1)
{
//填充结构体
msg.mtype = getpid();
printf("输入消息:\n");
fgets(msg.mtext, SIZE, stdin);
//添加消息到消息队列,进行排队
ret = msgsnd(qid, &msg1, SIZE, 0);
if (ret < 0)
{
perror("msgsnd1");
exit(0);
}
ret = msgsnd(qid, &msg, SIZE, 0);
if (ret < 0)
{
perror("msgsnd2");
exit(0);
}
//结束我们进程
if (strncmp(msg.mtext, "quit", 4) == 0)
break;
}
return 0;
}
共享内存
共享内存:从真实的物理空间找一段内存,映射到我们的虚拟内存,在不同进程间的映射内存都以为这段内存是自己的,这就牵扯到同步。
1、从物理地址得到确定大小的内存
int shmget(key_t key, size_t size, int shmflg);
参数1:特殊键值 参数2:获取内存大小 3:内存的权限
PS:ipcs -m 专门可以查看共享内存的详细信息。
2、删除共享内存
shmctl
3、建立共享
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数2:shmaddr == NULL,表示操作OS自动非配一个未使用而且安全的地址。
参数3shmflag:选则IPC_RDONLY只读模式,猜测选0是可读可写?
4、断开共享内存和所处进程之间的映射关系
int shmdt(const void *shmaddr);
参数:里面放的是,所处进程中映射过去的有效、合法、未使用地址
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define SIZE 2048
int main(void)
{
#if 1
int shmid = 0;
char *ptr = NULL;
char flag[] = "i am a flag!";
char buf[1024] = {0};
pid_t pid;
//创建共享内存
shmid = shmget(IPC_PRIVATE, SIZE, 0666);
if (shmid < 0)
{
perror("shmget");
exit(0);
}
else
{
printf("创建申请的shm_memory:%d.\n", shmid);
}
//查看共享内存使用情况
// system("ipcs -m");
//创建进程
pid = fork();
if (pid < 0)
{
perror("fork");
exit(0);
}
if (pid == 0)
{
// 子进程
//映射内存
ptr = shmat(shmid, NULL, 0);
if (ptr == (void *)-1)
{
perror("子进程shmat");
exit(0);
}
else
{
printf("子进程映射的地址:%p.\n", ptr);
}
//读父进程的flag是否写入
while (strncmp(ptr, flag, strlen(flag)))
{
printf("子进程等有效数据\n");
sleep(5);
}
//读取父进程有效数据
// strcpy(buf, ptr+strlen(flag));
// printf("子进程得到有效数据[%s].\n", buf);
struct infor
{
char name[20];
int age;
int money;
}s = {0};
//接受结构体信息
s = *((struct infor *)(ptr+strlen(flag)));
printf("名字:%s, 年龄:%d, 存款:%d.\n", s.name, s.age, s.money);
//子进程分离共享内存
if (shmdt(ptr) < 0)
{
perror("子进程ptr");
exit(0);
}
else
printf("子进程分离OK!\n");
// system("ipcs -m");
//删除共享内存
if (shmctl(shmid, IPC_RMID, NULL) < 0)
{
perror("子进程删除内存错误\n");
exit(0);
}
printf("子进程删除成功\n");
}
if (pid > 0)
{
// 父进程
//映射内存
ptr = shmat(shmid, NULL, 0);
if (ptr == (void *)-1)
{
perror("父进程shmat");
exit(0);
}
else
{
printf("父进程映射的地址:%p.\n", ptr);
}
printf("父进程写东西:\n");
//写正式内容
// fgets(buf, SIZE, stdin);
struct infor
{
char name[20];
int age;
int money;
}s = {"小丽", 16, 500000000};
// strncpy(ptr+strlen(flag), buf, strlen(buf));
memcpy((struct infor*)(ptr+strlen(flag)), &s, sizeof(s));
strncpy(ptr, flag, strlen(flag));
//父进程分离共享内存
if (shmdt(ptr) < 0)
{
perror("父进程ptr");
exit(0);
}
else
printf("父进程分离OK!\n");
// system("ipcs -m");
//父进程等待子进程结束
waitpid(pid, NULL, 0);
printf("所犯的罪过一切结束了\n");
}
#endif
/*
shmctl(5570615, IPC_RMID, NULL);
shmctl(5603388, IPC_RMID, NULL);
shmctl(5636157, IPC_RMID, NULL);
system("ipcs -m");
*/
// shmctl(shmid, IPC_RMID, NULL);
return 0;
}
信号
信号:是一种异步的通信方式,通信内容有限制。
信号:都是事先编好的一系列int数据
信号产生方式:
(1)硬件产生
(2)满足某种软件需求
(3)硬件异常发生信号
(4)kill -9
信号的处理方式:
给谁发的,谁处理
(1)默认处理
(2)捕获处理 (信号绑定了一个函数)
(3)忽略处理
常见的信号:(路径:/usr/include/bits/signum.h)
(1)SIGINT 2 就是平时用到的ctrl+c
(2)SIGABRT 6 程序异常终止
(3)SIGKILL 9 杀死一个进程(终极方法)
(4)SIGSEGV 11 无效访问内存
(5)SIGALRM 14 闹钟信号
(6)SIGCHLD 17 子进程结束时发送的信号
(7)SIGSTOP 19 杀死(暂停)一个进程ctrl+\
1、安装信号函数
raise发送一个信号给当前进程
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
int main(void)
{
pid_t pid;
int ret = -1;
pid = fork();
if (pid < 0)
{
perror("fork");
exit(0);
}
if (pid == 0)
{
sleep(1);
//子进程
printf("子进程等待别的信号唤醒自己pid = %d:\n", getpid());
raise(SIGSTOP);
printf("子进程醒了.\n");
exit(0);
}
if (pid > 0)
{
// 父进程
// sleep(3);
if (waitpid(pid, NULL, WNOHANG) == 0)
{
//
if ((ret = kill(pid, SIGCONT)) == 0)
{
printf("父进程杀了子进程pid = %d.\n", pid);
}
}
waitpid(pid, NULL, 0);
printf("一切结束.\n");
}
//一个进程发两个信号
// while (1);
/*
// int i = 4 / 0;
int i;
printf("i = %d.\n", i);
while (1);
*/
return 0;
}