KDOC 260: プロセスがSleepになるのを再現する

この文書のステータス

  • 作成
    • 2024-11-07 貴島
  • レビュー
    • 2024-11-14 貴島

概要

KDOC 254: ジョブプロセスがSleepしていた理由の、再現コードを書いた。もっとシンプルにしたいが、Python上でシェル実行するとき特有のことなのか、シェル実行でも再現する問題なのかわかっていない。パイプの挙動に見える。

再現

import subprocess

process = subprocess.Popen(
    ["bash", "-c", "./test.sh"],
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE
)

for line in process.stdout:
    print(line.decode('utf-8'), end="")
#!/bin/bash
set -eu

# 1000バイト
dummy=`printf "%1000s" | tr ' ' 'a'`
i=0
while true
do
    sleep 0.01
    echo -n $dummy >&2 # 標準エラー出力へ

    echo $i        # 標準出力へ
    i=`echo "$i+1" | bc`
done
python test.py

↑結果は、63ループ目でプロセスがSleep状態になる。Linuxのパイプのバッファの大きさは64KBというから、符合する。プロセスを再開(Continue)しても、1ループも進まず再びSleepになる。

strace python test.py

また、straceすると、最後はファイルディスクリプタ3番をreadしたところでSleepに入っていることがわかる。対応する括弧と後半の引数が出力されない。

(略...)
close(8)                                = 0
close(6)                                = 0
close(4)                                = 0
brk(0x565112213000)                     = 0x565112213000
read(7, "", 50000)                      = 0
brk(0x565112207000)                     = 0x565112207000
close(7)                                = 0
read(3,
read(3, 0x559d748f5030, 4096)           = ? ERESTARTSYS (To be restarted if SA_RESTART is set)

つまりreadの途中で、システムコールが中断されたということ。

ここで、ファイルディスクリプタ3番はパイプである。

$ ls -al /proc/3580807/fd
total 0
dr-x------ 2 orange orange  0 Nov  2 16:00 .
dr-xr-xr-x 9 orange orange  0 Nov  2 15:57 ..
lrwx------ 1 orange orange 64 Nov  2 16:00 0 -> /dev/pts/21
lrwx------ 1 orange orange 64 Nov  2 16:00 1 -> /dev/pts/21
lrwx------ 1 orange orange 64 Nov  2 16:00 2 -> /dev/pts/21
lr-x------ 1 orange orange 64 Nov  2 16:00 24 -> /dev/pts/0
lr-x------ 1 orange orange 64 Nov  2 16:00 3 -> 'pipe:[21445201]'
(略...)

書き込みの途中で止まる、というならわかる。が、readで止まる理由がわからない。明らかにパイプのバッファがいっぱいになって起こることに見えるが、writeで止まっているわけではない。試してみると思ったとおりにならない。何かの理解が誤っている。

関連