/proc

もう夏はすっかり終わって、幽霊シーズンも沈静化しちゃったけど、ゾンビを作り出してみる。 ゾンビは、子が死んでも、親が看取るのを放棄すると出来る。

int fork(void);
int sleep(int);
int main(){
  if (fork() == 0){
     return 0;
  }
  return sleep(500);
}

今回は、脳内cppを適用して、いいかげんに関数の型を定義しておいた。(正確なやつは、man fork等として調べてね)あれ? returnの型はどうなるんだろう? 書かなくてもコンパイラーが文句を言ってこなかったぞ。K&R本を見たら、returnは、return 式; と言う文になってた。(文と言う事は、returnはキーワードです)

このmainを見てたら、書き換え可能って事に気が付いたよ。

int main(){ return (fork() ? sleep(500) : 0); }

面白い。そして、更に面白いのがこれ

そしてYになる

遊んじゃったな。話を戻そう。

子が死んでも、親は忙しいって言って、子をほったらかしにした図。酒喰らって寝てるんだか、 パチンコに勤しんでいるんだか、秋の競馬に出かけているんだか(指紋認証のカードで、馬券が買えて、馬券データはカードの中。馬券の紛失を防げるし、払い戻しもカードにチャージされるとか、どこまで搾取する積りや >JRA)、そういう時にゾンビだぞが現れる。

debian:z$ ./a.out &
[1] 4335
debian:z$ ps 4335 4336
 4335 pts/2    S      0:00 ./a.out
 4336 pts/2    Z      0:00 [a.out] <defunct>

ステータスがZってなってる、defunctってのは、辞書を引いたら、今は亡きって出てたぞ。

