speaker note test
- 身近な例だと データベースサーバ、Webサーバとか、専用のプロセスがずっと動いてる
pid
と表現されます。os.getpid()
を実行すると、現在のプロセスIDが確認できるgetpid(2)
を実行している>> import os
>> os.getpid()
16801 # <- プロセスID
ps(1)
コマンドでそのプロセスの状態を確認できます$ ps -p 16801
PID TTY TIME CMD
16801 ttys003 0:00.04 /usr/local/Cellar/python/3.7.0/~省略~/Python
PID ... プロセスID
TTY ... 仮想端末ファイル
TIME ... CPU使用時間
CMD ... 実行コマンド
getppid(2)
で調べらる>>> import os
>>> os.getppid()
14455
例えばターミナル.app 起動 して、Bashのプロンプトが表示した場合
という親子関係のプロセスが作られます
fileno()
メソッドで調べるとファイルディスクリプタの番号が確認できます。>>> open('test.txt', 'w').fileno()
3
>>> import sys
>>> sys.stdin.fileno()
0
>>> sys.stdout.fileno()
1
>>> sys.stderr.fileno()
2
with open('test.txt', mode='w') as fp:
print(fp.fileno()) # => 3
with open('test2.txt', mode='w') as fp:
print(fp.fileno()) # => 3 上と同じ
resource
モジュールに諸々掲載されているgetrlimit(2)
のシステムコール利用して取得できる>>> import resource
>>> resource.getrlimit(resource.RLIMIT_NOFILE)
(4864, 9223372036854775807)
最初の数字がソフトリミット
次の数字がハードリミット
resource.RLIM_INFINITY
の数値と一緒setrlimit(2)
を利用Too may open files.
というエラーがでる>>> import resource
>>> resource.setrlimit(resource.RLIMIT_NOFILE, (3, 3))
>>> open('test.txt')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: [Errno 24] Too many open files: 'test.txt'
引き継ぎます
$ MESSAGE='pyconjp2018.' python -c "import os; print(os.getenv('MESSAGE'))"
pyconjp2018
sys.argv
に実行ファイル名とともに格納されている# show_argv.py
import sys
print(sys.argv) # 単純に引数を表示するだけ
$ python show_argv.py arg1 arg2
['show_argv.py', 'arg1', 'arg2']
$PROGRAM_NAME
or $0
でプロセス名を取得設定可能>>> from setproctitle import setproctitle, getproctitle
>>> getproctitle()
'python'
>>> setproctitle("newprocname")
>>> getproctitle()
'newprocname'
>>>
exit() #=> 引数無しは0
exit(1) #=> 異常終了
def hello():
print("hello")
import atexit
atexit.register(hello)
via https://docs.python.org/ja/3/library/atexit.html#atexit-example
fork(2)
システムコールで子プロセスが作れる
- 親プロセスで使われてる全てのメモリーのコピーを引き継ぐ
- 親プロセスのファイルディスクリプタも引き継ぐ
os.fork
を利用することで子プロセスを作れます。import os
if os.fork():
print('spam')
else:
print('ham')
// 実行結果
spam
ham
なぜなのか?
fork(2)
は親プロセスのコピーであるos.fork
の 戻り値は 0
になるimport os
print(f'親プロセス: {os.getpid()}')
if os.fork(): # <= 子プロセスはここから始まる
print(f'親プロセス: {os.getpid()}')
else:
print(f'子プロセス: {os.getpid()}')
// 実行結果
親プロセス: 88220
親プロセス: 88220
子プロセス: 88221
Ctrl + C
)とかは基本受け付けない。import os
import time
if os.fork():
exit('親プロセスは死んだ') # 親プロセスはforkした瞬間に死ぬ
for i in range(5):
time.sleep(1)
print(f'孤児として生きてる {i}')
$ python orphan_process.py
親プロセスは死んだ
$ 孤児として生きてる 0 <- ここで一旦Terminalに戻る
孤児として生きてる 1
孤児として生きてる 2
孤児として生きてる 3
孤児として生きてる 4
fork(2)
は親の完全なコピーを生成するos.wait
で待つことが可能import os
import time
if os.fork():
os.wait() # 子プロセスが終了するまで待つ
exit('親プロセスは死んだ')
for i in range(5):
time.sleep(1)
print(f'孤児として生きてる {i}')
# 実行結果
孤児として生きてる 0
孤児として生きてる 1
孤児として生きてる 2
孤児として生きてる 3
孤児として生きてる 4
親プロセスは死んだ
wait
は子プロセスのが どれかが終わるまで待つimport os
import sys
import time
import random
for _ in range(3):
if os.fork() == 0:
# ランダムで1〜5秒待つ子プロセスを生成する
time.sleep(random.randint(1, 5))
sys.exit()
for _ in range(3):
pid = os.wait()
print(f'終了プロセスID {pid}')
終了プロセスID (26238, 0)
終了プロセスID (26236, 0)
終了プロセスID (26237, 0)
wait
で子プロセスの終了ステータスを要求しないimport os
import time
import sys
pid = os.fork()
if pid == 0:
time.sleep(1)
sys.exit() # 先に子が終了
# os.waitしない
print(pid) # => 終了した子プロセスID 92763
time.sleep(10)
# 状態を確認する
$ ps -ho pid,state -p 92763
PID STAT
92763 Z+ # <= Z+
init
プロセス (pid=1, ppid=0)daemonize
)# 子プロセス生成
pid = os.fork()
if pid > 0:
# 親死
sys.exit(0)
# 子プロセスを制御端末から切り離すk
os.setsid()
# 孫プロセス生成 <- 完全に制御端末から切り離される
pid = os.fork()
if pid > 0:
print(f"Daemon PID {pid}")
sys.exit(0)
# ディレクトリ消されても動く
os.chdir('/')
os.umask(0)
# デーモンには不要なので標準ストリームを潰す
devnull = os.open('/dev/null', os.O_RDWR)
os.dup2(devnull, 0)
os.dup2(devnull, 1)
os.dup2(devnull, 2)
os.close(devnull)
# デーモン化したい処理
main()