2012年12月11日 情報科学類 オペレーティングシステム II 筑波大学 システム情報工学研究科 コンピュータサイエンス専攻, 電子・情報工学系 新城 靖 <yas@cs.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2012/2012-12-11
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.cs.tsukuba.ac.jp/~yas/
システム・コールの処理では、特権命令を必要とする。ライブラリ関数では、 普通は特権命令を使えない(必要な時にはシステム・コールを利用する)。
状態 | 説明 |
---|---|
新規(New) | プロセスが作られつつある。 |
実行待ち(Ready) | CPUがあれば実行できるが CPU がないので実行されていない。CPUが割り当てられるのを待っている。 |
実行中(Running) | CPUが実際に割り当てられ、実行されている。 |
待機中(Waiting、Blocked) | プロセスが、I/Oの完了やシグナルの受信といった事象(event)が 起きてるのを待っている。 |
終了(Terminated) | プロセスが実行を終えた。 |
図? プロセスの5状態
スレッドとは、1つのプロセス(のアドレス空間)の内部にふくまれている論 理的な並列処理の単位。
表示 | 説明 |
STAT | State。状態。 |
PID | Process ID。プロセス1つ1つに重複ないように(unique)割り当てた番号。 |
PPID | Parent PID。親プロセスのPID。 |
UID | User ID。プロセスを生成した利用者の識別子。 |
$ ps l
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
0 1013 24099 24098 15 0 88428 2800 wait Ss pts/1 0:00 -bash
0 1013 24754 24099 17 0 7836 2092 finish T pts/1 0:00 nm /usr/l
0 1013 24755 24099 15 0 65804 896 finish T pts/1 0:00 lv
0 1013 24798 24099 17 0 63480 796 - R+ pts/1 0:00 ps l
$
/proc/PID
; cat /proc/PID/status
1: /* 2: fork-pid.c -- fork() して画面に pid を表示するプログラム 3: ~yas/syspro/proc/fork-pid.c 4: Created on: 2010/12/13 21:19:17 5: */ 6: 7: #include <sys/types.h> /* getpid(), getppid() */ 8: #include <unistd.h> /* getpid(), getppid() */ 9: #include <stdio.h> 10: 11: main() 12: { 13: pid_t pid; 14: fork(); 15: pid = getpid(); 16: printf("pid=%d\n", pid ); 17: }
$ make fork-pid
cc fork-pid.c -o fork-pid
$ ./fork-pid
pid=1005
pid=1006
$ ./fork-pid
pid=1011
pid=1012
$
図? fork()によるプロセス生成と getpid()
1: /* 2: proc-pid-ppid.c -- 画面に pid と ppid を表示するプログラム 3: ~yas/syspro/proc/proc-pid-ppid.c 4: Created on: 2010/12/13 21:00:48 5: */ 6: 7: #include <sys/types.h> /* getpid(), getppid() */ 8: #include <unistd.h> /* getpid(), getppid() */ 9: #include <stdio.h> 10: 11: main() 12: { 13: pid_t pid, ppid; 14: pid = getpid(); 15: ppid = getppid(); 16: printf("pid=%d, ppid=%d\n", pid, ppid ); 17: }
$ make proc-pid-ppid
cc proc-pid-ppid.c -o proc-pid-ppid
$ echo $$
10771
$ ./proc-pid-ppid
pid=10873, ppid=10771
$ ./proc-pid-ppid
pid=10874, ppid=10771
$ ./proc-pid-ppid
pid=10875, ppid=10771
$
1: /* 2: fork-hello.c -- 画面に文字列を表示するプログラム 3: ~yas/syspro/proc/fork-hello.c 4: Start: 2001/05/13 23:19:01 5: */ 6: 7: #include <stdio.h> 8: 9: main() 10: { 11: fork(); 12: fork(); 13: fork(); 14: printf("hello\n"); 15: }
$ make fork-hello
cc fork-hello.c -o fork-hello
$ ./fork-hello
hello
hello
hello
hello
hello
hello
hello
hello
$
図? fork()システム・コールによるプロセスのコピー
Unixでは、全てのファイルやプロセスは、あるユーザの所有物である。 ファイルとプロセスには、UID が付加されている。
1人のユーザが複数のグループに属することができる。
$ id
uid=1013(yas) gid=510(prof) groups=20(games),510(prof),1020(c-admin),1065(c-spec),1150(tebiki)
$
1: 2: /* 3: id-simple.c -- a simple id command 4: Created on: 2009/12/07 22:16:23 5: */ 6: 7: #include <unistd.h> /* getuid(), getgid(), getgroups() */ 8: #include <sys/types.h> /* getuid(), getgid(), getgroups() */ 9: #include <stdio.h> /* printf() */ 10: 11: #define MAXNGROUPS 100 12: 13: main( int argc, char *argv[], char *envp[] ) 14: { 15: uid_t uid ; 16: gid_t gid ; 17: gid_t groups[MAXNGROUPS]; 18: int len, i; 19: uid = getuid(); 20: gid = getgid(); 21: len = getgroups(MAXNGROUPS,&groups[0]); 22: printf("uid=%d gid=%d groups=", uid, gid ); 23: for( i=0; i<len; i++ ) 24: printf("%d,", groups[i]); 25: printf("\n"); 26: }
$ cc id-simple.c -o id-simple
$ ./id-simple
uid=1013 gid=510 groups=20,510,1020,1065,1150,
$
http://www.coins.tsukuba.ac.jp/~yas/coins/literacy-2012/2012-05-15/
http://www.coins.tsukuba.ac.jp/~yas/coins/literacy-2012/2012-05-22/
Linux の特殊事情
1234: struct task_struct { 1235: volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ ... 1307: int exit_state; 1308: int exit_code, exit_signal; ... 1337: struct task_struct __rcu *real_parent; /* real parent process */ 1338: struct task_struct __rcu *parent; /* recipient of SIGCHLD, wait4() reports */ ... 1342: struct list_head children; /* list of my children */ 1343: struct list_head sibling; /* linkage in my parent's children list */ 1344: struct task_struct *group_leader; /* threadgroup leader */ ... 1355: struct pid_link pids[PIDTYPE_MAX]; ... 1379: const struct cred __rcu *cred; /* effective (overridable) subjective task 1380: * credentials (COW) */ 1381: char comm[TASK_COMM_LEN]; /* executable name excluding path 1382: - access with [gs]et_task_comm (which lock 1383: it with task_lock()) 1384: - initialized normally by setup_new_exec */ ... 1592: };
STAT
の部分に現れる。
一般的に、プロセスは、
3つの状態を持つ。
Linux のプロセスの状態はもう少し多い。主に task_struct 構造体の stateと
いうフィールドでプロセスの状態を表ている。(補助的に task_struct の
exit_state も使う)。
linux-3.6.8/include/linux/sched.h 184: #define TASK_RUNNING 0 185: #define TASK_INTERRUPTIBLE 1 186: #define TASK_UNINTERRUPTIBLE 2 187: #define __TASK_STOPPED 4 188: #define __TASK_TRACED 8 189: /* in tsk->exit_state */ 190: #define EXIT_ZOMBIE 16 191: #define EXIT_DEAD 32 192: /* in tsk->state again */ 193: #define TASK_DEAD 64 194: #define TASK_WAKEKILL 128 195: #define TASK_WAKING 256 196: #define TASK_STATE_MAX 512
一般的な状態 | Linuxの状態 | ps表示 | 説明 |
---|---|---|---|
実行待ち(Ready) | TASK_RUNNING | R | 実行可能。CPU が割り当てられていれば実行中。 |
実行中(Running) | TASK_RUNNING | ||
待機中(Waiting、Blocked) | TASK_INTERRUPTIBLE | S | キーボードや他のプロセスからの入力を待っている。 |
TASK_UNINTERRUPTIBLE | D | ディスク入出力などの完了を待っている。割り込み不可。 | |
__TASK_STOPPED, __TASK_TRACED | T | 一時的に停止しているか、デバッグの対象になっている。 | |
終了(Terminated) | TASK_DEAD | Z | 既に終了していて、終了処理の完了を待ってる。 |
pids[]
は、プロセス識別子を保持するための配列。いくつかの種類が
あるが、getpid(),getppid(),fork()
に関連しているものは、0 番目
の pids[PIDTYPE_PID]
。
pids[PIDTYPE_PID] は、pid_link 型で、内部にstruct pid を持つ。struct pid の中には、struct upid があり、その中には getpid() 等で用いる pid を 保持するフィールド nr がある。
linux-3.6.8/include/linux/pid.h 6: enum pid_type 7: { 8: PIDTYPE_PID, ... 12: }; 50: struct upid { ... 52: int nr; ... 55: }; 57: struct pid 58: { ... 62: struct hlist_head tasks[PIDTYPE_MAX]; ... 65: }; 69: struct pid_link 70: { ... 72: struct pid *pid; 73: };
図? プロセスの木構造
図? uid, gid, groups の保持方法
linux-3.6.8/include/linux/cred.h 29: #define NGROUPS_SMALL 32 ... 32: struct group_info { ... 34: int ngroups; ... 36: kgid_t small_block[NGROUPS_SMALL]; ... 38: }; 117: struct cred { ... 126: kuid_t uid; /* real UID of the task */ 127: kgid_t gid; /* real GID of the task */ ... 151: struct group_info *group_info; /* supplementary groups for euid/fsgid */ ... 153: }; include/linux/uidgid.h: 47:typedef gid_t kgid_t; include/linux/types.h: 41:typedef __kernel_gid32_t gid_t; include/asm-generic/posix_types.h:49: typedef unsigned int __kernel_gid32_tkuid_t は、uid をカーネル内で保持するための型。uid_t と同じ32 ビットの unsigned int だが、システム・コールの結果を返す所で、名前空間によるマッ ピングが行われることがある。
直感的には、次のような大域変数があると思ってよい(実際には、CPUごとに異なる値を持つ)。
struct task_struct *current;
図? current変数によるtask_structの参照
linux-3.6.8/kernel/timer.c 1421: SYSCALL_DEFINE0(getpid) 1422: { 1423: return task_tgid_vnr(current); 1424: } linux-3.6.8/include/linux/sched.h 1695: static inline pid_t task_tgid_vnr(struct task_struct *tsk) 1696: { 1697: return pid_vnr(task_tgid(tsk)); 1698: } 1633: static inline struct pid *task_tgid(struct task_struct *task) 1634: { 1635: return task->group_leader->pids[PIDTYPE_PID].pid; 1636: }
linux-3.6.8/kernel/pid.c 483: pid_t pid_vnr(struct pid *pid) 484: { 485: return pid_nr_ns(pid, 省略); 486: } 470: pid_t pid_nr_ns(struct pid *pid, 省略) 471: { 472: struct upid *upid; 473: pid_t nr = 0; .. 476: upid = &pid->numbers[省略]; ... 478: nr = upid->nr; ... 480: return nr; 481: }
linux-3.6.8/kernel/timer.c 1432: SYSCALL_DEFINE0(getppid) 1433: { 1434: int pid; ... 1437: pid = task_tgid_vnr(rcu_dereference(current->real_parent)); ... 1440: return pid; 1441: }currentのreal_parentを引数にして task_tgid_vnr() という関数を呼ぶ。 以降、getpid() と同じ。
rcu_dereference() の dereference は、ポインタが参照している先を取り出す ことを意味する。rcu は、read-copy update の省略形。マルチプロセッサで、 ポインタが変更される可能性がある時でも、ロックなしで読み込むことができ る。意味を考える時には、最初は rcu_dereference() を無視してよい。
$ pwd
/home/prof/yas/os2/linux-3.6.8
$ ls -l ID
-rw-r--r-- 1 yas prof 55798174 12 2 16:17 ID
$ lid chdir
chdir arch/powerpc/include/asm/systbl.h fs/open.c scripts/kconfig/confdata.c
tools/lguest/lguest.c tools/vm/slabinfo.c arch/parisc/hpux/sys_hpux.c arch/um/drivers/cow_user.c
tools/perf/util/run-command.c arch/ia64/kernel/fsys.S arch/parisc/kernel/syscall_table.S
$
$ gid chdir
arch/powerpc/include/asm/systbl.h:18:SYSCALL_SPU(chdir)
fs/open.c:380:SYSCALL_DEFINE1(chdir, const char __user *, filename)
scripts/kconfig/confdata.c:842: if (chdir("include/config"))
scripts/kconfig/confdata.c:936: if (chdir("../.."))
tools/lguest/lguest.c:2026: if (chdir("/") != 0)
tools/lguest/lguest.c:2027: err(1, "chdir(\"/\") failed");
tools/vm/slabinfo.c:1143: if (chdir("/sys/kernel/slab") && chdir("/sys/slab"))
tools/vm/slabinfo.c:1167: if (chdir(de->d_name))
tools/vm/slabinfo.c:1222: chdir("..");
arch/parisc/hpux/sys_hpux.c:493: "chdir",
arch/um/drivers/cow_user.c:158: if (chdir(from)) {
arch/um/drivers/cow_user.c:188: if (chdir(save_cwd)) {
tools/perf/util/run-command.c:100: if (cmd->dir && chdir(cmd->dir))
arch/ia64/kernel/fsys.S:609: data8 0 // chdir
arch/parisc/kernel/syscall_table.S:70: ENTRY_SAME(chdir)
$
$ gid gid_t |egrep typedef
include/linux/types.h:41:typedef __kernel_gid32_t gid_t;
include/linux/uidgid.h:47:typedef gid_t kgid_t;
$
1: #include <stdio.h> 2: #include <unistd.h> 3: 4: main() { 5: pid_t pid, ppid; 6: fork(); 7: pid = getpid(); 8: ppid = getppid(); 9: printf("hello: (pid=%d,ppid=%d)\n",pid, ppid); 10: }以下の空欄(空欄A、空欄B、空欄C、空欄D)を埋めて、起こり得る結果を 1つ作りなさい。
$ echo $$
1001
$ ./fork-printf
hello: (pid=空欄A,ppid=空欄B)
hello: (pid=空欄C,ppid=空欄D)
$
ただし、PID としては、1001,1002,1003,1004 の中から選びなさい。
なお、答えは1通りではない。
上のプログラムをコンパイルして実行した結果を参考にして、回答してもよい。 ただし、PID としては、実行結果のものをそのまま使うのではなく、指定され たものを使いなさい。
なお、実際の getuid() システム・コールの実装は、名前空間の導入により複 雑になっており、今日の授業の範囲を超えている。この課題では、実際のコー ドではなく、この授業の範囲内で答えなさい。(実際のコードをそのまま回答 しても、得点を与えない。)