ob6$ ps l
  UID   PID  PPID CPU PRI  NI   VSZ   RSS WCHAN   STAT  TT       TIME COMMAND
 1000 10506 73617   0 -22   0     0     0 -       Z     p0    0:00.00 (a.out)
 1000 73617 84840   0  10   0   228   772 nanosle I     p0    0:00.00 ./a.out
 1000 84840 44929   0  18   0   796   828 pause   Ssp   p0    0:00.12 -ksh (ksh

こちらはOpenBSDでの結果。ゾンビはカッコ付きになるのね。終了しちゃってるのでメモリーは既に取り上げられてる事が分かる。親の名前(じゃなくて、国民総番号)だけは、覚えてる。親の親は、kshって事も分かるな。

こんな薄情な親でも、生きている間は、仏様のinitは手を出さない。(ひょっとして親の気が変わってwaitするかも知れないからね。)しかし、親が死ぬと、哀れに思ったinitは、養子として受け入れ、成仏させてくれる。仏教思想がいきづいていますなあ。

あれ? unixって発祥地はキリストの国だよな。かの国にもそういう思想は有るのかな? そういう事は、あの人に聞けばいいのかな?

ある人が言ってた、『盆はご先祖様とのオフ会』と。連綿と続く命の営みの末に、自分が居る事を忘れちゃいけないぞ。

ps

例によって、psが何をやってるか見る。ps.cが主戦場。

270│         if (nlistf == NULL && memf == NULL && swapf == NULL) {
271├───────────────> kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
272│                 kvm_sysctl_only = 1;
 :
326│         /*
327│          * select procs
328│          */
329├───────> kp = kvm_getprocs(kd, what, flag, sizeof(*kp), &nentries);

主要部分は以上。

(gdb) p *kp
$2 = {
  p_forw = 0,
  p_back = 0,
  :
  p_pid = 83638,
  p_ppid = 82407,
  p_sid = 83638,
  p__pgid = 83638,
  p_tpgid = 83638,
  p_uid = 1000,
  :
  p_comm = "a.out", '\000' <repeats 18 times>,
  p_wmesg = "\000\000\000\000\000\000\000",
  p_wchan = 0,
  p_login = "sakae", '\000' <repeats 26 times>,
  p_vm_rssize = 301,
  :
  p_emul = "native\000",
  p_rlim_rss_cur = 2055917568,
  p_cpuid = 1,

なんか、見慣れたデータが含まれていますなあ。この巨大な構造体から、指定されたデータだけを抜き出して、整形して、並べ替えて、表示って塩梅だな。

kvm_getprocsの2番目の引数whatは、下記が選べるとな。

           KERN_PROC_KTHREAD     all processes (user-level plus kernel
                                 threads)
           KERN_PROC_ALL         all user-level processes
           KERN_PROC_PID         processes with process ID arg
           KERN_PROC_PGRP        processes with process group arg
           KERN_PROC_SESSION     processes with session arg
           KERN_PROC_TTY         processes with tty(4) arg
           KERN_PROC_UID         processes with effective user ID arg
           KERN_PROC_RUID        processes with real user ID arg

psのために用意されたようなものだな。

/proc

リナについては、psを見る代わりに有名な、/procを見るか。

proc - ファイルのフォーマットと規約の説明

/procディレクトリを活用する (1/4)

何でもファイルの思想によって、際限なく見せているね。こんなに開陳していいのか?

/proc in *BSD

そう思って、BSD陣営も見ておく。

OpenBSDには、そんなの無い。NetBSDは最初から見える。FreeBSDは、

mount -t procfs proc /proc

すると、見えるようになる。主張が違うんだなあ。で、FreeBSDのやつ。procfs(5) で、興味あるのは、

     status  The process status.  This file is read-only and returns a single
             line containing multiple space-separated fields as follows:

             o   command name
             o   process id
             o   parent process id
             o   process group id
             o   session id
             o   device name of the controlling terminal, or a minus sign
                 ("-") if there is no controlling terminal.
             o   a list of process flags: ctty if there is a controlling
                 terminal, sldr if the process is a session leader, noflags if
                 neither of the other two flags are set.
             o   the process start time in seconds and microseconds, comma
                 separated.
             o   the user time in seconds and microseconds, comma separated.
             o   the system time in seconds and microseconds, comma separated.
             o   the wait channel message
             o   the process credentials consisting of the effective user id
                 and the list of groups (whose first member is the effective
                 group id) all comma separated.
             o   the hostname of the jail in which the process runs, or `-' to
                 indicate that the process is not running within a jail.

ぐらいかな。

sakae@fb:~ % sleep 1000 &
[2] 763
sakae@fb:~ % cat /proc/763/status
sleep 763 695 763 695 pts/1 ctty 1535867324,723778 0,0 0,1244 nanslp 1001 1001 1001,1001,0,5 -

セキュリティーに五月蝿いOpenBSDでも、世間の風には抵抗出来ないだろう。man -k proc とかやってみる。

ob6$ procmap
procmap: /dev/mem: Permission denied
ob6$ doas procmap
procmap: /dev/mem: Operation not permitted

それらしい雰囲気のコマンドが見つかったので、実行してみたけどエラー。諦めてman。

NAME
     procmap - display process memory map

     procmap requires the ability to open /dev/kmem which may be restricted
     based upon the value of the kern.allowkmem sysctl(8).
ob6# sysctl kern.allowkmem=1
sysctl: kern.allowkmem: Operation not permitted

オンラインでも書き換えを許していない。/etc/sysctl.confに設定を書いてからrebootして やっと、有効になる。

ob6$ sleep 1000 &
[1] 28072
ob6$ procmap 28072
procmap: /dev/mem: Permission denied

でも、一般ユーザーには、解放されていない。rootさんの権限を得て、やっと見られるぞ。

ob6$ doas procmap 28072
0000000000001000      0K                     [ anon ]
0000009F63B00000     84K read/exec         /bin/sleep
0000009F63C14000     24K read              /bin/sleep
0000009F63D00000      0K                     [ anon ]
0000009F63D19000      4K read              /bin/sleep
0000009F63D1A000      4K read/write        /bin/sleep
0000009F63D1B000      4K read/write          [ anon ]
0000009F63D1C000     20K read/write          [ anon ]
000000A163D00000      0K                     [ anon ]
000000A1BA903000      4K read/exec           [ uvm_aobj ]
000000A21B317000      4K read/write          [ anon ]
000000A223D85000      4K read                [ anon ]
00007F7FFDEF0000  29696K                     [ stack ]
00007F7FFFBF0000   4032K read/write          [ anon ]
00007F7FFFFE0000     60K read/write          [ anon ]
00007F7FFFFEF000      4K                     [ anon ]
 total             4244K

気持ち、融通を利かせてくれている。リナは巨大な利権だからなあ。そっぽを向くわけにはいかないのね。

     -l           Dumps information in a format like the contents of the maps
                  pseudo-file under the /proc file system which was, in turn,
                  modeled after the similarly named entry in the Linux /proc
                  file system.  When combined with the -v option, identifiers
                  for all entries are printed.

OpenBSD3.5の時に、NetBSDで知られてるpmapをお手本に導入したって説明されてた。大人になれよと諭されたんだな。

procじゃないけど、たまたま見つけた面白いデータ。

ob6$ sysctl -a | grep men
kern.malloc.buckets=16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288
kern.malloc.bucket.16=(calls = 999 total_allocated = 512 total_free = 231 elements = 256 high watermark = 1280 could_free = 0)
kern.malloc.bucket.32=(calls = 1961 total_allocated = 768 total_free = 98 elements = 128 high watermark = 640 could_free = 0)
 :
kern.malloc.bucket.262144=(calls = 0 total_allocated = 0 total_free = 0 elements = 1 high watermark = 5 could_free = 0)
kern.malloc.bucket.524288=(calls = 1 total_allocated = 1 total_free = 0 elements = 1 high watermark = 5 could_free = 0)

カーネル内でメモリーが必要になった時、まさかGC(ガベコレ)やる訳にいかない。どう逃げるかと言うと、メモリーを要求する時、要求サイズに応じて、エリアを分割してるんだな。そうする事によって、メモリーの無駄を無くすとな。それから、メモリーの割り当て、返却処理も高速化されるんかな。

kern.mallocと名の付くものを見ていくと、他にも有った。

kern.malloc.kmemnames=free,,devbuf,,pcb,rtable,,,,ifaddr......
kern.malloc.kmemstat.ifaddr=(inuse = 27, calls = 28, memuse = 8K, limblocks = 0, mapblocks = 0, maxused = 8K, limit = 78644K, spare = 0, sizes = (32,64,128,256,4096))
kern.malloc.kmemstat.mount=(inuse = 2, calls = 2, memuse = 2K, limblocks = 0, mapblocks = 0, maxused = 2K, limit = 78644K, spare = 0, sizes = (1024))

とか、メモリーの使い方とかも、リアルタイムに取れるっぽい。こういうのも広義のprocですかね。

sysctl(2)に、他の面白そうなものが載ってる。と思ったら、コマンドを使えとか言われた。

ob6$ sysctl kern.mbstat
sysctl: use netstat to view kern.mbstat
ob6$ sysctl kern.proc
sysctl: use ps to view kern.proc information

めげずに、こんなのを見る。

ob6$ sysctl kern.timecounter.choice
kern.timecounter.choice=i8254(0) acpihpet0(1000) tsc(2000) acpitimer0(1000) dummy(-1000000)
ob6$ sysctl kern.timecounter.hardware
kern.timecounter.hardware=tsc

時計と言うか計時するカウンターが色々あるよ。数字の大きいやつが優秀ですと。それで、今kernelがどれを使ってるか、教えてもらう。

カーネルの属性だけじゃなく、ハードウェアの状況も、

ob6$ sysctl hw
hw.machine=amd64
hw.model=Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz
hw.ncpu=2
hw.byteorder=1234
hw.pagesize=4096
hw.disknames=cd0:,wd0:42ea0f64e77db907
hw.diskcount=2
hw.sensors.acpiac0.indicator0=On (power supply)
 :

この他にネット関係の情報がいやになる程、取れる。色々チューニングが出来るって事だな。 よって、/procなんて無くてもへっちゃらだい。

voluntary context switch

以前、\timeを調べていた時に忘れていた事があった。自発的なコンテキストスイッチの件。 調べてみると、sched_bsd.c の yield が呼び出されているようなので、BPを貼ってみた。

#0  yield () at /usr/src/sys/kern/sched_bsd.c:297
#1  0xd0700e3e in uvm_pagezero_thread (arg=0xd17722cc) at /usr/src/sys/uvm/uvm_pmemrange.c:2014
#2  0xd089aec9 in proc_trampoline ()
290│ /*
291│  * General yield call.  Puts the current process back on its run queue and
292│  * performs a voluntary context switch.
293│  */
294│ void
295│ yield(void)
296│ {
297│         struct proc *p = curproc;
298│         int s;
299│
300│         NET_ASSERT_UNLOCKED();
301│
302│         SCHED_LOCK(s);
303│         p->p_priority = p->p_usrpri;
304│         p->p_stat = SRUN;
305│         setrunqueue(p);
306├───────> p->p_ru.ru_nvcsw++;
307│         mi_switch();
308│         SCHED_UNLOCK(s);
309│ }

確かに、カウンターの値がインクリメントされてる。

軽くkernの中を調べてみたら、そこそこ呼んでいる所が見つかった。システムコールの一員なのね。

NAME
     sched_yield - yield the processor

DESCRIPTION
     The sched_yield() function makes the current thread yield the processor
     and be put at the end of its run queue without altering its priority.

やる事ないから、CPUの権利を譲るわって、まるで日本人みたいな(最近は、米流に毒されて、がめつい奴らがのさばってきたけど)謙虚なシステムコールだ。

もう、これぐらいにしておくか。最後にまとめ

まとめ

システムプログラム(fork/exec,pipe)

システムプログラム(Signal)

パイプ屋さんが、残ってた。

pipeを使ってプロセス間通信してみる

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
 
int main(int argc, char** argv)
{
    pid_t pid;
    int status;
    int fd;
 
    pid = fork();
 
    if (pid == 0) {
        // ファイルオープン
        fd = open("./output", O_CREAT | O_WRONLY, 0666);
 
        /*
         * オープンしたファイルのディスクリプタを
         * 標準出力に複製する
         */
        dup2(fd, stdout);
        close(fd); // 作成したディスクリプタはクローズ
 
        execl("/bin/ls", "ls", (char *)NULL);
    } 
    waitpid(pid, &status, 0);
    printf ("child process (PID = %d) finished\n", pid);
    return 0;
}

Same as ls >output