プロセス、リンク、task_struct構造体

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

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

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

■今日の大事な話

■補足

viola01..06 が稼働している。Intel Nehalem。16 CPU に見える。実際には、 4コアx2個。

◆main() の引数

シェルから main 関数の引数(argument)を与えることができる。
  1. argc: 引数の数
  2. argv: 引数の配列(vector)。0番目は、コマンドの名前。
  3. envp: 環境変数の配列。大域変数environと同じ。
これらの引数は、 execve() システム・コール の第2引数と第3引数と関連している。 以下のプログラムは、main()の引数を表示するプログラムである。
   1:	/*
   2:	        arg-print.c -- mainの引数を表示するプログラム
   3:	        ~yas/syspro/proc/arg-print.c
   4:	        Start: 1997/04/21 18:23:13
   5:	*/
   6:	
   7:	#include <stdio.h>
   8:	
   9:	main( int argc, char *argv[], char *envp[] )
  10:	{
  11:	    int i ;
  12:	        printf("&argc == 0x%x, argc == %d\n", &argc, argc );
  13:	        printf("&argv == 0x%x, argv == 0x%x\n",&argv, argv );
  14:	        for( i=0 ; argv[i] ; i++ )
  15:	            printf("argv[%d]==0x%x, \"%s\"\n",i,argv[i],argv[i] );
  16:	}
% cp ~yas/syspro/proc/arg-print.c . [←]
% cc arg-print.c -o arg-print [←]
% ./arg-print a b c [←]
&argc == 0x6f461d2c, argc == 4
&argv == 0x6f461d20, argv == 0x6f461e28
argv[0]==0x6f4639c7, "./arg-print"
argv[1]==0x6f4639d3, "a"
argv[2]==0x6f4639d5, "b"
argv[3]==0x6f4639d7, "c"
% ./arg-print "a b c" [←]
&argc == 0x33c0ba2c, argc == 2
&argv == 0x33c0ba20, argv == 0x33c0bb28
argv[0]==0x33c0d9c7, "./arg-print"
argv[1]==0x33c0d9d3, "a b c"
% []

■実行形式

前回は、nmコマンドまで。 今回は、libcから。

◆libc

印刷資料は、先週配布済み。

■プロセスを表現する構造体

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

◆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 $$ [←]
12507
% 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:   tcsh
State:  S (sleeping)
SleepAVG:       98%
Tgid:   12507
Pid:    12507
PPid:   12506
TracerPid:      0
Uid:    1013    1013    1013    1013
Gid:    510     510     510     510
FDSize: 64
Groups: 20 510 1020 1065 1150 
% []

◆fork-pid-ppid.c

PID と PPID を調べるプログラム。
   1:	/*
   2:	        fork-pid-ppid.c -- 画面に pid と ppid を表示するプログラム
   3:	        ~yas/syspro/proc/fork-pid-ppid.c
   4:	        Created on: 2009/12/08 00:50:25
   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:	        fork();
  15:	        pid = getpid();
  16:	        ppid = getppid();
  17:	        printf("pid=%d, ppid=%d\n", pid, ppid );
  18:	}
  19:	
% cc fork-pid-ppid.c -o fork-pid-ppid [←]
% echo $$ [←]
11080
% ./fork-pid-ppid [←]
pid=11650, ppid=11080
pid=11651, ppid=11650
% ./fork-pid-ppid [←]
pid=11652, ppid=11080
pid=11653, ppid=11652
% ./fork-pid-ppid [←]
pid=11654, ppid=11080
pid=11655, ppid=11654
% ./fork-pid-ppid [←]
pid=11658, ppid=11657
pid=11657, ppid=11080
% []
PID と PPID により、プロセスの木構造が作られる。pstree コマンドで表示できる。

◆idコマンド

idコマンドを使うと、そのプロセスの(親プロセスであるシェルから継承した) 属性が調べられる。
% id [←]
uid=1013(yas) gid=510(prof) groups=20(games),510(prof),1020(c-admin),1065(c-spec),1150(tebiki)
% []
getuid(), getgid(), getgroups() システム・コールを使っている。

◆id-simple.c

   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,
% []

■Linux task構造体

Linux のプロセスは、task 構造体で表現されている。

◆task_struct構造体

<省略> linux-2.6.31.6

include/linux/sched.h
1166: struct task_struct {
1167:         volatile long state;    /* -1 unrunnable, 0 runnable, >0 stopped */
...
1220: 
1223: /* task state */
1224:         struct linux_binfmt *binfmt;
1225:         int exit_state;
...
1233:         pid_t pid;
1234:         pid_t tgid;
1244:         struct task_struct *real_parent; /* real parent process */
1245:         struct task_struct *parent; /* recipient of SIGCHLD, wait4() reports */
...
1249:         struct list_head children;      /* list of my children */
1250:         struct list_head sibling;       /* linkage in my parent's children list */
1251:         struct task_struct *group_leader;       /* threadgroup leader */
...
1267:         /* PID/PID hash table linkage. */
1268:         struct pid_link pids[PIDTYPE_MAX];
1269:         struct list_head thread_group;
...
1288:         const struct cred *real_cred;   /* objective and real subjective task
1289:                                          * credentials (COW) */
1290:         const struct cred *cred;        /* effective (overridable) subjective task
1291:                                          * credentials (COW) */
...
1296:         char comm[TASK_COMM_LEN]; /* executable name excluding path
1297:                                      - access with [gs]et_task_comm (which lock
1298:                                        it with task_lock())
1299:                                      - initialized normally by flush_old_exec */
...
1483: };

