/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); }
面白い。そして、更に面白いのがこれ
遊んじゃったな。話を戻そう。
子が死んでも、親は忙しいって言って、子をほったらかしにした図。酒喰らって寝てるんだか、 パチンコに勤しんでいるんだか、秋の競馬に出かけているんだか(指紋認証のカードで、馬券が買えて、馬券データはカードの中。馬券の紛失を防げるし、払い戻しもカードにチャージされるとか、どこまで搾取する積りや >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 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の権利を譲るわって、まるで日本人みたいな(最近は、米流に毒されて、がめつい奴らがのさばってきたけど)謙虚なシステムコールだ。
もう、これぐらいにしておくか。最後にまとめ
まとめ
パイプ屋さんが、残ってた。
#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