2011年12月13日
情報科学類 オペレーティングシステム II
筑波大学 システム情報工学研究科
コンピュータサイエンス専攻, 電子・情報工学系
新城 靖
<yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2011/2011-12-13
あるいは、次のページから手繰っていくこともできます。
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 20591 20590 15 0 9224 2696 rt_sig Ss pts/1 0:00 -tcsh
0 1013 20761 20591 16 0 3724 904 finish T pts/1 0:00 nm /usr/lib/l
0 1013 20762 20591 16 0 2720 624 finish T pts/1 0:00 lv
0 1013 20795 20591 16 0 4256 628 - 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-2011/2011-05-10/
http://www.coins.tsukuba.ac.jp/~yas/coins/literacy-2011/2011-05-17/
Linux の特殊事情
1220: struct task_struct {
1221: volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
...
1292: int exit_state;
...
1321: struct task_struct *real_parent; /* real parent process */
1322: struct task_struct *parent; /* recipient of SIGCHLD, wait4() reports */
1323: /*
1324: * children/sibling forms the list of my natural children
1325: */
1326: struct list_head children; /* list of my children */
1327: struct list_head sibling; /* linkage in my parent's children list */
1328: struct task_struct *group_leader; /* threadgroup leader */
...
1339: struct pid_link pids[PIDTYPE_MAX];
...
1363: const struct cred __rcu *cred; /* effective (overridable) subjective task
1364: * credentials (COW) */
...
1367: char comm[TASK_COMM_LEN]; /* executable name excluding path
1368: - access with [gs]et_task_comm (which lock
1369: it with task_lock())
1370: - initialized normally by setup_new_exec */
...
1572: };
STAT の部分に現れる。
一般的に、プロセスは、
3つの状態を持つ。
Linux のプロセスの状態はもう少し多い。主に task_struct 構造体の stateと
いうフィールドでプロセスの状態を表ている。(補助的に task_struct の
exit_state も使う)。
linux-3.1.3/include/linux/sched.h 182: #define TASK_RUNNING 0 183: #define TASK_INTERRUPTIBLE 1 184: #define TASK_UNINTERRUPTIBLE 2 185: #define __TASK_STOPPED 4 186: #define __TASK_TRACED 8 187: /* in tsk->exit_state */ 188: #define EXIT_ZOMBIE 16 189: #define EXIT_DEAD 32 190: /* in tsk->state again */ 191: #define TASK_DEAD 64 192: #define TASK_WAKEKILL 128 193: #define TASK_WAKING 256 194: #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.1.3/include/linux/pid.h
50: struct upid {
...
52: int nr;
...
55: };
57: struct pid
58: {
...
64: struct upid numbers[1];
65: };
...
69: struct pid_link
70: {
...
72: struct pid *pid;
73: };

図? プロセスの木構造

図? uid, gid, groups の保持方法
linux-3.1.3/include/linux/cred.h
28: #define NGROUPS_SMALL 32
...
31: struct group_info {
...
33: int ngroups;
...
35: gid_t small_block[NGROUPS_SMALL];
37: };
...
116: struct cred {
...
125: uid_t uid; /* real UID of the task */
126: gid_t gid; /* real GID of the task */
...
150: struct group_info *group_info; /* supplementary groups for euid/fsgid */
...
152: };
直感的には、次のような大域変数があると思ってよい(実際には、CPUごとに異なる値を持つ)。
struct task_struct *current;

図? current変数によるtask_structの参照
linux-3.1.3/kernel/timer.c
1355: SYSCALL_DEFINE0(getpid)
1356: {
1357: return task_tgid_vnr(current);
1358: }
linux-3.1.3/include/linux/sched.h
1675: static inline pid_t task_tgid_vnr(struct task_struct *tsk)
1676: {
1677: return pid_vnr(task_tgid(tsk));
1678: }
1613: static inline struct pid *task_tgid(struct task_struct *task)
1614: {
1615: return task->group_leader->pids[PIDTYPE_PID].pid;
1616: }
linux-3.1.3/kernel/pid.c
479: pid_t pid_vnr(struct pid *pid)
480: {
481: return pid_nr_ns(pid, 省略);
482: }
...
466: pid_t pid_nr_ns(struct pid *pid, 省略)
467: {
468: struct upid *upid;
469: pid_t nr = 0;
...
472: upid = &pid->numbers[省略];
...
474: nr = upid->nr;
476: return nr;
477: }
linux-3.1.3/kernel/timer.c
1366: SYSCALL_DEFINE0(getppid)
1367: {
1368: int pid;
...
1371: pid = task_tgid_vnr(current->real_parent);
...
1374: return pid;
1375: }
currentのreal_parentを引数にして task_tgid_vnr() という関数を呼ぶ。
以降、getpid() と同じ。
linux-3.1.3/kernel/timer.c
1377: SYSCALL_DEFINE0(getuid)
1378: {
...
1380: return current_uid();
1381: }
current_uid() という関数(マクロ)を呼び出す。
linux-3.1.3/include/linux/cred.h
343: #define current_cred_xxx(xxx) \
344: ({ \
345: current_cred()->xxx; \
346: })
347:
348: #define current_uid() (current_cred_xxx(uid))
current_cred() は、意味としては、current->cred と同じ。
currentからcredを引き、その中の uid というフィールドを返す。
$ pwd
/home/prof/yas/os2/linux-3.1.3
$ lid chdir
chdir arch/powerpc/include/asm/systbl.h fs/open.c scripts/kconfig/confdata.c
tools/slub/slabinfo.c Documentation/virtual/lguest/lguest.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:374:SYSCALL_DEFINE1(chdir, const char __user *, filename)
scripts/kconfig/confdata.c:810: if (chdir("include/config"))
scripts/kconfig/confdata.c:904: if (chdir("../.."))
tools/slub/slabinfo.c:1137: if (chdir("/sys/kernel/slab") && chdir("/sys/slab"))
tools/slub/slabinfo.c:1161: if (chdir(de->d_name))
tools/slub/slabinfo.c:1214: chdir("..");
Documentation/virtual/lguest/lguest.c:2025: if (chdir("/") != 0)
Documentation/virtual/lguest/lguest.c:2026: err(1, "chdir(\"/\") failed");
arch/parisc/hpux/sys_hpux.c:500: "chdir",
arch/um/drivers/cow_user.c:159: if (chdir(from)) {
arch/um/drivers/cow_user.c:189: if (chdir(save_cwd)) {
tools/perf/util/run-command.c:100: if (cmd->dir && chdir(cmd->dir))
arch/ia64/kernel/fsys.S:775: data8 0 // chdir
arch/parisc/kernel/syscall_table.S:70: ENTRY_SAME(chdir)
$
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通りではない。
ヒント: current から出発する。current_cred_xxx() マクロを使っても使わな くてもよい。