fork + exec
fork是低層次的系統調用,經過複製父進程來建立子進程。shell
fork的行爲
fork用來拷貝當前進程,生成一個基本徹底同樣的子進程。less
my $pid=fork();
若是fork成功:code
- 則表示成功建立子進程,這時會有兩條執行路線:繼續執行父進程、執行子進程
- fork成功時,會返回兩個值:對父進程返回子進程的pid,對子進程返回0
若是fork失敗,將對父進程返回undef,並設置錯誤信息。fork失敗的可能緣由有:orm
- 內核內存不夠,沒法申請內存來fork
- 達到了容許的最大進程數量(進程數上限)
- 達到了rlimit限制的某種資源上限
例如:blog
#!/usr/bin/perl
#
use 5.010;
my $pid=fork();
say $pid,"======";
執行該程序,將返回兩行數據:進程
62620======
0======
其中第一行輸出是父進程輸出的,第二行是子進程輸出的。ip
雖然這裏父進程先輸出,但fork成功以後,父、子進程並無執行的前後順序,也可能cpu會先調度到上子進程去執行。內存
注意上圖中子進程部分只畫了"say"那行語句,但實際上子進程是徹底複製父進程的,子進程也能夠有say前面的那段語句(好比那個fork語句),但因爲父進程的狀態已經執行完了fork,因此子進程也是從fork語句以後開始執行的,fork語句以前的語句對於子進程來講是透明的。並且按照寫時複製的技術,子進程用不到它因此不會複製fork前面的代碼(注:也就是說子進程和父進程是共享代碼的)。ci
更復雜一點的例子:element
#!/usr/bin/perl
#
use 5.010;
print "id1: ",$pid,"\n";
my $pid=fork;
print "id2: ",$pid,"\n";
if(!$pid){ # ==0 exec
say "child process: ",$pid;
}
waitpid($pid,0);
say "parent process: ",$pid;
首先perl進程輸出id1。而後fork一個子進程,這時有兩條執行路線。假如fork後先執行父進程,則:
- 此父進程將輸出id2
- 而後判斷pid的值,由於fork返回給父進程的的pid變量值爲子進程的進程號,因此不會等於0,因而if判斷不經過
- 繼續執行waitpid(),它將等待子進程執行結束,如下是子進程的執行過程:
- 子進程首先輸出id2
- 而後判斷
$pid
,因爲fork返回給子進程的pid變量值爲0,因此if判斷經過,因而子進程輸出"child process"
- 繼續執行waitpid(),因爲
$pid=0
,waitpid()的等待pid爲0時,表示等待以本身爲leader進程的進程組中的其它進程,因爲沒有進程組,因此waitpid失敗
- 繼續執行,輸出"parent process"
- 子進程執行完畢
- 父進程的waitpid()等待子進程執行完畢,繼續向下執行
- 父進程輸出"parent process"
假如fork以後,先執行子進程,且還先把子進程執行完了,cpu才調度到父進程,則也沒有影響,子進程執行完畢後,早晚會調度到父進程,而父進程的waitpid($pid)
已經沒有子進程了,因而waitpid()失敗(返回-1),繼續向下執行。
顯然,上面fork的代碼有一些問題。因爲fork建立子進程以後。父、子進程都繼續執行,且執行的前後順序不定。因此:
- 在fork以後,應該緊接着判斷是不是子進程,避免有些在操做父子中都執行
- 在父進程中等待子進程
- 在子進程中加入執行完後就退出子進程的動做,省得執行本該父進程執行的動做
大概代碼以下:
my $pid=fork;
unless($pid){ # 判斷子進程的語句緊跟着fork
CODE1;
exit; # 要讓子進程退出
}
waitpid($pid,0); # 要等待子進程
CODE2;
fork和exec結合
通常fork和exec會一塊兒用,fork用來建立新的子進程,exec啓動一個程序替代當前子進程並在子進程結束時退出子進程。
例如system "date"
命令,替換爲低層次的fork+exec+waitpid。
defined(my $pid=fork) or die "Cannot fork: $!";
unless($pid){
# 進入到這裏執行的,表示進入子進程
exec 'date'; # exec正確執行時,執行完後將結束子進程
die "cannot exec date: $!"; # exec啓動失敗時,將執行die來結束子進程
}
# 這裏的表示是父進程
waitpid($pid,0);
system、exec、fork等的區別
第一我的解釋:
- exec replaces the current process with another one.
- system runs another program, wait for its completion.
- fork copies the current process; both the original and the copy continue from the same point.
- pipe sets up a pipe between two handles.
- syscall makes a low-level system call.
- eval executes (the string form will first compile) a piece of Perl code.
第二我的解釋:
- exec is used to execute the given process by replacing the current process. If the given process get executed successfully then exec will not return the value. exec returns the value in case of failure only.
- System is also doing the same thing as exec but system returns value in both success and failure cases. And parent process waits for the child process to complete. System() runs the command through a shell,while exec() runs the command directly.
- fork is used to create a new process(child process). And it is returning the PID of child to parent and zero to child if the fork is successful. The difference between the fork and exec is exec replaces the current process but fork doesn't.
- pipe is used for communicating between two processes. We can use both named and nameless pipes. It returns open a pair of pipes. In one end we can write. And in another end we can read the content.
- syscall is used to call the system call which is specified as a first argument. Remaining elements are the arguments to the system call.