背景幸福的生活总是相似的,只是天上掉下来的大锅不一样。我们有这样一个功能:有一个python程序以root身份运行,需要以test用户执行linux命令,所以通过subprocess库+sudo执行,就是下面的关系图:./test_b是这样的一个很简单的要求没什么大问题,但是事实总是喜欢抽我们的脸。只是输出如下错误:上面的错误虽然不会影响程序的运行,但是处女座受不了,一定要干净利落!错误位置取决于优秀的英语水平,我们理解这个错误是由于访问不畅造成的。转到父目录导致getcwd失败。聪明的童鞋们认为和上面目录的删除有关系。这时候一定要看看test_b的内容,也许可以解决我们的疑惑:#!/usr/bin/pythonimporttimeimportostime.sleep(3)os.system('sleep1')那么问题就来了,test_b显然只想睡觉,不想插手江湖事务,又不调用getcwd,怎么会输出这个报错!没有头绪的时候,我们可以去喝快乐肥宅的水,说不定就能脉动回来了。因为我就是这样看到线索的:shell-init。凭借优秀的英语水平,我们可以看出这个错误应该是在初始化shell时报错的,所以很明显,去搜索bash代码。很快我们发现这句话定义错误:root@bash-4.4$grep'shell-init'-r*variables.c:temp_string=get_working_directory("shell-init");看到get_working_directory函数名这么正规,感觉这件事靠谱,跟着看内容://builtins/common.cchar*get_working_directory(for_whom)char*for_whom;{...(skip)if(the_current_working_directory==0){fprintf(stderr,_("%s:错误检索当前目录:%s:%s\n"),(for_whom&&*for_whom)?for_whom:get_name_for_error(),_(bash_getcwd_errstr),strerror(错误号));返回(字符*)NULL;}...(略过)}虽然大部分都是通过变量传值,但是还是可以看出来是我们报错的原型。其实上面代码的实现并不是最关键的,关键是,这些代码文件都在bash里面,为什么system跟bash有关系呢?系统还需要启动shell吗?它崩溃了!我心目中的系统可没那么随便!系统源码不愿意去寻找它的实现:intsystem(constchar*cmdstring){pid_tpid;内部状态;如果(cmdstring==NULL){返回(1);}if((pid=fork())<0){status=-1;}elseif(pid=0){execl("/bin/sh","sh","-c",cmdstring,(char*)0);exit(127);//如果子进程正常执行则不会执行该语句}else{while(waitpid(pid,&status,0)<0){if(errno!=EINTER){status=-1;break;}}}returnstatus;}出厂设置是这样的,原来没深入了解,现在居然一目了然,系统调用/bin/sh,触发shell的初始化,初始化变量的时候调用get_working_directory,因为获取父目录失败,所以输出错误。由于我们知道错误是通过那么我们应该可以通过更改方法来避免它吗?所以差不多,./test_b代码改成这样就不会报错了:#!/usr/bin/pythonimporttimeimportostime.sleep(3)#os.system('sleep')os.execl('/bin/sleep','sleep','1')那么这里又是一个问题,system和execl都可以执行系统命令,两者有什么区别?答案是上面80%的系统源码,它们的区别是:system=fork+execl+waitpid而execl只是系统的exec家族函数之一。当涉及到exec系列函数时,它们会用新的程序内容替换当前的进程内容。具体可以去google看看,这里就不多说了~system的实现我们已经很熟悉了。后面使用这个方法的时候,我们应该多注意是资源占用还是排错问题。看不到不代表没问题~欢迎大家指点交流,QQ讨论群:258498217转载请注明出处:https://segmentfault.com/a/11...
