设为首页 收藏本站
查看: 559|回复: 0

[经验分享] linux下单例进程的一个实现方式

[复制链接]

尚未签到

发表于 2016-3-14 08:49:11 | 显示全部楼层 |阅读模式
  今天在论坛上有朋友问如果截获SIGKILL信号,然后删除锁文件,他主要想要做一个单例进程,进程开始时判断一个文件config的存在性,如果不存在,则创建之然后运行,如果已经存在那么进程退出,进程在退出的时候必须把这个config文件删除以确保不能影响下一次进程的运行,但是问题出来了,如果有人用SIGKILL把他的进程杀死怎么办?于是我的回答就是再建一个进程守护着它的运行,比如用心跳检测就可以,一旦检测不到这个进程的心跳了,那么就帮忙删除掉这个config文件,另一种做法就是将这个单例的进程作为一个父进程的子进程运行,然后在父进程中设置SIGCHLD信号处理器,一旦子进程被杀,那么就会发送信号给父进程,然后父进程在信号处理中可以删除config文件,这样做的话仅仅是多了一层保护,试想如果守护此进程运行的进程或者这个进程的父进程被杀怎么办?改内核是一种方式,比如钩住do_exit内核函数,然后判断结束的是哪个进程后做善后处理,但是如果谁都要通过改内核行为来解决问题的话,那么操作系统的意义何在,内核是为系统服务的,而不是为用户服务的,因此不要指望内核行为完成用户策略,因此这些方式都还不是十全十美的方式。十全十美的方式就是自洽的,不需要别的东西帮忙的方式,不用别的进程,不用什么锁文件,而且这种方式还不能影响别的进程,不能给系统带来不稳定性,因此linux的“小即是好”的思想派上了用场,linux总是可以用很多很小的东西组合成一个很猛的功能,比如今天我想到的一个办法就是用管道实现的。在windows上也可以这么实现但是windows上的管道要复杂些,而且windows中还可以有别的很多方式,比如windows中可以设置全局的信号量和互斥体,它们都可以实现,windows的粒度某种意义上更小,比如windows可以基于窗口发送消息,而linux在系统范围内发送消息的最小单位就是进程,比如基于进程发送信号,虽然如此,windows总是带来很多混乱因为它没有处理好大和小的边界,粒度小是好的,比如像linux哲学所述的那样,但是过于小的话就会相互杂糅不稳定,过大又会臃肿不灵活。因此设计粒度必须是可控范围内的最小值,这个可控范围就是进程,而且必须有一个统一的实体来控制粒度,比如unix/linux中将很多东西往进程的意义上靠拢,因此进程就是unix/linux的中心。下面看看我实现单例的方式吧:   
#include <stdio.h><br>int check( char * name )    <br>{    <br> int len = strlen(name),i = 0;    <br> for(;name[len-1-i]!='/'&amp;&amp;i<len></len> name = name+len-i;    <br> char cmd[64];    <br> sprintf(cmd,"ps -e|grep %s|awk '{print $4}'",name); //拼串,这就是创举的证明,ps,grep,awk都是用管道结合的,而且代价很小,并且ps,grep,awk都不大    <br> FILE *fp;    <br> char pro[64]; //64个字节足够存放进程名字了    <br> fp = popen(cmd, "r");    <br> if(fp != 0)    <br> {    <br> int count = 0; //计数器,记录一共有多少个该进程在运行    <br> while(fgets(pro, 64, fp) &gt; 0)    <br> {    <br> pro[strlen(pro)-1]=0; //去掉后面的换行    <br> if(!strcmp(pro,name))    <br> count++;    <br> }    <br> if( count &gt; 1 ) //超过一个就退出    <br> return 1; <br> pclose(fp);    <br> }    <br> return 0;    <br>}    <br>int main(int argc, char* argv[])    <br>{    <br> if(check(argv[0]))    <br> {    <br> printf("This programe is already runing/n");    <br> exit(1);    <br> }    <br>...//做真正的事    <br> return 0;    <br>}    <br>注释中提到管道的代价不大,事实上很小,看看内核的sys_pipe就知道了,这里就不说了,但是还是有必要说一下用户空间的popen,它是管道创建和执行shell的封装:    <br>FILE * popen(const char *cmdstring, const char *type)    <br>{    <br> int i, pfd[2];    <br> pid_t pid;    <br> FILE *fp;    <br>... //判断参数,并且验证其正确性    <br> pipe(pfd) //创建一对管道    <br> if ( (pid = fork()) == 0) //准备在子进程里面执行新的任务cmdstring    <br> { <br> if (*type == 'r') { //如果父进程是用来read的,那么子进程就是write的,因此关闭掉子进程一对管道的read端    <br> close(pfd[0]);    <br> if (pfd[1] != STDOUT_FILENO) {    <br> dup2(pfd[1], STDOUT_FILENO); //因为子进程是write端,因此将子进程的标准输出重新定向到新创建管道的写端,也就是exec出来的进程的输出全部写到管道里面了,然后父进程将之读出。    <br> close(pfd[1]); //关闭掉管道描述符,它已经没有用了,因为标准输出已经dup到它的内核实体了    <br> }    <br> } else { //对于父进程是写端的情况和上述情况相反,故而不再赘述。    <br>...//关闭文件描述符    <br> execl(SHELL, "sh", "-c", cmdstring, (char *) 0);//真正执行命令    <br>...    <br> }    <br> if (*type == 'r') { //如果父进程为read端,那么要将新创建管道的write端返回给调用的用户    <br> close(pfd[1]);    <br> if ( (fp = fdopen(pfd[0], type)) == NULL)    <br> return(NULL);    <br> } else {//如果父进程为write端则相反    <br>...    <br> return(fp); //返回文件描述符    <br>}</stdio.h>
  那位朋友问是否不应该用文件锁实现单例,我的回答是:
  你的办法是标准的做法,别人用SIGKILL杀你的进程才是不好的做法,任何事情都不能做那么绝,冲动是魔鬼。如果你再怕别人用SIGKILL杀你的进 程,那么我刚才给你的程序会有用,不过最好在linux和unix上都测试一下,我怕ps和awk的输出语义在不同系统上会有不同。
  他问:除了再设置一个守护进程看护这个单例进程外是否还有别的办法。我的回答:
  写内核模块吧,钩住do_exit内核函数,在里面判断是否退出的是你的那个进程,如果是的话就删除掉那么config文件,然后继续正常的流程。不过我 觉得没有必要这么做,就好像人死亡一样,如果是得病死去或者自杀,那么有足够的时间交待后事,当然猝死病除外,然而意外死亡的话,比如车祸,误杀等等几乎 没有任何机会交待后事的。
  他最开始问:能否捕捉到SIGKILL信号,然后做善后工作。我的回答:
  对于SIGKILL,被杀进程本身没有机会做任何事情,但是并不是说就没有办法做善后工作了,你可以把你的进程作为一个进程的子进程,然后在父进程里面设 置SIGCHLD处理器,一旦子进程被杀或者退出,那么父进程可以帮忙善后;另外你还可以写任何一个进程来看护你的这个需要善后的进程,办法多的是,比如 可以用心跳

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-190463-1-1.html 上篇帖子: Linux那些事儿 之 戏说USB(12)从这里开始 下篇帖子: Linux下进行GPT分区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表