プロセスとtask_struct構造体

					2010年12月14日
情報科学類 オペレーティングシステム II

                                       筑波大学 システム情報工学研究科 
                                       コンピュータサイエンス専攻, 電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2010/2010-12-14
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.cs.tsukuba.ac.jp/~yas/

■連絡事項

Softlab 研究室紹介

■今日の大事な話

■補足

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

■プロセスとは

「オペレーティングシステムI」の範囲

◆プロセスの3つの状態

プロセスの基本的な状態
状態 説明
Ready CPUがあれば実行できるが CPU がないので実行されていない
Running CPUが実際に割り当てられ、実行されている
Blocked (waiting) I/O完了待ち、ネットワークやユーザからの入力待ち。

Ready、Running、Blocked

図? プロセスの3状態

◆プロセスとスレッド

スレッドとは、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
$ []

◆その他のコマンド

◆/proc ファイル・システム

/proc の下に、カーネル内のデータを取り出すための疑似的なファイ ルが存在する。特に、/proc/PID の下には、プロセス識別子 がPID のプロセスの情報が現れる。詳しくは、man procを見 なさい。
$ echo $$ [←]
23069
$ ls /proc/$$ [←]
attr             cpuset   fd        maps        numa_maps  schedstat  status
auxv             cwd      io        mem         oom_adj    smaps      task
cmdline          environ  limits    mounts      oom_score  stat       wchan
coredump_filter  exe      loginuid  mountstats  root       statm
$ head -11 /proc/$$/status  [←]
Name:   bash
State:  S (sleeping)
SleepAVG:       98%
Tgid:   23069
Pid:    23069
PPid:   23068
TracerPid:      0
Uid:    1013    1013    1013    1013
Gid:    510     510     510     510
FDSize: 256
Groups: 20 510 1020 1065 1150 
$ []

◆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=11041
pid=11040
$ ./fork-pid [←]
pid=11042
pid=11043
$ []

◆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年生の「コンピュータリテラシ」の試料も、参考になる。 http://www.coins.tsukuba.ac.jp/~yas/coins/literacy-2010/2010-05-11/

■Linux task構造体

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

Linux の特殊事情

◆task_struct構造体

linux-2.6.36/include/linux/sched.h

1163:	struct task_struct {
1164:	        volatile long state;    /* -1 unrunnable, 0 runnable, >0 stopped */
...
1224:	        int exit_state;
...
1251:	        struct task_struct *real_parent; /* real parent process */
1252:	        struct task_struct *parent; /* recipient of SIGCHLD, wait4() reports */
1253:	        /*
1254:	         * children/sibling forms the list of my natural children
1255:	         */
1256:	        struct list_head children;      /* list of my children */
1257:	        struct list_head sibling;       /* linkage in my parent's children list */
1258:	        struct task_struct *group_leader;       /* threadgroup leader */
...
1268:	        /* PID/PID hash table linkage. */
1269:	        struct pid_link pids[PIDTYPE_MAX];
1270:	        struct list_head thread_group;
...
1290:	/* process credentials */
1291:	        const struct cred *real_cred;   /* objective and real subjective task
1292:	                                         * credentials (COW) */
1293:	        const struct cred *cred;        /* effective (overridable) subjective task
1294:	                                         * credentials (COW) */
...
1300:	        char comm[TASK_COMM_LEN]; /* executable name excluding path
1301:	                                     - access with [gs]et_task_comm (which lock
1302:	                                       it with task_lock())
1303:	                                     - initialized normally by setup_new_exec */
...
1497:	};

◆state

プロセスの状態。psコマンドで STAT の部分に現れる。 一般的に、プロセスは、 3つの状態を持つ。 Linux のプロセスの状態はもう少し多い。主に task_struct 構造体の stateと いうフィールドでプロセスの状態を表ている。(補助的に task_struct の exit_state も使う)。
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
Blocked TASK_INTERRUPTIBLE Sキーボードや他のプロセスからの入力を待っている。
TASK_UNINTERRUPTIBLE Dディスク入出力などの完了を待っている。割り込み不可。
__TASK_STOPPED, __TASK_TRACED T一時的に停止しているか、デバッグの対象になっている。
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 がある。

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の保存方法

◆struct cred

task_struct に保存すべき内容のうち、uid、gid、groups 等を抜き出したもの。
include/linux/cred.h

  25:	/*
  26:	 * COW Supplementary groups list
  27:	 */
  28:	#define NGROUPS_SMALL           32
  29:	#define NGROUPS_PER_BLOCK       ((unsigned int)(PAGE_SIZE / sizeof(gid_t)))
  30:	
  31:	struct group_info {
  32:	        atomic_t        usage;
  33:	        int             ngroups;
  34:	        int             nblocks;
  35:	        gid_t           small_block[NGROUPS_SMALL];
  36:	        gid_t           *blocks[0];
  37:	};
...
 116:	struct cred {
 117:	        atomic_t        usage;
 118:	#ifdef CONFIG_DEBUG_CREDENTIALS
 119:	        atomic_t        subscribers;    /* number of processes subscribed */
 120:	        void            *put_addr;
 121:	        unsigned        magic;
 122:	#define CRED_MAGIC      0x43736564
 123:	#define CRED_MAGIC_DEAD 0x44656144
 124:	#endif
 125:	        uid_t           uid;            /* real UID of the task */
 126:	        gid_t           gid;            /* real GID of the task */
 127:	        uid_t           suid;           /* saved UID of the task */
 128:	        gid_t           sgid;           /* saved GID of the task */
 129:	        uid_t           euid;           /* effective UID of the task */
 130:	        gid_t           egid;           /* effective GID of the task */
...
 148:	        struct user_struct *user;       /* real user ID subscription */
 149:	        struct group_info *group_info;  /* supplementary groups for euid/fsgid */
...
 151:	};

◆uidとgid

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

◆euidとegid

effective uid と effective gid。

Unix には、set-uid のプログラムがある。ls -l で user の x の所が s になっ ている。

$ ls -l /usr/bin | egrep '^...s' [←]
...
-rwsr-xr-x 1 root root       49392 Jan 27  2010 at
...
-rwsr-sr-x 1 root root      315432 Jan  6  2010 crontab
-rwsr-xr-x 1 root root       51576 Mar 31  2010 gpasswd
...
---s--x--x 2 root root      180448 Oct 19 22:11 sudo
...
$ []
このようなプログラムを実行すると、一時的にファイル所有者(この例では root)の権限でプログラムが実行される。パスワードを変更したり、 ディスク・クォータを表示する時に、root の権限を使う。

◆group_info

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

■current

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

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

kernel/timer.c

1351:	SYSCALL_DEFINE0(getpid)
1352:	{
1353:	        return task_tgid_vnr(current);
1354:	}

include/linux/sched.h
1600:	static inline pid_t task_tgid_vnr(struct task_struct *tsk)
1601:	{
1602:	        return pid_vnr(task_tgid(tsk));
1603:	}

1538:	static inline struct pid *task_tgid(struct task_struct *task)
1539:	{
1540:	        return task->group_leader->pids[PIDTYPE_PID].pid;
1541:	}

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

kernel/pid.c
 474:	pid_t pid_vnr(struct pid *pid)
 475:	{
 476:	        return pid_nr_ns(pid, 省略);
 477:	}
...
 461:	pid_t pid_nr_ns(struct pid *pid, 省略)
 462:	{
 463:	        struct upid *upid;
 464:	        pid_t nr = 0;
...
 467:	                upid = &pid->numbers[省略];
...
 469:	                        nr = upid->nr;
 471:	        return nr;
 472:	}

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

kernel/timer.c
1362:	SYSCALL_DEFINE0(getppid)
1363:	{
1364:	        int pid;
...
1367:	        pid = task_tgid_vnr(current->real_parent);
...
1370:	        return pid;
1371:	}
currentのreal_parentを引数にして task_tgid_vnr() という関数を呼ぶ。 以降、getpid() と同じ。

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

kernel/timer.c
1373:	SYSCALL_DEFINE0(getuid)
1374:	{
1375:	        /* Only we change this so SMP safe */
1376:	        return current_uid();
1377:	}
current_uid() という関数(マクロ)を呼び出す。

include/linux/cred.h

 342:	#define current_cred_xxx(xxx)                   \
 343:	({                                              \
 344:	        current->cred->xxx;                     \
 345:	})
 346:	
 347:	#define current_uid()           (current_cred_xxx(uid))
currentからcredを引き、その中の uid というフィールドを返す。

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

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

オペレーティング・システムでは、「一般に」プロセスは、Ready、Running、 Blockedの状態を持つ。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: 2010/12/21 16:47:46
Yasushi Shinjo / <yas@is.tsukuba.ac.jp>