进程间通信(IPC) 之 共享内存 和 闲扯其他一些东西
来源:互联网 发布:九章算法系统设计视频 编辑:程序博客网 时间:2024/06/03 17:22
其实,写这篇文章是带着多么激动的心情,终于没有bug,这一篇有好几处我还要在加强练习。在进程间通信(IPC),函数好多,好多都没有记住,查man的时候就特别烦,一大段一大段的英语,看的一个头有8个大,先来谝谝共享内存。
先说说我理解,对于共享内存,我感觉这效率还是很高的,因为内核基本上说不干预,说的最好(IPC之中)也是可以的。像管道,消息队列这些都很依赖于内核,消息队列很多都是系统函数,对于调用系统函数,需要站在不同的角度说,调用系统函数好呀,快么,内核就像皇帝,系统调用就是宠妃,你要干个事,内核马上可以帮你处理好,但是另一角度,后宫佳丽3000人,每个佳丽都向皇上提出自己的要求,皇上估计要驾崩了吧,也就是说系统调用多的话,内核根本忙不过来,那么系统的效率也就低了,本来内核事情就多,能减少工作量就将少吧,内核不做,我们可以找库函数做阿,我们调用库函数,很多事情都让他做,只有少部分去库函数找找内核帮忙。库函数就相当于大臣。感觉这样说应该差不多吧。
共享内存从字面意思都可以理解出来,大家都可以用吗?我只要知道确定ID,标识符,我就可以用,在这里还是扯一扯操作系统的一些知识,对于CPU而言,她永远看到都是都是虚拟地址,真正的物理内存地址她是看不到的,从虚地址到实际地址的转化是通过MMU,一个专门的转化地址的,这个就需要硬件支持了(至于为什么要用虚拟内存,而不用实际物理内存还是专门写一片博客吧,这里就不详细说了),那么对于每个进程而言,各自的虚拟地址空间(虚拟内存)相互之间都是不可见的,就算你知道别的进程假如某个变量的虚拟地址,拿过来没有一点用,还是看不到(现在都基本采用分页式),因为物理内存完全不知道在哪里。
因此呢,我们搞一个共享内存出来,大家对于所有的进程都可以看见这片内存,那么大家都可以用,也就实现了通信。我们都应该知道进程的虚拟地址空间分布图吧,从高地址开始,比如有4G内存,其中1G是划分给内核的,其他3G给用户,内核的1G就是直接映射到所有的进程虚拟地址空间。而共享内存也是如此,你要用到这片内存的时候,调用shmat,直接映射到进程虚拟地址空间。原理就是这个样子吧!,这图太丑了,关键我的linux上没有画图工具,这么丑,情有可原吧
我们现在用的是System V标准,不要去看posix标准了,了解一个就好。知道怎么回事。我简单和所下函数,因为这些函数网上博客中说的太多了,没必要在细说了.
shmget 获取以俄国共享存储标识符
shmctl 对共享存储段执行多种操作(查看状态,删除,复制)
shmat 将共享内存映射到自己的进程空间
shmdt 将共享内存分离出去
我们可以利用 ipcs -m 查看共享内存,其中那个0xabcd0001我自己创建的,对于其他几个键,其实人间是私有的,还有重要的一点忘记说了,一点私有的放在共享内存中其实挺好的,可以保存一些数据,一些配置文件比如nginx中的热部署,我直接写在共享内存中,修改配置文件我仅仅需要reload,而不需要把nginx重新启动一遍(对于服务器而言,重新启动一次压力是非常巨大的,对于一些公司,好几年应该才重新启动一次),进程死了的话,那么她的内存空间也就被释放了,内存的数据一点不剩,如果把一些重要的数据放在共享内存中,就算进程死了,重新启动进程我们还是可以从之前的进程读取出数据的,但是也要吐槽一下私有,说好的共享呢,你却私有了。
前面的废话真是扯了好多了,也是够了吧。开始今天的代码之旅。对于代码而言,现在也是比较规范的,总是自我感觉良好。这个程序用练习了下共享内存,几个点也是特别的重要。such as:
myinfo = (_procinfo *)((char *)pctl + sizeof(_pool_ctl)) + loop
这个就是把进程池结构体强制转化为char *加上进程池结构的大小,loop是按照(_procinfo *)大小移动的,这就计算出子进程在共享的位置, 其实我们在C语言中用到强制类型转化真是太多了,对于好的C程序猿来说,对于内存应该斤斤计较。举个简单的例子,读出一个int的地址, char *p = (char *)&x;然后p++循环4次就出来了,
#include <stdio.h> #include <stdlib.h>#include <unistd.h>#include <string.h>#include <sys/ipc.h>#include <sys/shm.h>#include <signal.h>#include <sys/wait.h>#include <sys/types.h>#define SHMKEY_MAIN_NET 0xabcd0001#define POOLMAX 128 //最大子进程的数目#define IDLE 0 // 表示子进程空闲#define BUSY 1 // 表示子进程繁忙#define OK 0#define NG -1#define SIG_WAKEUP SIGUSR1#define SIG_END SIGUSR2// 进程的信息typedef struct procinfo{ pid_t pid; int isbusy;}_procinfo;//进程池的控制结构体typedef struct pool_ctl{ int shmid; // key值 int procnum; // 子进程个数 _procinfo *info; // 指向子进程的指针}_pool_ctl;_pool_ctl *pctl;int poolrun = 1;// 函数的接口void *shm_init(key_t key,void **maddr,size_t size); // 共享内存的初始化void _sig_wake(int sig);void _sig_end(int sig);void _proc_start(); // 接受信号的函数int make_pool(); // 创建进程池void do_serv(); // 给子进程发信号// 函数的实现void *shm_init(key_t key,void **maddr,size_t size){ int shmid = -1; void *addr = NULL; if(maddr == NULL){ return NULL; } if(size > 0){ shmid = shmget(key,size,IPC_CREAT|0666); }else{ shmid = shmget(key,0,0); } if(shmid < 0){ return NULL; } addr = shmat(shmid,NULL,0); *maddr = addr; if(maddr){ memset(maddr,0x00,size); } return addr;}// 三个信号的处理函数,这里面可以什么东西都不写void _sig_wake(int sig){ //just wake up} void _sig_end(int sig){ poolrun = 0; printf("got sig %d\n",getpid());} //void _sig_int(int sig)//{ // ctrl + c 终止程序//} // 三个注册信号的函数void _proc_start(){ if(signal(SIG_WAKEUP,_sig_wake) == SIG_ERR){ //error } if(signal(SIG_END,_sig_end) == SIG_ERR){ //error } if(signal(SIGINT,_sig_end) == SIG_ERR){ //error }} // 创建进程池int make_pool(){ pid_t chid = -1; int loop = 0; int rst = OK; if(pctl == NULL){ return NG; } for(loop = 0; loop < pctl->procnum; loop++){ chid = fork(); if(chid < 0){ rst =NG; break; }else if(chid == 0){ _procinfo *myinfo = NULL; pctl = (_pool_ctl *)shm_init(SHMKEY_MAIN_NET,(void **)&pctl,0); if(pctl == NULL){ fprintf(stderr,"child pctl is NULL \n"); exit(1); } //很重要,计算逻辑地址,这里需要强制类型转化 myinfo = (_procinfo *)((char *)pctl + sizeof(_pool_ctl)) + loop; printf("child born [%d]\n",getpid()); while(poolrun){ pause(); myinfo->isbusy = BUSY; printf("%d I'm working\n",myinfo->pid); myinfo->isbusy = IDLE; } exit(0); }else{ pctl->info[loop].pid = chid; } } return rst; } // 给子进程发信号void do_serv(){ int index = 0; if(pctl->procnum <= 0){ printf("No sons\n"); return ; } while(poolrun){ index = (index + 1)% pctl->procnum; if(pctl->info[index].isbusy == BUSY){ continue; } kill(pctl->info[index].pid,SIG_WAKEUP); sleep(1); } for(index = 0;index < pctl->procnum; index++){ kill(pctl->info[index].pid,SIG_END); } index = 0; while(index < pctl->procnum){ wait(NULL); index++; } } int main(int argc,char *argv[]){ int pnum = 0; int rst = NG; if(argc < 1){ fprintf(stderr,"argc argv input error\n"); exit(1); } pnum = atoi(argv[1]); if(pnum <= 0 || POOLMAX < pnum){ fprintf(stderr,"pnum error\n"); exit(2); } //很重要 pctl = shm_init(SHMKEY_MAIN_NET,(void **)&pctl, sizeof(_pool_ctl) + pnum * sizeof(_procinfo)); if(pctl == NULL){ fprintf(stderr,"shm_init error\n"); exit(3); } //很重要 pctl->info = (_procinfo *)((char *)pctl + sizeof(_pool_ctl)); pctl->procnum = pnum; _proc_start(); rst = make_pool(); if(rst == OK){ do_serv(); } return 0;}
对于上述程序的结果演示演示:
[root@localhost IPC]# ./pro_pool_shm 6
6就代表创建6个子进程
重新打开一个终端,输入
<span style="font-size:18px;">[root@localhost IPC]# pstree -ap | grep pro_pool_shm</span>
然后我在这个终端发送kill -12 7917, 第一个终端出现了
这个进程已经捕捉到用户自定义停止的信号,由got可以看出来,在got的下一行7917 I'm working 之后,再也不执行了,因为已经死了,可是却是僵尸进程,资源已经释放,可是PCB中的东西还在。如果我执行连续kill -10 7918那么他就一直唤醒执行。如果在主进程中,我ctrl + c 那么父进程收到终止信号,那么子进程也会收到的,这样才不会进程都会被终止,就像这个样子
正常退出,也不会产生孤儿进程,如果我杀死父进程,那么他的儿子该进去孤儿院了,由1号进程收养,也就是会出现下面的样子
好了这就是今晚闲扯的淡,扯淡,如果有什么问题,哪里说的不对,欢迎指正,欢迎吐槽。
- 进程间通信(IPC) 之 共享内存 和 闲扯其他一些东西
- 进程间通信IPC之--共享内存
- 进程间通信IPC之--共享内存
- 进程间通信IPC之--共享内存
- 进程间通信IPC之--共享内存
- 进程间通信(IPC)之共享内存
- 进程间通信--IPC之共享内存
- 进程间通信(IPC):共享内存
- 进程间通信(IPC)---共享内存
- Linux 进程间通信(IPC)之三 --- 共享内存
- Windows IPC 进程间通信之共享内存
- IPC进程间通信主题之共享内存
- 进程间通信总结 && IPC主题三之 共享内存
- 笔记五:进程间的通信(IPC通信之共享内存)
- Linux 进程间通信 (IPC) //共享内存
- 进程间通信IPC---共享内存
- 多进程通信(IPC)--共享内存
- linux 进程间通信(IPC)一共享内存
- BGP timer协商实验
- java cookie
- 完全背包问题 动态规划
- POJ-1837-Balance-DP
- CAS 单点登录实战 (1) 介绍
- 进程间通信(IPC) 之 共享内存 和 闲扯其他一些东西
- 后天service定时提醒且不易被销毁
- MySQL的btree索引和hash索引的区别
- 第三天的实现,主要是封装优化,不会文件啊,把优化后的主文件发上来(用了xlb)
- db2性能分析思路
- 手工注入简记
- Relevancy 3.1 搜索相关性探究
- Spring注解:@Responsebody与@RequestBody
- 迭代加深搜索(埃及分数)