今さらPython3 (69) - プロセス

第10章後半へ。

入門 Python 3

入門 Python 3

プロセス

プログラムを実行すると、OSがプロセスIDを割り当てるという話は、知っている人は知っているって感じなんだろうか。

f:id:deutschina:20151227082059p:plain

MacのActivity Monitorを開いたスクリーンショットだけど、これを見るとPythonがPID919で動いているのが分かる。

>>> import os 
>>> os.getpid()
919
>>> os.getcwd()
'/Users/ken'
>>> os.getuid()
501
>>> os.getgid()
20
>>> 
>>> os.chdir('PyCharmProjects/IntroducingPython3')
>>> os.getcwd()
'/Users/ken/PycharmProjects/IntroducingPython3'
>>> 

os.getpid()をすると919が返ってきているのが分かるね。getcwd()はカレントディレクトリ。getuid()はユーザID、getgid()はグループIDですと。この2つは普通にマシンを触ってて直接目にすることはないような気もするけど。

>>> import subprocess
>>> ret = subprocess.getoutput('date')
>>> ret
'Sun Dec 27 08:27:51 JST 2015'

subprocessを使うと、他のプログラムを起動、終了できると。上の例だと早くて見えないだろうけど、dateというプログラムが他のPIDで動かされて、その結果を受け取るという感じだろうね。

>>> ret = subprocess.getoutput('date -u')
>>> ret
'Sat Dec 26 23:31:55 UTC 2015'
>>> ret = subprocess.getoutput('date -u| wc')
>>> ret
'       1       6      29'

getoutput()の引数は文字列になっているから、-uみたいなパラメータを渡すことも出来ますねと。wcはよく知らないけどword countの略かな?戻り値は、1行、6語、29文字という意味らしい。本にも書いてあるけど、戻り値を受け取ると言うことは、処理が終わるまで他のことが出来ないってことだから、用途は限られるような気もする。

>>> ret = subprocess.check_output(['date', '-u'])
>>> ret
b'Sat Dec 26 23:37:53 UTC 2015\n'

check_output()は引数に文字列ではなくて、コマンドと引数のリストを受け取るところが少し違うんだね。戻り値はbyte形式になりますと。この本を読み始める前だったら、bってなんだよ?って感じだっただろうけど、今見たら「そうなのねbyte形式なのね」って程度。

>>> ret = subprocess.getstatusoutput('date')
>>> ret
(0, 'Sun Dec 27 08:42:22 JST 2015')

getstatusoutput()の場合は、戻りはタプルになってステータス(0は正常終了)とその結果が返されると。

>>> ret = subprocess.call(['date', '-u'])
Sat Dec 26 23:44:23 UTC 2015
>>> ret
0

call()だとステータスだけを受け取る。

>>> ret = subprocess.call('date -u')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/subprocess.py", line 537, in call
    with Popen(*popenargs, **kwargs) as p:
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/subprocess.py", line 859, in __init__
    restore_signals, start_new_session)
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/subprocess.py", line 1457, in _execute_child
    raise child_exception_type(errno_num, err_msg)
FileNotFoundError: [Errno 2] No such file or directory: 'date -u'

わざと本と違うことをやって怒られたんだけど、その説明が続きに書いてあった。

>>> ret = subprocess.call('date -u', shell=True)
Sat Dec 26 23:50:49 UTC 2015

shell=Trueが必要だったのね。

multiprocessing

おっと。これは11章で登場かと思っていたら、意外にも早くこんなところでお目に掛かるとは。

まずは、こんな感じのプログラムを作って、mp.pyとして保存。そこから実行する。

$ python3 mp.py
Process 1027 says: I'm the main program
Process 1028 says: I'm function 0
Process 1029 says: I'm function 1
Process 1030 says: I'm function 2
Process 1031 says: I'm function 3

Process()で、do_this()関数を実行しなさい、引数はこれだよと指定しているんだけど、プログラムを書く時にargs=("I'm funciton %s" % n))と書いてしまい、最後のカンマを忘れて保存して実行したらこんなエラーが出た。

...(略)...
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/multiprocessing/process.py", line 93, in run
    self._target(*self._args, **self._kwargs)
TypeError: do_this() takes 1 positional argument but 14 were given

argsなのでタプルを入れないと行けないんだけど、最後のカンマを入れ忘れたので文字列として認識され、I'm funciton xの部分が1文字ずつスライスされて計14個(14文字だから)に分割され、パラメータが14個渡されたと言って怒られたと。カンマを入れることでタプルとして認識されるというのはすでに学習済みだよね。

プログラムの動きに戻ると、PID1027でプログラムが実行されて、I'm the main programと高らかに宣言したのち、4回繰り返されるfor文の中で、1028, 1029, 1030, 1031という新しいプロセスが呼び出されて、I'm funciton xと言っていると。

もっと細かい話は11章に出てくるので、その時まで取っておきますか。

terminate()

これも、まずはプログラムを作って、実行してみましょか。


$ python3 terminate.py
I'm main, in process 1095
I'm loopy, in process 1096
	Number 1 of 1000000. Honk!
	Number 2 of 1000000. Honk!
	Number 3 of 1000000. Honk!
	Number 4 of 1000000. Honk!
	Number 5 of 1000000. Honk!

これも、親プロセス(PID1095)は、子プロセス1つ(PID1096)にloopy()を実行させて5秒休む。その間に子プロセスは1秒おきに1から1000000まで数え始める。ところが5秒後に目覚めた(実際には待っているだけで寝ているわけではない)親プロセスは、子プロセスに、「待て。いつまでやってるんだよ」とストップをかけるという感じだね。

(つづく)