2009年12月22日
情報科学類 オペレーティングシステム II
筑波大学 システム情報工学研究科
コンピュータサイエンス専攻, 電子・情報工学系
新城 靖
<yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/literacy-2009/2009-12-22
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.cs.tsukuba.ac.jp/~yas/
vfork() は、直後に execve() を行う時にコピーの一部をさぼるような fork()。copy-on-write が一般的な時代は、普通の fork() もコピーをさぼる ので、存在意義が薄くなった。
`http://www.ibm.com/developerworks/jp/linux/library/l-gcc-hacks/',M. Tim Jones、Linux カーネルのなかに入り込む GCC、IBM Developer Works、2008年 11月 18日
arch/x86/kernel/process.c
232: int sys_fork(struct pt_regs *regs)
233: {
234: return do_fork(SIGCHLD, regs->sp, regs, 0, NULL, NULL);
235: }
arch/の下にある。
kernel/fork.c
1339: long do_fork(unsigned long clone_flags,
1340: unsigned long stack_start,
1341: struct pt_regs *regs,
1342: unsigned long stack_size,
1343: int __user *parent_tidptr,
1344: int __user *child_tidptr)
1345: {
1346: struct task_struct *p;
...
1388: p = copy_process(clone_flags, stack_start, regs, stack_size,
1389: child_tidptr, NULL, trace);
...
1394: if (!IS_ERR(p)) {
1395: struct completion vfork;
...
1399: nr = task_pid_vnr(p);
...
1428: wake_up_new_task(p, clone_flags);
...
1440: } else {
1441: nr = PTR_ERR(p);
1442: }
1443: return nr;
1444: }
938: static struct task_struct *copy_process(unsigned long clone_flags,
939: unsigned long stack_start,
940: struct pt_regs *regs,
941: unsigned long stack_size,
942: int __user *child_tidptr,
943: struct pid *pid,
944: int trace)
945: {
...
972: retval = -ENOMEM;
973: p = dup_task_struct(current);
974: if (!p)
975: goto fork_out;
...
985: retval = -EAGAIN;
...
1015: INIT_LIST_HEAD(&p->children);
1016: INIT_LIST_HEAD(&p->sibling);
...
1089: sched_fork(p, clone_flags);
..
1100: if ((retval = copy_files(clone_flags, p)))
1101: goto bad_fork_cleanup_semundo;
1102: if ((retval = copy_fs(clone_flags, p)))
1103: goto bad_fork_cleanup_files;
1104: if ((retval = copy_sighand(clone_flags, p)))
1105: goto bad_fork_cleanup_fs;
1106: if ((retval = copy_signal(clone_flags, p)))
1107: goto bad_fork_cleanup_sighand;
1108: if ((retval = copy_mm(clone_flags, p)))
1109: goto bad_fork_cleanup_signal;
1110: if ((retval = copy_namespaces(clone_flags, p)))
1111: goto bad_fork_cleanup_mm;
...
1118: if (pid != &init_struct_pid) {
1119: retval = -ENOMEM;
1120: pid = alloc_pid(p->nsproxy->pid_ns);
...
1129: }
1130:
1131: p->pid = pid_nr(pid);
1132: p->tgid = p->pid;
...
1174: p->exit_state = 0;
...
1212: p->real_parent = current;
...
1241: if (likely(p->pid)) {
1242: list_add_tail(&p->sibling, &p->real_parent->children);
...
1259: }
...
1267: return p;
...
1310: fork_out:
1311: return ERR_PTR(retval);
1312: }
212: static struct task_struct *dup_task_struct(struct task_struct *orig)
213: {
214: struct task_struct *tsk;
216: unsigned long *stackend;
...
222: tsk = alloc_task_struct();
...
232: err = arch_dup_task_struct(tsk, orig);
...
257: return tsk;
...
263: }
...
205: int __attribute__((weak)) arch_dup_task_struct(struct task_struct *dst,
206: struct task_struct *src)
207: {
208: *dst = *src;
209: return 0;
210: }
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
%

図? 親プロセスと子プロセスの実行(非同期)
親プロセスに対して、exit status という小さな値(0..254)を返す事ができる。 シェルでは、直前のコマンドの exit status を $? という変数で調べることができる。
% tcsh
[yas@viola01 proc]$ exit 300
exit
% echo $?
44
% tcsh
[yas@viola01 proc]$ exit 254
exit
% echo $?
254
%
Unix のコマンドの多くは、成功すると exit( 0 )し、失敗すると 0 以外を返す(C言語の true/false とは逆)。
% ls /bin/ls
/bin/ls
% echo $?
0
% ls /bin/nocommand
ls: /bin/nocommand: No such file or directory
% echo $?
2
%
exit status は、シェルの if 文で使うことができる。
tcsh, csh の場合
% /bin/true
% echo $?
0
% /bin/false
% echo $?
1
% if ( { /bin/true } ) echo ok
ok
% if ( { /bin/false } ) echo ok
%
sh, bash の場合
$ if /bin/true; then echo ok; fi
ok
$ if /bin/false; then echo ok; fi
$
1: /*
2: fork-pid-ppid-wait.c -- 画面に pid と ppid を表示するプログラム(wait付)
3: ~yas/syspro/proc/fork-pid-ppid-wait.c
4: Created on: 2009/12/20 16:04:41
5: */
6:
7: #include <sys/types.h> /* getpid(), getppid() */
8: #include <unistd.h> /* getpid(), getppid() */
9: #include <sys/wait.h> /* wait() */
10: #include <stdlib.h> /* exit() */
11: #include <stdio.h>
12:
13: main()
14: {
15: pid_t pid, ppid, child_pid;
16: int status;
17: child_pid = fork();
18: if( child_pid > 0 )
19: {
20: /* parent process */
21: if( wait( &status ) < 0 )
22: {
23: perror("wait");
24: }
25: printf("child exited with %d (%d)\n",
26: WEXITSTATUS(status),status );
27: }
28: else if( child_pid == 0 )
29: {
30: /* child process */
31: /* fall through */
32: }
33: else
34: {
35: perror("fork");
36: exit( 1 );
37: }
38: pid = getpid();
39: ppid = getppid();
40: printf("pid=%d, ppid=%d\n", pid, ppid );
41: exit( 100 );
42: }
% cc fork-pid-ppid-wait.c -o fork-pid-ppid-wait
% ./fork-pid-ppid-wait
pid=32423, ppid=32422
child exited with 100 (25600)
pid=32422, ppid=11080
% ./fork-pid-ppid-wait
pid=32425, ppid=32424
child exited with 100 (25600)
pid=32424, ppid=11080
% ./fork-pid-ppid-wait
pid=32427, ppid=32426
child exited with 100 (25600)
pid=32426, ppid=11080
% echo $?
100
%
WEXITSTATUS() マクロを使って
得られる値が exit status 。

図? 親プロセスと子プロセスの実行(waitシステム・コールによる同期付き)
# ifdef __USE_BSD ... # else /* Don't use BSD. */ # define __WAIT_INT(status) (status) # define __WAIT_STATUS int * # define __WAIT_STATUS_DEFN int * # endif /* Use BSD. */ # include <bits/waitstatus.h> # define WEXITSTATUS(status) __WEXITSTATUS(__WAIT_INT(status)) # define WTERMSIG(status) __WTERMSIG(__WAIT_INT(status)) # define WSTOPSIG(status) __WSTOPSIG(__WAIT_INT(status)) # define WIFEXITED(status) __WIFEXITED(__WAIT_INT(status)) #endif /* <stdlib.h> not included. *//usr/include/bits/waitstatus.h
#define __WEXITSTATUS(status) (((status) & 0xff00) >> 8) #define __WTERMSIG(status) ((status) & 0x7f) #define __WSTOPSIG(status) __WEXITSTATUS(status) #define __WIFEXITED(status) (__WTERMSIG(status) == 0)
kernel/exit.c
1032: SYSCALL_DEFINE1(exit, int, error_code)
1033: {
1034: do_exit((error_code&0xff)<<8);
1035: }
kernel/exit.c
888: NORET_TYPE void do_exit(long code)
889: {
...
953: tsk->exit_code = code;
...
956: exit_mm(tsk);
...
963: exit_files(tsk);
964: exit_fs(tsk);
...
984: exit_notify(tsk, group_dead);
..
1012: tsk->state = TASK_DEAD;
1013: schedule();
1014: BUG();
...
1018: }
803: static void exit_notify(struct task_struct *tsk, int group_dead)
804: {
...
843: if (signal >= 0)
844: signal = do_notify_parent(tsk, signal);
846: tsk->exit_state = signal == DEATH_REAP ? EXIT_DEAD : EXIT_ZOMBIE;
859: if (signal == DEATH_REAP)
860: release_task(tsk);
861: }
pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status, int options); int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options); pid_t wait3(int *status, int options, struct rusage *rusage); pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage);最終的には、すべて wait4() を呼ぶ。
kernel/exit.c
1689: SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr,
1690: int, options, struct rusage __user *, ru)
1691: {
1692: struct wait_opts wo;
1693: struct pid *pid = NULL;
1694: enum pid_type type;
1695: long ret;
1696:
1697: if (options & ~(WNOHANG|WUNTRACED|WCONTINUED|
1698: __WNOTHREAD|__WCLONE|__WALL))
1699: return -EINVAL;
1700:
1701: if (upid == -1)
1702: type = PIDTYPE_MAX;
1703: else if (upid < 0) {
1704: type = PIDTYPE_PGID;
1705: pid = find_get_pid(-upid);
1706: } else if (upid == 0) {
1707: type = PIDTYPE_PGID;
1708: pid = get_task_pid(current, PIDTYPE_PGID);
1709: } else /* upid > 0 */ {
1710: type = PIDTYPE_PID;
1711: pid = find_get_pid(upid);
1712: }
1713:
1714: wo.wo_type = type;
1715: wo.wo_pid = pid;
1716: wo.wo_flags = options | WEXITED;
1717: wo.wo_info = NULL;
1718: wo.wo_stat = stat_addr;
1719: wo.wo_rusage = ru;
1720: ret = do_wait(&wo);
...
1725: return ret;
1726: }
kernel/exit.c
1563: static long do_wait(struct wait_opts *wo)
1564: {
1565: DECLARE_WAITQUEUE(wait, current);
1566: struct task_struct *tsk;
1567: int retval;
...
1571: add_wait_queue(¤t->signal->wait_chldexit,&wait);
1572: repeat:
...
1584: set_current_state(TASK_INTERRUPTIBLE);
1586: tsk = current;
...
1588: retval = do_wait_thread(wo, tsk);
1589: if (retval)
1590: goto end;
...
1601: notask:
1602: retval = wo->notask_error;
1603: if (!retval && !(wo->wo_flags & WNOHANG)) {
1604: retval = -ERESTARTSYS;
1605: if (!signal_pending(current)) {
1606: schedule();
1607: goto repeat;
1608: }
1609: }
1610: end:
1611: __set_current_state(TASK_RUNNING);
1612: remove_wait_queue(¤t->signal->wait_chldexit,&wait);
...
1638: return retval;
1639: }
kernel/exit.c
1532: static int do_wait_thread(struct wait_opts *wo, struct task_struct *tsk)
1533: {
1534: struct task_struct *p;
1536: list_for_each_entry(p, &tsk->children, sibling) {
...
1541: int ret = wait_consider_task(wo, tsk, 0, p);
1542: if (ret)
1543: return ret;
..
1545: }
1547: return 0;
1548: }
1473: static int wait_consider_task(struct wait_opts *wo, struct task_struct *parent,
1474: int ptrace, struct task_struct *p)
1475: {
...
1508: if (p->exit_state == EXIT_ZOMBIE && !delay_group_leader(p))
1509: return wait_task_zombie(wo, p);
...
1521: }
kernel/exit.c
1159: static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)
1160: {
...
1201: if (likely(!traced) && likely(!task_detached(p))) {
...
1221: psig = p->real_parent->signal;
1222: sig = p->signal;
1223: psig->cutime =
1224: cputime_add(psig->cutime,
1225: cputime_add(p->utime,
1226: cputime_add(sig->utime,
1227: sig->cutime)));
...
1255: }
...
1263: retval = wo->wo_rusage
1264: ? getrusage(p, RUSAGE_BOTH, wo->wo_rusage) : 0;
1265: status = (p->signal->flags & SIGNAL_GROUP_EXIT)
1266: ? p->signal->group_exit_code : p->exit_code;
1267: if (!retval && wo->wo_stat)
1268: retval = put_user(status, wo->wo_stat);
...
1314: if (p != NULL)
1315: release_task(p);
1317: return retval;
1318: }