我在我的代碼中使用了 execvp,據我理解,它直接呼叫它傳遞的命令,不像 popen 或 system,它也呼叫一個 shell。
但是,我也明白 execvp 會檢查PATH變數以查找它在各個目錄中傳遞的命令,這也是我使用它的原因,所以我不必傳遞程式的整個路徑。
那么如果 execvpPATH甚至不呼叫 shell,它如何檢查變數呢?我的直覺告訴我,這可能是因為登錄后的每個行程都是 shell 會話的間接子行程,但我可能錯了。
uj5u.com熱心網友回復:
環境變數和Shell變數不同
一個常見的誤解,也許是這個問題的部分基礎,是所有shell 變數都是“環境變數”。
這是不正確的:Shell 具有常規的 shell 變數,存盤在堆記憶體中,不會跨行程邊界傳播,并且是該 shell 行程的本地變數;它們有環境變數,它們在內核管理的每個行程記憶體塊中維護一個副本。此塊中的可用空間有限制(通常與命令列引數長度共享——更多/更大的環境變數意味著命令列引數的空間更少),因此分配環境變數比常規 shell 變數“更昂貴” .
環境變數在創建時被復制到子行程,并且可以由非 Shell 軟體訪問
當標記為匯出的 shell 變數發生變化時,用 C 撰寫的典型 shell 使用setenv()或putenv()將新值復制到行程資訊空間中。
與所有其他行程狀態一樣,該空間塊在 上復制,并通過未顯式覆寫它的呼叫fork()保留;exec*()這就是子行程繼承環境變數的方式。
上面鏈接的exec規范詳細介紹了環境變數如何在 C 程式之間傳遞和表示;強烈推薦閱讀。
任何程式都可以呼叫getenv()(或實作與 libc 實作包括的類似功能getenv)來讀取這些變數的值。
因此,execvp()可以在純 C 中實作,在任何地方都不涉及 shell。
通常這是一個 libc 包裝器,它圍繞一個較低級別的呼叫execve,例如直接指示內核用新的行程映像替換當前行程映像;但也可以直接在內核中實作它,因為內核可以使用它傳遞給子行程的行程資訊訪問記憶體塊,并且可以從那里檢索環境變數。
uj5u.com熱心網友回復:
如果 execvp 甚至不呼叫 shell,它如何檢查 PATH 變數?
從 glibc 來源https://github.com/zerovm/glibc/blob/master/posix/execvp.c#L93首先它只是從當前行程的環境變數中獲取變數:
char *path = getenv ("PATH");
然后對于以冒號分隔的路徑中的每個目錄:
path = p;
p = __strchrnul (path, ':');
它添加了傳遞給的可執行檔案的“名稱”,execvp路徑來自PATH. 因此,例如您呼叫execvp("somexe", ...)and PATH=/dir1/dir2:/dir3/dir4,回圈將運行兩次,首先是 with startp=/dir1/dir2/somexe,然后是 with startp=/dir3/dir4/someexe。
startp = (char *) memcpy (name - (p - path), path, p - path);
execve然后嘗試使用該路徑 名稱部分將其呼叫到系統呼叫:
/* Try to execute this name. If it works, execve will not return. */
__execve (startp, argv, __environ);
如果內核execve可以找到路徑,它將運行它,如果它不能,它將失敗ENOENT:
case ENOENT:
case ESTALE:
case ENOTDIR:
/* Those errors indicate the file is missing or not executable
by us, in which case we want to just try the next path
directory. */
...
break;
“從 PATH 獲取目錄”和“將其添加到傳遞給 execve 的名稱”然后呼叫系統呼叫execve的程序對于 PATH 中的每個目錄都重復。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/512511.html
標籤:ClinuxUnix
