wait/waitpid and Blocking

  • wait がブロックする条件
  • non-blocking な wait(waitpid & WNOHANG)

についてメモ
1. wait and blocking

fork(2) した子プロセスを wait(2) すると、親プロセスは子プロセスのステートが変わるまでブロックされる。man によるとステートがかわるのは次の3つのケースがある

  1. the child terminated
  2. the child was stopped by a signal
  3. the child was resumed by a signal

ターミナルからコマンドを受け付けて、fork したプロセスで exec するプログラム(簡易的なシェル)を考える。
ls のようにすぐに処理が完了するコマンドであれば、すぐにコマンドが再入力できるようになるが、sleep 10 のように完了まで時間のかかるコマンドを入力すると、処理が完了するまで(10秒たつまで)コマンド入力を受け付けない。

# 例解UNIXプログラミング コード例5.8(P.163) 改良
# blocking.py
import os
import shlex
import sys

while True:
    cmd = raw_input('@')
    args = shlex.split(cmd)
    pid = os.fork()
    if pid == 0:
        try:
            os.execlp(args[0], *args)
        except OSError:
            sys.exit(1)
    else:
        pid, status = os.wait()
        if status != 0:
            sys.stderr.write('wait failed\n')
            sys.exit(1)

実行して動作確認

$ python ./blocking.py
@date
Sat Dec 25 21:24:24 JST 2011
@sleep 10 # blocked for 10 seconds
@

2. waitpid and non-blocking

wait(2) とよく似たシステムコールに waitpid(2) がある。こちらはプロセスID、プロセスグループ、任意の子プロセスなどの粒度で子プロセスを wait する。wait 方法も指定可能で、WNOHANG オプションを指定すると、状態を返せる子プロセスがないときにはブロックせずに入力を受け付けるようになる。

コマンド入力時に & をつければバックグラウンド実行してブロックしないようにプログラムを改良したのが次。

# nonblocking.py
# 例解UNIXプログラミング コード例5.10(P.169)

import os
import shlex
import sys

while True:
    while True:
        try:
            # pid=-1 : pertains to any child of the current process
            zpid, status = os.waitpid(-1, os.WNOHANG)
            if zpid > 0:
                sys.stderr.write('process %d salvaged\n' % zpid)
            else:
                break
        except OSError, err:
            # No child processes
            break
    try:
        cmd = raw_input('@')
        if not cmd:
            continue
    except EOFError, err:
        break
    args = shlex.split(cmd)
    if args[-1] == '&':
        # run in the background
        args.pop()
        bg = True
    else:
        bg = False
    pid = os.fork()
    if pid == -1:
        sys.stderr.write('fork failed\n')
        sys.exit(1)
    elif pid == 0:
        # child process
        try:
            os.execlp(args[0], *args)
        except OSError:
            sys.etderr('%s'%argv[0])
            sys.exit(1)
    else:
        # parent process
        if not bg:
            # wait for the child process to finish
            cpid, status = os.waitpid(pid, 0)
            sys.stderr.write('process %d finished\n' % cpid)

実行して動作確認。「salvaged」はバックグラウンド実行されたプロセスが終了後に回収されたもの。

$ python ./nonblocking.py
@date
Sat Dec 25 21:42:57 JST 2011
process 4062 finished
@sleep 10 &
@date
Sat Dec 25 21:43:12 JST 2011
process 4064 finished
@date
Sat Dec 25 21:43:44 JST 2011
process 4065 finished
process 4063 salvaged

3. waitpid and process state changes

Linux の man に シグナルで子プロセスのステートを変えた時の wait の挙動をテストする waitpid のサンプルコードがあった。

Python に移植したのが以下。

# from linux man page : waitpid(2)
# http://www.kernel.org/doc/man-pages/online/pages/man2/waitpid.2.html

import os
import signal
import sys
import time

cpid = os.fork()
if cpid == -1:
    sys.etderr('fork')
    sys.exit(1)
elif cpid == 0:
    print 'Child PID is %d'% os.getpid()
    if len(sys.argv) == 1:
       signal.pause() # pause(2) :  wait for signal
    os._exit(int(sys.argv[1]))
else:
    while True:
        pid, status = os.waitpid(cpid, os.WUNTRACED | os.WCONTINUED)
        if  status == -1:
            sys.stderr('waitpid')
            sys.exit(1)
        if os.WIFEXITED(status):
            print 'exited, status = %d' % os.WEXITSTATUS(status)
        elif os.WIFSIGNALED(status):
            print 'killed by signal %d' % os.WTERMSIG(status)
        elif os.WIFSTOPPED(status):
            print 'stopped by signal %d' % os.WSTOPSIG(status)
        elif os.WIFCONTINUED(status):
            print 'continued'
        if os.WIFEXITED(status) or os.WIFSIGNALED(status):
            break

ステートが変わる3つのシグナル(SIGTERM/SIGSTOP/SIGCONT)を送信して動作確認

$ python sample_waitpid.py  &
[1] 4117
Child PID is 4118
$ kill -STOP 4118
stopped by signal 19
$ kill -CONT 4118
continued
$ kill -TERM 4118
killed by signal 15
[1]+  Done  python sample_waitpid.py

4. Resources

冨永和人、権藤克彦著「例解UNIXプログラミング教室」(2007, ピアソンエデュケーション)では以下が関連する

著:冨永 和人、権藤 克彦

  • 3.17.1 ブロックと遅いシステムコール
  • 5.9 シェルの動作
  • 5.10 子プロセスを待つ : wait
  • 5.11 背景実行
Advertisements
Tagged with: , ,
Posted in linux

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Archives
  • RT @__apf__: How to write a research paper: a guide for software engineers & practitioners. docs.google.com/presentation/d… /cc @inwyrd 6 months ago
  • RT @HayatoChiba: 昔、自然と対話しながら数学に打ち込んだら何かを悟れるのではと思いたち、専門書1つだけ持ってパワースポットで名高い奈良の山奥に1週間籠ったことがある。しかし泊まった民宿にドカベンが全巻揃っていたため、水島新司と対話しただけで1週間過ぎた。 それ… 6 months ago
  • RT @googlecloud: Ever wonder what underwater fiber optic internet cables look like? Look no further than this deep dive w/ @NatAndLo: https… 6 months ago
  • @ijin UTC+01:00 な時間帯で生活しています、、、 1 year ago
  • RT @mattcutts: Google's world-class Site Reliability Engineering team wrote a new book: amazon.com/Site-Reliabili… It's about managing produc… 1 year ago
%d bloggers like this: