プロセスとtask_struct構造体

					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/

■連絡事項

Softlab 研究室紹介

■今日の大事な話

■補足

システム・コールの処理では、特権命令を必要とする。ライブラリ関数では、 普通は特権命令を使えない(必要な時にはシステム・コールを利用する)。

■プロセスとは

「オペレーティングシステムI」の復習

◆プロセスの3つの状態

基本は3つ。新規と終了を入れたら5つ。
プロセスの基本的な状態
状態 説明
新規(New) プロセスが作られつつある。
実行待ち(Ready) CPUがあれば実行できるが CPU がないので実行されていない。CPUが割り当てられるのを待っている。
実行中(Running) CPUが実際に割り当てられ、実行されている。
待機中(Waiting、Blocked) プロセスが、I/Oの完了やシグナルの受信といった事象(event)が 起きてるのを待っている。
終了(Terminated) プロセスが実行を終えた。

New、Ready、Running、Waiting、Terminated

図? プロセスの5状態

◆プロセスとスレッド

スレッドとは、1つのプロセス(のアドレス空間)の内部にふくまれている論 理的な並列処理の単位。

シングルスレッドのプログラム
1度に1つの手続き(Cの関数)しか動かない。
マルチスレッドのプログラム
1度にスレッドの数だけの手続きが論理的には同時に動く。 (同期でブロックされているものも含む)
スレッドはプロセスの機能のうち、保護の機能を抜いて高速化したもの。歴史 的には、SMP (Symmetric multiprocessor) での効率的な並列処理のために導入 された。同一プロセスに属するスレッドは、アドレス空間やメモリを共有する。 生成、消滅、スレッド間通信が高速に実現される。

■利用者から見たプロセスの見え方

次の3つのレベルで見える

◆Unixにおけるプロセスに関するシステム・コール(一部)

◆ps -lコマンド

ps -l の結果のうち、次の属性に注目。IDは、identifierの意味。
表示 説明
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
$ []

◆その他のコマンド

◆fork() と pid を調べるプログラム

   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()してprintf()するプログラム

図? fork()によるプロセス生成と getpid()

◆fork() と pid を調べるプログラム

pid
   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
$ []

◆fork()によるプロセスの木

   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() を 3 回するプログラム

図? fork()システム・コールによるプロセスのコピー

◆ユーザ

ユーザ(user, 利用者)とは、Unixの外では、個人(人間)。 Unixの内部では、次のどれかで表現する。
ユーザ名(user name)
文字列
UID(user ID, user identifier)
16ビット-32ビットの整数

Unixでは、全てのファイルやプロセスは、あるユーザの所有物である。 ファイルとプロセスには、UID が付加されている。

◆グループ

グループ(group)とは、Unixの外の世界では、計算機を使う人間の集合。 Unixの内部では、次のどれかで表現する。
グループ名(group name)
文字列
UID(user ID, user identifier)
16ビット-32ビットの整数

1人のユーザが複数のグループに属することができる。

◆プロセスの属性

プロセスは、UID (1個) と GID を複数個持つ。

◆idコマンド

idコマンドを使うと、そのプロセス(の親プロセスであるシェルから継承した) UIDとGIDが調べられる。
$ id [←]
uid=1013(yas) gid=510(prof) groups=20(games),510(prof),1020(c-admin),1065(c-spec),1150(tebiki)
$ []

◆id-simple.c

id コマンドは、内部的にはgetuid(), getgid(), getgroups() システム・コー ルを使っている。以下は、id コマンドの簡易版である。
   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,
$ []

◆UID、GIDとアクセス制御

1年生の「コンピュータリテラシ」の試料も、参考になる。

■Linux task構造体

利用者レベルの視点では、プロセスは、メモリに読み込まれてCPUが割り当てら れれば実行可能な「プログラム」である。メタレベルでは、「データ」になる。 Linux のプロセスは、task_struct 構造体というデータ構造で表現されている。

Linux の特殊事情

◆task_struct構造体

linux-3.1.3/include/linux/sched.h

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:	};


◆state

プロセスの状態。psコマンドで 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既に終了していて、終了処理の完了を待ってる。

◆pid_t型

pid_t 型は、システム・コールのレベルで(APIとして) プロセス識別子を表す 型。int型を typedef で名前を付けている。1から32,768までで明いている部分 を再利用する。(32,768を超えたら、1に戻って開いている番号を探して使う。)

◆struct pidとstruct upid

APIとしてのプロセス識別子を、カーネルの中で表現するための構造体。

◆pids[]

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:	};

◆char comm[TASK_COMM_LEN]

command名。最大16文字。

◆プロセスの木構造

real_parent, children, sibling というフィールドでプロセスの木構造が作られる。

task_struct、parent、child、sibling

図? プロセスの木構造

◆struct task_struct *real_parent

親プロセス(fork() システム・コールでコピー元のプロセス。)のtask_struct構造体へのポインタ。 親が先に死んだ場合には、initプロセスに養子に出される。

◆struct task_struct *parent

ptrace で、トレースしているプロセス(デバッガ等)。

◆struct list_head children

子プロセスのリストの先頭。

◆struct list_head sibling

兄弟プロセス(つまり、同じ親プロセスの子プロセス)のリスト。

◆struct cred *real_cred

credentials。資格。ファイルをアクセスする時等にプロセスがどのような権限 を持っているかを表す。

task_struct構造体、cred構造体、group_info構造体

図? uid, gid, groups の保持方法

◆struct cred

task_struct に保存すべき内容のうち、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:	};

◆uidとgid

uidは、ユーザを識別する番号、gidは、グループを識別する番号を保持する。 ファイルを開く時等のアクセス権のチェックに使われる。

◆group_info

追加のgid (getgroups()) を保持する。

■current

変数 current は、カーネル中で現在実行中のプロセスの task_struct 構造体 を保持する。さまざまなシステム・コールで、current を参照 する。

直感的には、次のような大域変数があると思ってよい(実際には、CPUごとに異なる値を持つ)。

struct task_struct *current; 

current→task_struct

図? current変数によるtask_structの参照

◆getpid()システム・コール

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:	}

group_leader は、ユーザレベルで同じプロセスに属する、カーネル・レベルの スレッドの先頭のものを指す。group_leader は、どのカーネル・レベルのスレッ ドから調べても、同じ struct task_struct を指している。

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:	}

◆getppid()システム・コール

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() と同じ。

◆getuid()システム・コール

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 というフィールドを返す。

■idutils

大きなソースコードを読むには、grepやgrep -r よりも idutils パッケージが便利。
mkid {filenames}
make id。ソースコードを解析して索引ファイル (./ID という名前) を作る。一度だけやれば十分。
lid id
list id。引数で与えられた id を含むファイルを表示する。
lid -R grep id、または、gid id
grep 形式での検索。
実行例
$ 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)
$ []

■クイズ2 プロセスとtask_struct構造体

★問題(201) Ready状態のプロセス

オペレーティング・システムでは、「一般に」プロセスは、 実行待ち(Ready)、実行中(Running)、 待機中(Waiting、Blocked)という3つの 状態を持つ。Linux において、プロセスが Ready 状態であることを 示すために、task struct のフィールド state に、何という値を設定しているか。

★問題(202) pidとppid

次のプログラムをシェルから実行したとする。
   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通りではない。

★問題(203) getgid()システム・コール

getuid() システム・コールに習って getgid() システム・コールを実装してみなさい。

ヒント: current から出発する。current_cred_xxx() マクロを使っても使わな くてもよい。


Last updated: 2011/12/12 15:39:33
Yasushi Shinjo / <yas@is.tsukuba.ac.jp>