系統(tǒng)進(jìn)程<a id="sec-1" name="sec-1"></a>
今天在看《Beginning Linux Programming》中的進(jìn)程相關(guān)部分侵歇,講到Linux幾個(gè)進(jìn)程相關(guān)的系統(tǒng)函數(shù): system
, exec
, fork
, wait
. Python的 os
模塊實(shí)現(xiàn)了對(duì)應(yīng)的函數(shù)封裝了這些系統(tǒng)調(diào)用: os.system
, os.exec
, os.fork
, os.wait
.
1. os.system(command)
# 在一個(gè)程序內(nèi)部啟動(dòng)另一個(gè)程序胶逢,從而創(chuàng)建一個(gè)新進(jìn)程
# os.system 在一個(gè)shell中執(zhí)行command命令,這是一個(gè)對(duì)C函數(shù)system()的python實(shí)現(xiàn)愤惰,具有相同的限制條件荸哟。在Unix系統(tǒng)中嘱能,返回值是命令執(zhí)行后的退出狀態(tài)值擎浴。由于POSIX沒有為C函數(shù)system()的返回值指定明確的含義,所以os.system()的返回值依# 賴具體的系統(tǒng)根暑。在Windowns中力试,返回值由系統(tǒng)環(huán)境變量決定: COMSPEC, 此環(huán)境變量依據(jù)不同系統(tǒng)版本不同
# 因?yàn)閛s.system的局限性,官方文檔建議用 subprocess模塊的功能來代替此函數(shù)
2. os.exec系列
os.execl(path, arg0, arg1, ...)
os.execle(path, arg0, arg1, ..., env)
os.execlp(file, arg0, arg1, ...)
os.execlpe(file, arg0, arg1, ..., env)
os.execv(path, args)
os.execve(path, args, env)
os.execvp(file, args)
os.execvpe(file, args, env)
# 這些函數(shù)都執(zhí)行一個(gè)新的程序排嫌,然后用新的程序替換當(dāng)前子進(jìn)程的進(jìn)程空間畸裳,而該子進(jìn)程從新程序的main函數(shù)開始執(zhí)行。在Unix下淳地,該新程序的進(jìn)程id是原來被替換的子進(jìn)程的進(jìn)程id怖糊。原來的程序不再運(yùn)行。在原來子進(jìn)程中打開的所有描述符默認(rèn)都是可用的薇芝,不會(huì)被關(guān)閉蓬抄。
# execv*系列的函數(shù)表示其接受的參數(shù)是以一個(gè)list或者是一個(gè)tuple表示的參數(shù)表
# execl*系列的函數(shù)表示其接受的參數(shù)是一個(gè)個(gè)獨(dú)立的參數(shù)傳遞進(jìn)去的丰嘉。
# exec*p*系列函數(shù)表示在執(zhí)行參數(shù)傳遞過去的命令時(shí)使用PATH環(huán)境變量來查找命令
# exec*e系列函數(shù)表示在執(zhí)行命令的時(shí)候讀取該參數(shù)指定的環(huán)境變量作為默認(rèn)的環(huán)境配置夯到,最后的env參數(shù)必須是一個(gè)mapping對(duì)象,可以是一個(gè)dict類型的對(duì)象饮亏。
3. os.fork (只在Unix有效)
# 要想讓進(jìn)程同時(shí)執(zhí)行多個(gè)函數(shù)耍贾,可以使用線程或者從原程序中創(chuàng)建一個(gè)完全分離的進(jìn)程÷沸遥可以通過fork創(chuàng)建一個(gè)新進(jìn)程荐开。這個(gè)調(diào)用復(fù)制當(dāng)前進(jìn)程,在系統(tǒng)的進(jìn)程表上創(chuàng)建一個(gè)新表項(xiàng)简肴,新表項(xiàng)中的許多屬性與當(dāng)前進(jìn)程相同晃听。但新進(jìn)程有自己的數(shù)據(jù)空間、環(huán)境和文件描述符
# os.fork出一個(gè)子進(jìn)程,在子進(jìn)程中返回0能扒,在父進(jìn)程中返回子進(jìn)程ID佣渴,如果發(fā)生錯(cuò)誤,則拋出OSError異常
# 注意:在一些平臺(tái)下如FreeBSD初斑,Cygwin和OS/2 EMX系統(tǒng)中使用該函數(shù)會(huì)有問題辛润。
4. os.wait (只在Unix有效)
# 當(dāng)fork啟動(dòng)一個(gè)子進(jìn)程時(shí),子進(jìn)程有了自己的生命周期并獨(dú)立運(yùn)行见秤,有時(shí)候希望知道一個(gè)子進(jìn)程何時(shí)結(jié)束砂竖,這時(shí)可以通過wait函數(shù)讓父進(jìn)程等待子進(jìn)程運(yùn)行結(jié)束
# os.wait 等待任何一個(gè)子進(jìn)程結(jié)束,返回一個(gè)tuple鹃答,包括子進(jìn)程的進(jìn)程ID和退出狀態(tài)信息:一個(gè)16位的數(shù)字乎澄,低8位是殺死該子進(jìn)程的信號(hào)編號(hào),而高8位是退出狀態(tài)(如果信號(hào)編號(hào)是0)测摔,其中低8位的最高位如果被置位三圆,則表示產(chǎn)生了一個(gè)core文件。
# 相關(guān)的的有os.waitpid(pid, options)
舉例<a id="sec-2" name="sec-2"></a>
os.fork<a id="sec-2-1" name="sec-2-1"></a>
#!/usr/bin/python
#coding=utf-8
import os
def child():
print('hello from child', os.getpid(), os.getppid()) #前者獲取進(jìn)程id, 或者獲取父進(jìn)程id
os._exit(0) # 退出進(jìn)程避咆, 通常只用在fork() 產(chǎn)生的子進(jìn)程中
def parent():
pid = os.fork() # 返回的pid在父進(jìn)程中為子進(jìn)程PID舟肉,在資進(jìn)程中為0, 所以下面根據(jù)pid判斷是哪個(gè)進(jìn)程
if pid == 0:
child()
print 'fork child process error!' #如果打印該字符串,說明調(diào)用child()出錯(cuò)
else:
print('hello from parent', os.getpid(), pid)
parent()
我電腦上運(yùn)行結(jié)果如下:
-> python test.py
('hello from parent', 1952, 1953)
('hello from child', 1953, 1952)
os.exec 和 os.system<a id="sec-2-2" name="sec-2-2"></a>
os.execlp的測(cè)試代碼如下:
#!/usr/bin/python
#coding=utf-8
import os
def main():
print "Running ps with execlp"
os.execlp("ps", 'ps', 'ax')
print "Done."
main()
運(yùn)行結(jié)果:
-> python test4.py
Running ps with execlp
PID TT STAT TIME COMMAND
1 ?? Ss 0:12.43 /sbin/launchd
42 ?? Ss 0:03.05 /usr/libexec/UserEventAgent (System)
43 ?? Us 0:05.24 /usr/sbin/syslogd
...
481 s000 S+ 0:00.14 -zsh
1803 s001 Ss+ 0:00.49 /usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versio
1806 s002 Ss 0:00.10 /bin/zsh -i
2070 s002 R+ 0:00.02 ps ax
os.system的測(cè)試代碼如下:
#!/usr/bin/python
#coding=utf-8
import os
def main():
print "Running ps with execlp"
os.system("ps ax")
print "Done."
main()
運(yùn)行結(jié)果如下:
-> python test4.py
Running ps with execlp
PID TT STAT TIME COMMAND
1 ?? Ss 0:12.71 /sbin/launchd
42 ?? Ss 0:03.11 /usr/libexec/UserEventAgent (System)
...
481 s000 S+ 0:00.14 -zsh
1803 s001 Ss+ 0:00.50 /usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versio
1806 s002 Ss 0:00.10 /bin/zsh -i
2113 s002 S+ 0:00.02 /usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versio
2114 s002 R+ 0:00.00 ps ax
Done.
以上兩個(gè)例子對(duì)比可以看到 exec系列
的調(diào)用使得原進(jìn)程接下的代碼都不會(huì)運(yùn)行。
os.wait<a id="sec-2-3" name="sec-2-3"></a>
#! /usr/bin/python
#coding=utf-8
import os
import sys
def child_process():
'''child process'''
print 'child process is running'
sys.exit(0) # 常用的退出進(jìn)程
def parent_process():
'''parent process'''
print 'parent process is running'
print 'waiting for child process'
exit_stat = os.wait() # 返回值是一個(gè)pid 和退出狀態(tài)的元組
print "waited child process's PID = %d" % (exit_stat[0])
sys.exit(0)
def main():
'''main function'''
try:
pid = os.fork()
if pid > 0:
'''parent process'''
parent_process()
else:
child_process()
except OSError, e:
print os.strerror(e.errno)
main()
運(yùn)行結(jié)果:
-> python test4.py
parent process is running
waiting for child process
child process is running
waited child process's PID = 2152
總結(jié)<a id="sec-3" name="sec-3"></a>
本文簡(jiǎn)單介紹了系統(tǒng)進(jìn)程相關(guān)的 os.system
, os.exec
, os.fork
, os.wait
系統(tǒng)調(diào)用查库。相關(guān)的其它調(diào)用如:
os.kill
, os.ncie
, os.popen
, os.spawn系列
可以參照官方文檔路媚。另外實(shí)際上python多進(jìn)程編程更加推薦 multiprocessing
模塊提供的相關(guān)功能,將在以后的文章中探究樊销。