本系列專欄是「了解 ELF 動態時期行為」的前導專欄,建議您先行爬文「理解 dynamic loader 內部原理的幾個先備知識(下):Kernel 端的議題」。
Shell 執行外部程式的觀念在 2006 年的最後一天,就先來解說一下「下指令後、程式怎麼載入與執行」的觀念。首先,您必須知道並具備以下的 Linux 系統程式觀念:
1. fork() 系統呼叫:用來建立 child process。
2. exec 系列系統呼叫:以一個「外部程式」來取代自己(current process)的執行空間。
3. parent process、child process 與 Linux process tree。
了解以上三個主題後,就可以很容易理解「下指令後、程式怎麼載入與執行」的過程;這是很重要且基本的三個議題,若您不是很清楚這三個議題的觀念,可以查詢「Linux Systems Programming」的相關文件。
接下來,首先我們要知道的是,Linux 並沒有提供 "spawn" 的 system call;再來,shell 執行外部程式的做法是使用「spawn 版」的 fork() 實作。
好像又把大家搞胡塗了。所謂的「spawn 版的 fork()」,用對照的方式來說明會很清楚:
1. 真正的 spawn() 是「建立一個外部程式的 child process」;
2. 而 Linux 下討論的 fork() 則是「建立一個與父程序(parent process)完全相同的 process」。
也就是說,Linux 下的 fork() 是「non-spawn behavior」,因為他不會去跑一個外部程式,而是「複製 parent process 成為 child process」;那麼,要怎麼做出「spawn behavior」的 fork() 呢?方法很簡單:
1. 我們已經學過, exec 系列系統呼叫會以一個「外部程式」來取代 current process 的執行空間,所以不能在 parent process 裡叫用 exec system call 來跑外部程式;否則 parent process 會消失。
2. fork() 可以建立 child process,並且不讓 parent process 消失。
3. 所以,如果先 fork() 出 child process,再讓外部程式取代 child process,那麼就可以實作出「spawn style 的 fork()」了。
Jollen 一開始就打算以邏輯思考(理論推演)的方式來介紹「觀念」,所以講了這麼多,希望不會造成您閱讀上的困擾。引用「書上寫的」應可以勝過以上的長篇大論:
Shell 執行外部程式的做法是先 fork 出自己的 child process,然後在 child process 裡使用 exec 系統呼叫外部程式,以執行外部程式。
很簡單的概念,只是記結論的話多少會少掉一些思考的樂趣。依此來看,邏輯上(Linux programming view)「可視 spawn 為 fork + exec」;實際上,若由 kernel view 來解讀,spawn 並不等於 fork + exec。
程式範例看此範例需要知道 fork() 與 exec 系列函數的用法,您可參考「Linux Systems Programming」相關文件。
/* * Copyright(c) 2003,2004,2005,2006 www.jollen.org * * Spawn-style Linux fork(). * * This file is GPLed. */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> int spawn(char *prog, char **arg_list) { pid_t child; child = fork(); if (child != 0) { return child; } else { execvp(prog, arg_list); fprintf(stderr, "spawn error\n"); return -1; } } int main() { char *arg_list[] = { "ls", "-l", "/tmp", NULL }; spawn("ls", arg_list); return 0; }
以上範例可由「http://tw.jollen.org/elf-programming/spawn_fork.c」下載。
Happy New Year ;-)
Jollen's Blog 使用 Github issues 與讀者交流討論。請點擊上方的文章專屬 issue,或 open a new issue
您可透過電子郵件 jollen@jollen.org,或是 Linkedin 與我連絡。更歡迎使用微信,請搜尋 WeChat ID:jollentw