1、进程间的沟通方式有哪几种?
1.1、管道
(管道我在上一篇为大家介绍了,有兴趣的朋友可以看看我的上一篇文章)
1.2、消息队列
1.2.1、定义:消息队列是一些消息的列表
1.2.2、消息队列的操作函数:
创建:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
添加:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, void *msgp, int msgsz, int msgflg);
读取:
#include <sys/types>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgrcv(int msgid, void *msgp, int msgsz, long msgtyp, int msgflg);
控制:
#include <sys/types>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msgid,int cmd,struct msqid_ds *buf);
1.2.3:消息队列的特点:
①:支持非亲缘进程间通信;
②:可以共享大量数据;
③:具有先进先出的特点;
④:可以提供优先级;
1.2.4:消息队列的操作:
①:创建消息队列;
②:写入数据到消息队列;
③:读出消息;
④:销毁消息队列;
1.2.5、实例实现:
//p1.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct msg_buf
{
long mtype;
char mtext[100];
};
int main()
{
//ftok
key_t key = ftok(".",3);
if(key == -1)
{
puts("ftok error.");
return -1;
}
//msgget
int msgid = msgget(key, IPC_CREAT | 0664);
if(msgid == -1)
{
puts("msgget error.");
return -1;
}
struct msg_buf msg;
//msgsnd
memset(&msg, 0, sizeof(msg));
msg.mtype = 1;
strcpy(msg.mtext,"PJZJDGG");
msgsnd(msgid, &msg, 100, 0);
return 0;
}
//p2.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct msg_buf
{
long mtype;
char mtext[100];
};
int main()
{
//ftok
key_t key = ftok(".",3);
if(key == -1)
{
puts("ftok error.");
return -1;
}
//msgget
int msgid = msgget(key, IPC_CREAT | 0664);
if(msgid == -1)
{
puts("msgget error.");
return -1;
}
struct msg_buf msg;
//msgsnd
memset(&msg, 0, sizeof(msg));
int ret = msgrcv(msgid, &msg, 100, 1, 0);
if(ret > 0)
{
puts(msg.mtext);
}
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
1.3、共享内存
1.3.1、共享内存的定义:共享内存是常用的进程间通信,两个进程可以直接共享访问同一块内存区域。
1.3.2、共享内存的特点:
①、访问高效(不需要从内核中复制数据到用户态(管道);不需要多次的系统调用);
②、容易产生静态问题,需要一种同步或者互斥机制来保证共享内存中数据的安全性;
③、可以使非亲缘进程通信;
1.3.3、
实现共享内存的步骤如下:
(1)、 创建内存共享区;
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
(2)、 映射共享内存到用户态;
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
(3)、通过映射出来的指针操作共享内存;
#include <sys/types.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
(4)、撤销内存映射关系;
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
1.3.4、共享内存的实例:
#include <stddef.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "shmdata.h"
int main(int argc, char **argv)
{
void *shm = NULL;
struct shared_use_st *shared; // 指向shm
int shmid; // 共享内存标识符
// 创建共享内存
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);
if (shmid == -1)
{
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}
// 将共享内存连接到当前进程的地址空间
shm = shmat(shmid, 0, 0);
if (shm == (void *)-1)
{
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}
printf("\nMemory attached at %X\n", (int)shm);
// 设置共享内存
shared = (struct shared_use_st*)shm; // 注意:shm有点类似通过 malloc() 获取到的内存,所以这里需要做个 类型强制转换
shared->written = 0;
while (1) // 读取共享内存中的数据
{
// 没有进程向内存写数据,有数据可读取
if (shared->written == 1)
{
printf("You wrote: %s", shared->text);
sleep(1);
// 读取完数据,设置written使共享内存段可写
shared->written = 0;
// 输入了 end,退出循环(程序)
if (strncmp(shared->text, "end", 3) == 0)
{
break;
}
}
else // 有其他进程在写数据,不能读取数据
{
sleep(1);
}
}
// 把共享内存从当前进程中分离
if (shmdt(shm) == -1)
{
fprintf(stderr, "shmdt failed\n");
exit(EXIT_FAILURE);
}
// 删除共享内存
if (shmctl(shmid, IPC_RMID, 0) == -1)
{
fprintf(stderr, "shmctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
1.4、信号
1.4.1、信号是什么?
①、信号是进程间通信的一种手段;
②、是一种事件触发机制;
③、是一种异步通信(是进程间通信机制中唯一一种异步手段)
ps: 同步:是一种按照顺序依次执行的过程,若其环节被阻塞,则整个过程会被阻塞;
异步:是一种使用信号或者中断来实现一种事件的触发过程,若没有信号或中断产生,并不会影响程序的正常执行;
④、信号可以由一个进程发送,另一个进程捕获,阻塞和忽略;
ps: 捕获:收到信号后会执行相应的动作;
阻塞: 会阻塞信号的传递,直至解除阻塞,才会继续传递;
忽略:收到信号后,进程不会做出 任何反应和处理;
1.4.2、常用的信号的特点:
①、信号无法传递大量数据;
②、信号可以在任意进程间通信;
③、信号是一种异步触发手段;
1.4.3、常用的信号列举:
SIGHUP: 1号信号,(在控制终端上挂起信号,或让进程结束);
SIGINT: 2号信号,(键盘输入中断,ctrl + c );
SIGQUIT: 3号信号,(键盘输入退出,ctrl+ | );
SIGABRT:6号信号,(非正常终止,double free);
SIGKILL: 9号信号,(杀死进程信号),该信号不能被阻塞、忽略、自定义处理;
SIGSEGV:11号信号,(无效的内存引用,解引用空指针、内存越界访问);
SIGPIPE:13号信号,(管道中止: 写入无人读取的管道,会导致管道破裂);
SIGCHLD:17号信号,(子进程发送给父进程的信号,但该信号为忽略处理的);
SIGSTOP:19号信号,(停止进程);
1.4.4、信号的操作
①、信号的发送
②、信号的捕获
1.4.5、信号操作实例:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
typedef void (*sighandler_t)(int);
//捕获函数
void func(int sig)
{
if(SIGINT != sig)
{
return;
}
printf("func for signal: %d\n",sig);
}
int main(void)
{
sighandler_t ret = (sighandler_t)-2;
//sighandler_t signal(int signum, sighandler_t handler);函数原型
//signal(SIGINT,func);
//signal(SIGINT,SIG_DFL); //让信号默认处理
ret = signal(SIGINT,SIG_IGN); //忽略处理
//ret = signal(SIGKILL,SIG_IGN);
printf("返回值为:%d\n",(int)ret);
if(SIG_ERR == ret) //判断信号处理是否成功
{
perror("signal");
exit(-1);
}
printf("before while(1)\n");
while(1);
printf("after while(1)\n");
return 0;
}
1.5、信号量
1.5.1、定义:它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据;
1.5.2、信号量的特点:
①、是一种同步机制(保证访问临界资源的有序性);
②、不能实现大量的数据共享;
③、支持非双亲缘进程间通信;
④、信号量可以由一个进程申请,其他进程释放;
1.5.3、操作信号量的流程:
①、创建或者获取信号值;
②、获得信号值当前的数值;
③、判断当前信号量的值是否是自己能访问的;
④、如果是自己可以操作的状态:(申请信号量资源(p操作);执行临界区代码;如果需要释放资源再释放(v操作))
ps:p原子操作:信号量的值减1(只有当信号量的值大于0时,才能成功),描述的是申请资源的过程,申请成功后,会操作临界区的代码,当信号量的值为0时,p操作会被阻塞,直至信号量值不为0,解除阻塞;
v原子操作:信号量值加1;
1.5.4、信号量操作的函数
(1)、信号量的获取:sem_open;
(2)、获得信号量的值:sem_getvalue;
(3)、p操作:sem_wait;
(4)、v操作:sem_post;
(5)、关闭信号量:sem_break;
1.5.5:关于信号量操作的实例:
//p1.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <semaphore.h>
int main()
{
//ftok()
key_t key = ftok(".",2);
if(key == -1)
{
puts("ftok error.");
return -1;
}
//shm_get()
int shmid = shmget(key, 100, IPC_CREAT | 0664);
if(shmid == -1)
{
puts("shmget error.");
return -1;
}
//sem_open()
sem_t *sem = NULL;
sem = sem_open("mysem", O_CREAT | O_RDWR, 0664, 3);
if(NULL == sem)
{
puts("sem_open error.");
return -1;
}
//shmat()
volatile char *shmaddr = NULL;
shmaddr = shmat(shmid, shmaddr, 0);
if(NULL == shmaddr)
{
puts("shmat error.");
sem_close(sem);
return -1;
}
//do_work
while(1)
{
int sval = 0;
sem_getvalue(sem, &sval);
//printf("sval:%d\n",sval);
if(sval == 3)
{
sem_wait(sem);
char buf[] = "准备包装盒";
memset(shmaddr, 0, 100);
memcpy(shmaddr, buf, sizeof(buf));
if(strcmp(buf, shmaddr) == 0)
{
puts(shmaddr);
}
}
}
//shmdt()
shmdt(shmaddr);
//sem_close()
sem_close(sem);
return 0;
}
//p2.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <semaphore.h>
int main()
{
//ftok()
key_t key = ftok(".",2);
if(key == -1)
{
puts("ftok error.");
return -1;
}
//shm_get()
int shmid = shmget(key, 100, IPC_CREAT | 0664);
if(shmid == -1)
{
puts("shmget error.");
return -1;
}
//sem_open()
sem_t *sem = NULL;
sem = sem_open("mysem", O_RDWR);
if(NULL == sem)
{
puts("sem_open error.");
return -1;
}
//shmat()
volatile char *shmaddr = NULL;
shmaddr = shmat(shmid, shmaddr, 0);
if(NULL == shmaddr)
{
puts("shmat error.");
sem_close(sem);
return -1;
}
//do_work
while(1)
{
int sval = 0;
sem_getvalue(sem, &sval);
if(sval == 2)
{
sem_wait(sem);
char buf[] = "放入礼品";
memset(shmaddr, 0, 100);
memcpy(shmaddr, buf, sizeof(buf));
if(strcmp(buf, shmaddr) == 0)
{
puts(shmaddr);
}
}
}
//shmdt()
shmdt(shmaddr);
//sem_close()
sem_close(sem);
return 0;
}
//p3.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <semaphore.h>
int main()
{
//ftok()
key_t key = ftok(".",2);
if(key == -1)
{
puts("ftok error.");
return -1;
}
//shm_get()
int shmid = shmget(key, 100, IPC_CREAT | 0664);
if(shmid == -1)
{
puts("shmget error.");
return -1;
}
//sem_open()
sem_t *sem = NULL;
sem = sem_open("mysem", O_RDWR);
if(NULL == sem)
{
puts("sem_open error.");
return -1;
}
//shmat()
volatile char *shmaddr = NULL;
shmaddr = shmat(shmid, shmaddr, 0);
if(NULL == shmaddr)
{
puts("shmat error.");
sem_close(sem);
return -1;
}
//do_work
while(1)
{
int sval = 0;
sem_getvalue(sem, &sval);
if(sval == 1)
{
sem_wait(sem);
char buf[] = "打包";
memset(shmaddr, 0, 100);
memcpy(shmaddr, buf, sizeof(buf));
if(strcmp(buf, shmaddr) == 0)
{
puts(shmaddr);
}
sem_post(sem);
sem_post(sem);
sem_post(sem);
//sleep(1);
}
#if 0
sem_getvalue(sem, &sval);
printf("sval:%d\n",sval);
#endif
}
//shmdt()
shmdt(shmaddr);
//sem_close()
sem_close(sem);
return 0;
}
1.5.6:信号量异常实验现象的原因:
①、Linux高版本内核对高性能CPU的兼容性有bug;
②、实现的代码依赖于进程上下文切换,如果上下文切换出问题就导致代码跳跃性执行;
③、如果虚拟机配置多核模式,可能出现上述问题,如果配置的是单核问题应该不大;
2、总结:昨天课堂上结束了对于进程的学习,但对于我来说,不管我有没有完全掌握关于进程的知识点,进程的内容远远不止我所接触的知识面 ,所以我会好好复习以前学过的知识,至少做到吃进去的东西得消化吧,还有我所分享的内容如果有错误还请大家帮我多多指正,这周的分享就到此结束了,谢谢大家观看。