幸福的生活老是类似的,天降的大锅各有各不一样。python
咱们有个功能是这样的:有个以 root 运行的 python 程序,它须要以 test 用户执行 linux 命令,因此就经过 subprocess 库 + sudo
来执行,也就是下面的关系图:linux
./test_b
就是这么一个很简单的需求,原本是没有什么太大的问题的,然而事实老是喜欢打咱们脸。shell
就输出下面的错误了:segmentfault
虽然上面的错误不会影响程序的运行,可是处女座无法忍,必定要干干净净,明明白白!bash
凭借过硬的英语水平,咱们明白这个报错是由于访问不到父目录致使 getcwd
出错了。函数
聪明的童鞋一想就以为是否是和上面的删除目录有关系,这时候确定得看看 test_b
是什么内容,说不定能解决咱们的疑问:ui
#!/usr/bin/python import time import os time.sleep(3) os.system('sleep 1')
那么问题来了,test_b
明明就只想睡个觉,不想涉足江湖事,也没有调用getcwd
,为何会输出这个报错咧!spa
在咱们毫无头绪时,能够去喝喝快乐肥宅水,说不定就能脉动回来。code
由于我就是这样看到找到线索了:shell-init
。blog
凭借过硬的英语水平,咱们能够看到这个错误应该在 shell 初始化时候报的,这样很明显啦,去搜 bash 代码。
很快咱们就找到这句错误定义的地方了:
root@bash-4.4 $ grep 'shell-init' -r * variables.c: temp_string = get_working_directory ("shell-init");
看到 get_working_directory
这个函数名这么正规,感受这事靠谱了,顺着看看内容:
// builtins/common.c char * get_working_directory (for_whom) char *for_whom; { ... (跳过) if (the_current_working_directory == 0) { fprintf (stderr, _("%s: error retrieving current directory: %s: %s\n"), (for_whom && *for_whom) ? for_whom : get_name_for_error (), _(bash_getcwd_errstr), strerror (errno)); return (char *)NULL; } ... (跳过) }
虽然大部分是经过变量传值进去,可是仍是能看出就是我们那句报错的原型了,
其实上面的代码实现并非最关键的,关键的是,这些代码文件是在 bash 里面的,为何system
会和bash
扯上关系呢?难道 system
还须要撸一发 shell
么,崩溃!我心目中的 system
不是这么随便的!
带着不甘心去搜它的实现:
int system(const char * cmdstring) { pid_t pid; int status; if(cmdstring == NULL){ return (1); } if((pid = fork())<0){ status = -1; } else if(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; } } } return status; }
它的出厂设置就是这样,原来是我一直没去深刻了解它。
那如今其实一目了然了,system
调用了 /bin/sh
, 触发shell 初始化了, 在初始化变量时候调用了get_working_directory
,由于获取父目录失败了,因此输出了那段错误。
既然咱们知道错误是 system
输出的,那么咱们换个方式就应该能规避咯?
因而乎,./test_b
代码改为这样就不报错了:
#!/usr/bin/python import time import os time.sleep(3) # os.system('sleep') os.execl('/bin/sleep', 'sleep', '1')
那么这里又引出了一个问题了
system 和 execl 都能执行系统命令,那二者有什么区别呢?
答案在上面的 system 的源码已经给出 80% 了,他们的区别就是:
system = fork + execl + waitpid
而 execl
只是系统 exec
族函数的其中一个,说到 exec
族函数,它们是将新的程序内容替换当前进程内容运行,具体你们能够去谷歌看看,这边就很少说了~
咱们对 system
的实现已经有必定熟悉了,在后面使用这个方法时候,无论是在资源使用仍是问题排查,都应该多一些意识,眼不见不表明不要紧~
欢迎各位大神指点交流, QQ讨论群: 258498217
转载请注明来源: https://segmentfault.com/a/11...