Linux:system 調用引起的 getcwd 異常

背景

幸福的生活老是類似的,天降的大鍋各有各不一樣。python

咱們有個功能是這樣的:有個以 root 運行的 python 程序,它須要以 test 用戶執行 linux 命令,因此就經過 subprocess 庫 + sudo 來執行,也就是下面的關係圖:linux

clipboard.png

./test_b 就是這麼一個很簡單的需求,原本是沒有什麼太大的問題的,然而事實老是喜歡打咱們臉。shell

就輸出下面的錯誤了:segmentfault

clipboard.png

雖然上面的錯誤不會影響程序的運行,可是處女座無法忍,必定要乾乾淨淨,明明白白!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-initblog

憑藉過硬的英語水平,咱們能夠看到這個錯誤應該在 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 不是這麼隨便的!

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...

相關文章
相關標籤/搜索