◆state

プロセスの状態。psコマンドで STAT の部分に現れる。 一般的に、プロセスの基本は、3状態。
状態 説明
Ready CPUがあれば実行できる
Running CPUが割り当てられ、実行中
Blocked (waiting) I/O完了待ち、ネットワークやユーザからの入力待ち。

Ready、Running、Blocked

図? プロセスの3状態

Linux のプロセスの状態はもう少し多い。 task_struct 構造体の state, exit_state の2つの変数で表現している。
include/linux/sched.h 
 180: #define TASK_RUNNING            0
 181: #define TASK_INTERRUPTIBLE      1
 182: #define TASK_UNINTERRUPTIBLE    2
 183: #define __TASK_STOPPED          4
 184: #define __TASK_TRACED           8
 185: /* in tsk->exit_state */
 186: #define EXIT_ZOMBIE             16
 187: #define EXIT_DEAD               32
 188: /* in tsk->state again */
 189: #define TASK_DEAD               64
 190: #define TASK_WAKEKILL           128
一般的な状態 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

プロセス識別子。pid_t は、int型。1から32,768までで明いている部分を再利用。 (32,768を超えたら、1に戻って開いている番号を探して使う。)

◆pid_t tpid

プロセス識別子。普通は、pid と同じ値になる。 clone() という Linux 独自のシステム・コールで CLONE_THREAD が指定された時だけ異なる。

◆struct linux_binfmt *binfmt

binary の意味。主に、ELF 形式であることを示す。

◆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
  30: struct group_info {
  31:         atomic_t        usage;
  32:         int             ngroups;
  33:         int             nblocks;
  34:         gid_t           small_block[NGROUPS_SMALL];
  35:         gid_t           *blocks[0];
  36: };
...
 115: struct cred {
 116:         atomic_t        usage;
 117:         uid_t           uid;            /* real UID of the task */
 118:         gid_t           gid;            /* real GID of the task */
 119:         uid_t           suid;           /* saved UID of the task */
 120:         gid_t           sgid;           /* saved GID of the task */
 121:         uid_t           euid;           /* effective UID of the task */
 122:         gid_t           egid;           /* effective GID of the task */
...
 140:         struct user_struct *user;       /* real user ID subscription */
 141:         struct group_info *group_info;  /* supplementary groups for euid/fsgid */
...
 143: };

◆uidとgid

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

◆euidとegid

effective uid と effective gid。

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

% ls -l /usr/bin | egrep '^...s' [←]
...
-r-sr-xr-x   1 root   wheel        70352 Jun 19 11:39 passwd
-r-sr-xr-x@  1 root   wheel        47296 Feb 24  2009 quota
...
-r-sr-xr-x@  1 root   wheel        48160 May 31  2008 su
-rwsr-xr-x@  1 root   wheel       149024 May 31  2008 top
...
% []
このようなプログラムを実行すると、一時的にファイル所有者(この例では root)の権限でプログラムが実行される。パスワードを変更したり、 ディスク・クォータを表示する時に、root の権限を使う。

◆group_info

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

■current

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

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

include/linux/syscalls.h:
137:#define SYSCALL_DEFINE0(name)         asmlinkage long sys_##name(void)

kernel/timer.c:
1233: SYSCALL_DEFINE0(getpid)
1234: {
1235:         return task_tgid_vnr(current);
1236: }
currentを引数にして task_tgid_vnr() という関数を呼ぶ。

include/linux/sched.h
1586: static inline pid_t task_tgid_vnr(struct task_struct *tsk)
1587: {
1588:         return pid_vnr(task_tgid(tsk));
1589: }
引数で与えられた task_struct 構造体の tgid を返す。(実際にはもう少し複雑。)

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

kernel/timer.c
1244: SYSCALL_DEFINE0(getppid)
1245: {
1246:         int pid;
1247: 
1248:         rcu_read_lock();
1249:         pid = task_tgid_vnr(current->real_parent);
1250:         rcu_read_unlock();
1251: 
1252:         return pid;
1253: }
currentのreal_parentを引数にして task_tgid_vnr() という関数を呼ぶ。

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

kernel/timer.c
1255: SYSCALL_DEFINE0(getuid)
1256: {
1257:         /* Only we change this so SMP safe */
1258:         return current_uid();
1259: }
current_uid() という関数(マクロ)を呼び出す。

include/linux/sched.h
 305: #define current_cred_xxx(xxx)                   \
 306: ({                                              \
 307:         current->cred->xxx;                     \
 308: })
 309: 
 310: #define current_uid()           (current_cred_xxx(uid))
currentからcredを引き、その中の uid というフィールドを返す。

■クイズ2 プロセス、リンク、task_struct構造体

★問題(201) pidとppid

次のプログラムを同じシェルから2回連続して実行したとする。
   1:   main() {
   2:           printf("pid=%d, ppid=%d\n",getpid(), getppid());
   3:   }
% ./a.out [←]
pid=空欄A, ppid=空欄B
% ./a.out [←]
pid=空欄C, ppid=空欄D
% []
結果は、実行する度に異なる。結果として起こりえる例を1つ作りなさい。た だし、ただし、PID としては、 1001,1002,1003,1004,1005,1006,1007,1008,1009,1010 の中から選びなさい。 また、短期間に連続してプログラムを実行するために、PID はオーバーフロー しないものとする。

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

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

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


Last updated: 2010/01/25 17:48:05
Yasushi Shinjo / <yas@is.tsukuba.ac.jp>