システムコール、プロセスとtask_struct構造体

					2020年01月10日
情報科学類 オペレーティングシステム II

                                       筑波大学 システム情報系 
                                       新城 靖
                                       <yas@cs.tsukuba.ac.jp>

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

■今日の大事な話

◆参考書

Robert Love: "Linux Kernel Development", Addison-Wesley Professional (2010). ISBN-13: 9780672329463

Claudia Salzberg Rodriguez, Gordon Fischer, and Steven Smolski: "The Linux Kernel Primer: A Top-Down Approach for x86 and PowerPC Architectures", Prentice Hall (2005). ISBN-10: 0131181637

Daniel P. Bovet, Marco Cesati 著, 高橋 浩和 (監訳), 杉田 由美子, 清水 正明 , 高杉 昌督 , 平松 雅巳 , 安井 隆宏(訳) 詳解 Linuxカーネル 第3版 オライリー・ジャパン (2007). ISBN-13: 978-4873113135

Jonathan Corbet, Alessandro Rubini, Greg Kroah-Hartman (著), 山崎 康宏 , 山崎 邦子 , 長原 宏治 , 長原 陽子(訳): "Linuxデバイスドライバ", オライリージャパン (2005). ISBN-13: 978-4873112534

■オペレーティングシステムII

◆この授業の狙い

◆この授業を受ける意義

将来カーネルをいじる人もいじらない人にも意義がある。

◆メタレベルのプログラム

インタプリタ言語を考える。例:Ruby。
$ ls -l hello.rb  [←]
-rw-r--r-- 1 yas prof 26 Dec  7 06:35 hello.rb
$ cat hello.rb  [←]
printf("hello, world!\n")
$ ruby hello.rb  [←]
hello, world!
$ []
Ruby 言語のインタプリタ自身は、C 言語で記述されている。

rubyのCのソース(メタ)、コンパイル&リンク、実行形式、プロセス、hello.rbソース(プログラム)
図? メタレベルのプログラミング

プログラム「hello.rb」の動きを変更する方法
プログラミング
hello.rb を書き換える
メタレベルのプログラミング
ruby インタプリタのプログラムを書き換える
「hello.rb」は、ruby インタプリタから見ると、入力「データ」の1つ。

◆メタレベルのプログラムとしてのOS

$ ls -l hello.c [←]
-rw-r--r-- 1 yas prof 44 Dec  7 06:25 hello.c
$ cat hello.c [←]
main()
{
        printf("hello, %s!\n","world");
}
$ cc hello.c -o hello [←]
$ ./hello  [←]
hello, world!
$ []

プロセス、カーネル(メタ)、ハードウェア(メタメタ)
図? メタレベルのプログラムとしてのOSカーネル

プログラム「hello.c」の動きを変更する方法
プログラミング
hello.c を書き換える
メタレベルのプログラミング
OSカーネルのプログラムを書き換える
「hello(実行形式)」は、OSカーネルから見ると、入力「データ」に相当する。

OSカーネル自身は、普通は、ハードウェアによるインタプリタ(CPU、入出力)で 実行される。インタフェースは、機械語命令や入出力機器への操作。

OSカーネルへのインタフェースは、システム・コール。 == OSカーネルは、システム・コールのインタプリタ。

◆シラバス

◆Linux

教材としては、Linux を使う。 2020年1月現在、学類では次のコンピュータで Linux が使える。 サーバには、ssh でログインして使う。

■システム・コールとライブラリ

システム・コールとライブラリは、プログラマにとってのオペレーティング・ システム。共通点は、両方とも、C言語からは、関数呼出しの形で使えること。 2つを合わせて、API (Application Programming Interface) とも言う。

◆OSの構造

図? 3層、複数コンポーネント
図? OSの構造

プログラム・コードの場所

◆システム・コールのライブラリの違い

システム・コール(system call) ライブラリ(library)

システムコールの例:

ライブラリの例:

システムコールとライブラリの見分け方(Unix編)

同じUnix系OSでも、細かい所でシステム・コールとライブラリが入れ替わって いることもある。

システム・コールは、トラップ命令(trap instruction)を含む。 その部分は、アセンブリ言語。

ライブラリ関数は、大部分はC言語で書かれている。 printf() のソース・プログラムがある。

C言語で記述したプログラムは、文法さえあっていれば コンパイルはできる。 ライブラリ関数やシステムコールがないと動作しない。

◆リンク

ライブラリやシステム・コールは、コンパイルされて オブジェクト・コードの形で保存されている。 実行形式を作る時に取り出され、 アプリケーション・プログラマから作成した オブジェクト・コード(main()関数など)と結合(リンク)される。

静的リンク(static link)
プログラムを実行する前(実行形式を作成する時点)で、ライブラリ関数の呼出し をすべて機械語の手続き呼び出し命令に変換する。データへの参照を固定番地 に変換する。
動的リンク(dynamic link)
プログラムを実行中に必要に応じてリンクを行う。

動的リンクを行う時には、共有ライブラリ(shared library) を使い、メモリの節約をする。

Microsoft Windows では、システムコールとライブラリの区別が希薄。 Win32 API。3000以上。 Unix のシステムコールは、300程度。

◆hello.cとgcc -S

main()
{
	printf("hello, %s!\n","world");
}
$ ls hello*   [←]
hello.c
$ make hello    [←]
cc     hello.c   -o hello
hello.c: In function 'main':
hello.c:3: warning: incompatible implicit declaration of built-in function 'printf'
$ ./hello  [←]
hello, world!
$ gcc -m32 -S hello.c [←]
hello.c: In function 'main':
hello.c:3: warning: incompatible implicit declaration of built-in function 'printf'
$ ls hello* [←]
hello  hello.c  hello.s
$ []
	.file	"hello.c"
	.section	.rodata
.LC0:
	.string	"world"
.LC1:
	.string	"hello, %s!\n"
	.text
.globl main
	.type	main, @function
main:
	pushl	%ebp
	movl	%esp, %ebp
	andl	$-16, %esp
	subl	$16, %esp
	movl	$.LC0, 4(%esp)
	movl	$.LC1, (%esp)
	call	printf
	leave
	ret
	.size	main, .-main
	.ident	"GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-4)"
	.section	.note.GNU-stack,"",@progbits

◆printf() のソース・プログラム

~yas/os2/glibc-2.12/stdio-common/printf.c

<省略>
  28:	int
  29:	__printf (const char *format, ...)
  30:	{
  31:	  va_list arg;
  32:	  int done;
  33:	
  34:	  va_start (arg, format);
  35:	  done = vfprintf (stdout, format, arg);
  36:	  va_end (arg);
  37:	
  38:	  return done;
  39:	}
<省略>
  42:	ldbl_strong_alias (__printf, printf);
PRINTF(3)                  Linux Programmer's Manual                 PRINTF(3)

NAME
       printf,   fprintf,  sprintf,  snprintf,  vprintf,  vfprintf,  vsprintf,
       vsnprintf - formatted output conversion
SYNOPSIS
...
       int printf(const char *format, ...);
       int fprintf(FILE *stream, const char *format, ...);
...
       int vfprintf(FILE *stream, const char *format, va_list ap);
...
DESCRIPTION
...
       The functions vprintf(), vfprintf(), vsprintf(), vsnprintf() are equiv-
       alent  to  the  functions  printf(),  fprintf(), sprintf(), snprintf(),
       respectively, except that they are called with a va_list instead  of  a
       variable  number  of  arguments. These functions do not call the va_end
       macro. Consequently, the value of ap is undefined after the  call.  The
       application should call va_end(ap) itself afterwards.
...

◆write()システム・コールのマニュアル

WRITE(2)                   Linux Programmer's Manual                  WRITE(2)

NAME
       write - write to a file descriptor

SYNOPSIS
       #include <unistd.h>

       ssize_t write(int fd, const void *buf, size_t count);

DESCRIPTION
       write()  writes  up  to  count bytes to the file referenced by the file
       descriptor fd from the buffer starting at buf.  POSIX requires  that  a
       read()  which  can  be  proved  to  occur  after a write() has returned
       returns the new data.  Note that not all file systems  are  POSIX  con-
       forming.

RETURN VALUE
       On  success,  the  number of bytes written are returned (zero indicates
       nothing was written).  On error, -1  is  returned,  and  errno  is  set
       appropriately.   If  count  is zero and the file descriptor refers to a
       regular file, 0 may be returned, or an error could be detected.  For  a
       special file, the results are not portable.

ERRORS
       EAGAIN Non-blocking  I/O  has  been  selected  using O_NONBLOCK and the
              write would block.

       EBADF  fd is not a valid file descriptor or is not open for writing.
...

◆write()システム・コール(ユーザ空間)

x86 アーキテクチャ(32ビット)の write.o 逆アセンブル結果より、主要部分抜 粋。 
write:
	...
	push   %ebx		# ebxレジスタの内容をスタックへ退避
	mov    0x10(%esp),%edx	# 第3引数をレジスタ edx へ
	mov    0xc(%esp),%ecx	# 第2引数をレジスタ ecx へ
	mov    0x8(%esp),%ebx	# 第1引数をレジスタ ebx へ
	mov    $0x4,%eax	# システム・コールの番号 eax へ
	call   *_dl_sysinfo	# 変数 _dl_sysinfo の内容の関数を呼び出す
	pop    %ebx		# ebxレジスタの内容を回復
	cmp    $0xfffff001,%eax	# エラーが起きたら
	jae    __syscall_error	# __syscall_errorへジャンプ (errno等の保存)
	ret    			# 呼び出した関数へリターン
linux-5.4.7/arch/x86/entry/vdso/vdso32/system_call.S
...
  42:	        pushl   %ecx
  45:	        pushl   %edx
  48:	        pushl   %ebp
...
  52:	        #define SYSENTER_SEQUENCE       "movl %esp, %ebp; sysenter"
...
  60:	        ALTERNATIVE "", SYSENTER_SEQUENCE, X86_FEATURE_SEP
...
  64:	        int     $0x80
...
  72:	        popl    %ebp
  75:	        popl    %edx
  78:	        popl    %ecx
  81:	        ret
参考

■Linuxカーネルのソース

◆バージョン

3.0 (2011年20周年) 以降。3つの数からなる。 2.6 系列は、4つの数からなる。例 「メジャー・バージョン」. 「マイナー・バージョン」. 「リビジョン」. 「パッチ番号」

2.6 以前には、2.4, 2.2, 2.0 等が広く使われた。

◆kernel.org

Linux のカーネルのソース・コードは、 http://kernel.org/ に保存されている。

展開の仕方

$ tar xf linux-X.Y.Z.tar.xz [←]
$ tar xf linux-X.Y.Z.tar.bz2 [←]
tar コマンドが圧縮形式を認識できない時の展開の仕方。
$ xz    -d < linux-X.Y.Z.tar.xz  | tar xf - [←]
$ bzip2 -d < linux-X.Y.Z.tar.bz2 | tar xf - [←]

~yas/os2/linux-5.4.7 に展開したものがある。

◆カーネルのディレクトリ構成

カーネル・ソースのトップレベルのディレクトリ
ディレクトリ 説明
Documentation ドキュメント
arch アーキテクチャ依存
block ブロック入出力層
crypto 暗号化
drivers デバイス・ドライバ
firmware ファームウェア
fs VFS(Virtual File System)とファイル・システム
include カーネル用のヘッダ・ファイル
init 起動と初期化(initialization)
ipc プロセス間通信(interprocess communication)
kernel カーネルの中心部分
lib ヘルパ
mm メモリ管理(memory management)
net ネットワーク
samples サンプルコード
scripts カーネルのコンパイルに必要なスクリプト
security Linux Security Module
sound サウンド・サブシステム
tools カーネル開発用のツール
usr 起動直後にユーザ空間で実行されるプログラム(initramfs)
virt 仮想化インフラ

■システム・コール

◆システムコールの番号

システム・コールの種類は、番号(整数)で区別される。 番号は、アーキテクチャ(CPU)に依存している。

(自動生成されたもの)

linux-5.4.7/arch/x86/include/generated/uapi/asm/unistd_32.h
   4:	#define __NR_restart_syscall 0
   5:	#define __NR_exit 1
   6:	#define __NR_fork 2
   7:	#define __NR_read 3
   8:	#define __NR_write 4
   9:	#define __NR_open 5
  10:	#define __NR_close 6
  11:	#define __NR_waitpid 7
  12:	#define __NR_creat 8
  13:	#define __NR_link 9
  14:	#define __NR_unlink 10
  15:	#define __NR_execve 11
  16:	#define __NR_chdir 12
...
 425:	#define __NR_fsmount 432
 426:	#define __NR_fspick 433
 427:	#define __NR_pidfd_open 434
 428:	#define __NR_clone3 435

◆システム・コールの表 sys_call_table

linux-5.4.7/arch/x86/include/asm/syscall.h
  23:	typedef asmlinkage long (*sys_call_ptr_t)(unsigned long, unsigned long,
  24:	                                          unsigned long, unsigned long,
  25:	                                          unsigned long, unsigned long);
...
  27:	extern const sys_call_ptr_t sys_call_table[];
...
  30:	#define ia32_sys_call_table sys_call_table
  31:	#define __NR_syscall_compat_max __NR_syscall_max
  32:	#define IA32_NR_syscalls NR_syscalls

linux-5.4.7/arch/x86/entry/syscall_32.c
  25:	#define __SYSCALL_I386(nr, sym, qual) [nr] = sym,
  26:	
  27:	__visible const sys_call_ptr_t ia32_sys_call_table[__NR_syscall_compat_max+1] = {
  28:	        /*
  29:	         * Smells like a compiler bug -- it doesn't work
  30:	         * when the & below is removed.
  31:	         */
  32:	        [0 ... __NR_syscall_compat_max] = &sys_ni_syscall,
  33:	#include <asm/syscalls_32.h>
  34:	};

linux-5.4.7/arch/x86/include/generated/asm/syscalls_32.h
   1:	#ifdef CONFIG_X86_32
   2:	__SYSCALL_I386(0, sys_restart_syscall, )
   3:	#else
   4:	__SYSCALL_I386(0, __ia32_sys_restart_syscall, )
   5:	#endif
..
   7:	__SYSCALL_I386(1, sys_exit, )
  12:	__SYSCALL_I386(2, sys_fork, )
  17:	__SYSCALL_I386(3, sys_read, )
  22:	__SYSCALL_I386(4, sys_write, )
  27:	__SYSCALL_I386(5, sys_open, )
  32:	__SYSCALL_I386(6, sys_close, )
  37:	__SYSCALL_I386(7, sys_waitpid, )
  42:	__SYSCALL_I386(8, sys_creat, )
...
2022:	__SYSCALL_I386(433, sys_fspick, )
2027:	__SYSCALL_I386(434, sys_pidfd_open, )
2032:	__SYSCALL_I386(435, sys_clone3, )

◆sys_write()

linux-5.4.7/fs/read_write.c
 620:	SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
 621:	                size_t, count)
 622:	{
 623:	        return ksys_write(fd, buf, count);
 624:	}

 600:	ssize_t ksys_write(unsigned int fd, const char __user *buf, size_t count)
 601:	{
 602:	        struct fd f = fdget_pos(fd);
 603:	        ssize_t ret = -EBADF;
 604:	
 605:	        if (f.file) {
 606:	                loff_t pos, *ppos = file_ppos(f.file);
 607:	                if (ppos) {
 608:	                        pos = *ppos;
 609:	                        ppos = &pos;
 610:	                }
 611:	                ret = vfs_write(f.file, buf, count, ppos);
 612:	                if (ret >= 0 && ppos)
 613:	                        f.file->f_pos = pos;
 614:	                fdput_pos(f);
 615:	        }
 616:	
 617:	        return ret;
 618:	}
この定義は、以下のように展開される。
asmlinkage long sys_write(unsigned int fd, const char __user *buf,
	   	size_t count)
{
	省略
}

◆エラー番号の例

linux-5.4.7/include/uapi/asm-generic/errno-base.h
   5:	#define EPERM            1      /* Operation not permitted */
   6:	#define ENOENT           2      /* No such file or directory */
   7:	#define ESRCH            3      /* No such process */
   8:	#define EINTR            4      /* Interrupted system call */
   9:	#define EIO              5      /* I/O error */
  10:	#define ENXIO            6      /* No such device or address */
  11:	#define E2BIG            7      /* Argument list too long */
  12:	#define ENOEXEC          8      /* Exec format error */
  13:	#define EBADF            9      /* Bad file number */
  14:	#define ECHILD          10      /* No child processes */
  15:	#define EAGAIN          11      /* Try again */
  16:	#define ENOMEM          12      /* Out of memory */
  17:	#define EACCES          13      /* Permission denied */
  18:	#define EFAULT          14      /* Bad address */
...

◆SYSCALL_DEFINEマクロ

linux-5.4.7/include/linux/syscalls.h
 207:	#define SYSCALL_DEFINE0(sname)                                  \
 208:	        SYSCALL_METADATA(_##sname, 0);                          \
 209:	        asmlinkage long sys_##sname(void);                      \
 210:	        ALLOW_ERROR_INJECTION(sys_##sname, ERRNO);              \
 211:	        asmlinkage long sys_##sname(void)
...
 214:	#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
 215:	#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
 216:	#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
 217:	#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
 218:	#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
 219:	#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
...
 223:	#define SYSCALL_DEFINEx(x, sname, ...)                          \
 224:	        SYSCALL_METADATA(sname, x, __VA_ARGS__)                 \
 225:	        __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

 235:	#define __SYSCALL_DEFINEx(x, name, ...)                                 \
...
 239:	        asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))       \
 240:	                __attribute__((alias(__stringify(__se_sys##name))));    \
...
 244:	        asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__))  \
 245:	        {                                                               \
 246:	                long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\
...
 250:	        }                                                               \
...
 252:	        static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))

SYSCALL_DEFINEn マクロにより、システム・コールを処理する関 数が定義される。

◆システム・コールの実行のまとめ

main()、printf()、・・・、write()、systnter トラップ、・・・、sys_write()
図? hello world プログラムでのシステム・コールの実行

■システム・コールやライブラリを観察するためのコマンド

◆strace コマンド

システム・コールの動きを調べるには、strace コマンド(Linux) が便利。
$ ./hello [←]
Hello, world!
$ strace ./hello [←]
execve("./hello", ["./hello"], [/* 31 vars */]) = 0
strace: [ Process PID=30009 runs in 32 bit mode. ]
brk(NULL)                               = 0x999c000
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf77cf000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=93696, ...}) = 0
mmap2(NULL, 93696, PROT_READ, MAP_PRIVATE, 3, 0) = 0xf77b8000
close(3)                                = 0
open("/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0\244\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=2107464, ...}) = 0
mmap2(NULL, 1878556, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xf75ed000
mprotect(0xf77b1000, 4096, PROT_NONE)   = 0
mmap2(0xf77b2000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c4000) = 0xf77b2000
mmap2(0xf77b5000, 10780, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xf77b5000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf75ec000
set_thread_area({entry_number:-1, base_addr:0xf75ec700, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0 (entry_number:12)
mprotect(0xf77b2000, 8192, PROT_READ)   = 0
mprotect(0x8049000, 4096, PROT_READ)    = 0
stat("/etc/sysconfig/64bit_strstr_via_64bit_strstr_sse2_unaligned", 0xffb114ac) = -1 ENOENT (No such file or directory)
mprotect(0xf77f3000, 4096, PROT_READ)   = 0
munmap(0xf77b8000, 93696)               = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf77ce000
write(1, "Hello, world!\n", 14Hello, world!
)         = 14
exit_group(14)                          = ?
+++ exited with 14 +++
$ []

◆ltrace コマンド

ltrace コマンド(Linux) を使うと、ライブラリの呼び出しを調べることができ る。
$ ./hello [←]
hello, world!
$ ltrace ./hello [←]
__libc_start_main(0x804840d, 1, 0xffaeae34, 0x8048430 
puts("Hello, world!"Hello, world!
)                            = 14
+++ exited (status 14) +++
$ []

◆gdb デバッガ

$ gdb hello [←]
省略
Reading symbols from /home/prof/yas/coins/syspro-2010/cc/hello...(no debugging symbols found)...done.
(gdb) b write[←]			# ブレークポイントの設定
Function "write" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y[←]
				# まだ読み込まれていない動的リンクライブラリにブレークポイントを設定する

Breakpoint 1 (write) pending.
(gdb) r				# 実行
Starting program: /home/prof/yas/coins/syspro-2010/cc/hello

Breakpoint 1, 0xf7ee18f0 in write () from /lib/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.17-292.el7.i686
(gdb) bt[←]			# バックトレース
#0  0xf7ee18f0 in write () from /lib/libc.so.6
#1  0xf7e68971 in _IO_new_file_write () from /lib/libc.so.6
#2  0xf7e6a45a in __GI__IO_do_write () from /lib/libc.so.6
#3  0xf7e6a900 in __GI__IO_file_overflow () from /lib/libc.so.6
#4  0xf7e6b22c in __overflow () from /lib/libc.so.6
#5  0xf7e5ebcf in puts () from /lib/libc.so.6
#6  0x08048422 in main ()
(gdb) c[←]				# 実行継続
Continuing.
Hello, world!
[Inferior 1 (process 30217) exited with code 016]
(gdb) q[←]				# 終了
$ []

◆その他のプロセスを調べるコマンド

■プロセスとは

「オペレーティングシステム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 19935 19934  20   0 106452  1808 wait   Ss   pts/0      0:00 -bash
0  1013 19984 19935  20   0 6258068 98480 futex_ Sl  pts/0      0:01 /usr/bin/ja
0  1013 20067 19935  20   0 153232  5232 poll_s S    pts/0      0:00 xterm -clas
0  1013 20072 20067  20   0 106440  1720 n_tty_ Ss+  pts/1      0:00 bash
0  1013 20157 19935  20   0 108132   980 -      R+   pts/0      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=1005
pid=1006
$ ./fork-pid [←]
pid=1011
pid=1012
$ []

fork()してgetpid()してprintf()するプログラム
図? 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
$ []

◆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)
文字列
GID(group ID, group identifier)
16ビット-32ビットの整数

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

◆プロセスの属性

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

◆idコマンド

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

◆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-5.4.7/include/linux/sched.h
 624:	struct task_struct {
...
 633:	        volatile long                   state;
...
 736:	        int                             exit_state;
 737:	        int                             exit_code;
...
 788:	        pid_t                           pid;
 789:	        pid_t                           tgid;
...
 802:	        struct task_struct __rcu        *real_parent;
...
 805:	        struct task_struct __rcu        *parent;
...
 810:	        struct list_head                children;
 811:	        struct list_head                sibling;
 812:	        struct task_struct              *group_leader;
...
 824:	        struct pid                      *thread_pid;
 825:	        struct hlist_node               pid_links[PIDTYPE_MAX];
...
 872:	        const struct cred __rcu         *ptracer_cred;
...
 892:	        char                            comm[TASK_COMM_LEN];
...
1286:	};

◆state

プロセスの状態。psコマンドで STAT の部分に現れる。 一般的に、プロセスは、 3つの状態を持つ。 Linux のプロセスの状態はもう少し多い。主に task_struct 構造体の stateと いうフィールドでプロセスの状態を表ている。(補助的に task_struct の exit_state も使う)。
linux-5.4.7/include/linux/sched.h
  76:	#define TASK_RUNNING                    0x0000
  77:	#define TASK_INTERRUPTIBLE              0x0001
  78:	#define TASK_UNINTERRUPTIBLE            0x0002
  79:	#define __TASK_STOPPED                  0x0004
  80:	#define __TASK_TRACED                   0x0008
  81:	/* Used in tsk->exit_state: */
  82:	#define EXIT_DEAD                       0x0010
  83:	#define EXIT_ZOMBIE                     0x0020
  84:	#define EXIT_TRACE                      (EXIT_ZOMBIE | EXIT_DEAD)
  85:	/* Used in tsk->state again: */
  86:	#define TASK_PARKED                     0x0040
  87:	#define TASK_DEAD                       0x0080
  88:	#define TASK_WAKEKILL                   0x0100
  89:	#define TASK_WAKING                     0x0200
  90:	#define TASK_NOLOAD                     0x0400
  91:	#define TASK_NEW                        0x0800
  92:	#define TASK_STATE_MAX                  0x1000
一般的な状態 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に戻って開いている番号を探して使う。)
linux-5.4.7/include/linux/types.h
  22:	typedef __kernel_pid_t          pid_t;

linux-5.4.7/include/uapi/asm-generic/posix_types.h
  28:	typedef int             __kernel_pid_t;

◆struct pid

APIとしてのプロセス識別子を、カーネルの中で表現するための構造体。
linux-5.4.7/include/linux/pid.h
  59:	struct pid
  60:	{
...
  68:	        struct upid numbers[1];
  69:	};

  54:	struct upid {
  55:	        int nr;
...
  57:	};

◆pidとtgid

Linux には、PID が何種類かある。カーネル・レベルのスレッドを生成すると、 task_struct も新たに作られる。その時、task_struct にある pid というフィー ルドも変わる。getpid() などのユーザ空間の API の実装は、カーネル内では、 TGID (Thread Group ID)に対応させる。
linux-5.4.7/include/linux/pid.h
   9:	enum pid_type
  10:	{
  11:	        PIDTYPE_PID,
  12:	        PIDTYPE_TGID,
  13:	        PIDTYPE_PGID,
  14:	        PIDTYPE_SID,
  15:	        PIDTYPE_MAX,
  16:	};
シングルスレッドなら、PIDTYPE_PID 型の PID と PIDTYPE_TGID 型の PID は同じ番号になる。 カーネルレベルのスレッドを生成すると、その PIDTYPE_PID 型の PID は変化するが、PIDTYPE_TGID 型の PID は変化しない。 getpid() で使われるのは、PIDTYPE_TGID 型の PID。 group_leader は、この意味のスレッド・グループの先頭のスレッドを指す。

PGID は、Process Group ID。これは、killpg() システム・コール等で、プロ セスのグループを使ったシグナルの送信に使われる。この機能は、シェルのジョ ブコントロールの実装に使われる。たとえば、^Z でパイプでつながったプロセ ス全部を一時的に止める時に使う。 PGID は、ps j コマンドで表示される。

SID は、Session ID。端末を開くと新しいセッションが始まる。 SID は、ps j コマンドで表示される。

◆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プロセス(PID==1の最初に作られるプロセス)に養子に出される。

◆struct task_struct *parent

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

◆struct list_head children

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

◆struct list_head sibling

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

◆struct cred *cred

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

task_struct構造体、cred構造体、group_info構造体
図? uid, gid, groups の保持方法

◆struct cred

task_struct に保存すべき内容のうち、uid、gid、groups 等を抜き出したもの。
linux-5.4.7/include/linux/cred.h
 111:	struct cred {
...
 120:	        kuid_t          uid;            /* real UID of the task */
 121:	        kgid_t          gid;            /* real GID of the task */
...
 146:	        struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
 147:	        struct group_info *group_info;  /* supplementary groups for euid/fsgid */
...
 153:	} __randomize_layout;

  25:	struct group_info {
  26:	        atomic_t        usage;
  27:	        int             ngroups;
  28:	        kgid_t          gid[0];
  29:	} __randomize_layout;

linux-5.4.7/include/linux/uidgid.h
  21:	typedef struct {
  22:	        uid_t val;
  23:	} kuid_t;
...
  26:	typedef struct {
  27:	        gid_t val;
  28:	} kgid_t;

linux-5.4.7/include/linux/types.h
  32:	typedef __kernel_uid32_t        uid_t;
  33:	typedef __kernel_gid32_t        gid_t;

linux-5.4.7/include/uapi/asm-generic/posix_types.h
  49:	typedef unsigned int    __kernel_uid32_t;
  50:	typedef unsigned int    __kernel_gid32_t;
kuid_t は、uid をカーネル内で保持するための型。32 ビットのuid_t (unsigned int) を保持するための構造体として定義されている。 (システム・コールの結果を返す所で、名前空間によるマッピングが行われることがある。)

◆uidとgid

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

◆group_info

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

■current

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

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

struct task_struct *current; 

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

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

getuid() システム・コールは、現在実行中のプロセスの UID を返す。 そのために、current を使う。
linux-5.4.7/kernel/sys.c
 919:	SYSCALL_DEFINE0(getuid)
 920:	{
 921:	        /* Only we change this so SMP safe */
 922:	        return from_kuid_munged(current_user_ns(), current_uid());
 923:	}

linux-5.4.7/include/linux/cred.h
 379:	#define current_uid()           (current_cred_xxx(uid))
 380:	#define current_gid()           (current_cred_xxx(gid))

 374:	#define current_cred_xxx(xxx)                   \
 375:	({                                              \
 376:	        current_cred()->xxx;                    \
 377:	})

 297:	#define current_cred() \
 298:	        rcu_dereference_protected(current->cred, 1)

 390:	extern struct user_namespace init_user_ns;
 391:	#ifdef CONFIG_USER_NS
 392:	#define current_user_ns()       (current_cred_xxx(user_ns))
 393:	#else
 394:	static inline struct user_namespace *current_user_ns(void)
 395:	{
 396:	        return &init_user_ns;
 397:	}
 398:	#endif

linux-5.4.7/include/linux/uidgid.h
 163:	static inline uid_t from_kuid_munged(struct user_namespace *to, kuid_t kuid)
 164:	{
 165:	        uid_t uid = from_kuid(to, kuid);
 166:	        if (uid == (uid_t)-1)
 167:	                uid = overflowuid;
 168:	        return uid;
 169:	}
...
 121:	#ifdef CONFIG_USER_NS
...
 141:	#else
...
 153:	static inline uid_t from_kuid(struct user_namespace *to, kuid_t kuid)
 154:	{
 155:	        return __kuid_val(kuid);
 156:	}
...
 189:	#endif /* CONFIG_USER_NS */

linux-5.4.7/include/linux/uidgid.h
  34:	static inline uid_t __kuid_val(kuid_t uid)
  35:	{
  36:	        return uid.val;
  37:	}
まとめると、getuid() システム・コールは、(名前空間が無効なら)次のようなコードと同じ。
SYSCALL_DEFINE0(getuid)
{
	kuid_t kuid;
	uid_t uid;
	kuid = current->cred->uid;
	uid = kuid.val;
	return uid;
}

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

fork() システム・コールの実装では、task_struct をコピーし、uid, gid を引き継ぐ。
linux-5.4.7/kernel/fork.c
2447:	SYSCALL_DEFINE0(fork)
2448:	{
...
2454:	        return _do_fork(&args);
...
2459:	}

2329:	long _do_fork(省略)
2330:	{
...
2334:	        struct task_struct *p;
...
2336:	        long nr;
...
2356:	        p = copy_process(NULL, trace, NUMA_NO_NODE, args);
...
2368:	        pid = get_task_pid(p, PIDTYPE_PID);
2369:	        nr = pid_vnr(pid);
...
2392:	        return nr;
2393:	}
...
1760:	static __latent_entropy struct task_struct *copy_process(省略)
1765:	{
1766:	        int pidfd = -1, retval;
1767:	        struct task_struct *p;
...
1847:	        p = dup_task_struct(current, node);
...
1880:	        retval = copy_creds(p, clone_flags);
...
1896:	        INIT_LIST_HEAD(&p->children);
1897:	        INIT_LIST_HEAD(&p->sibling);
...
2025:	                pid = alloc_pid(p->nsproxy->pid_ns_for_children);
...
2081:	        p->pid = pid_nr(pid);
...
2091:	                p->group_leader = p;
2092:	                p->tgid = p->pid;
...
2136:	                p->real_parent = current;
...
2191:	                        list_add_tail(&p->sibling, &p->real_parent->children);
...
2224:	        return p;
...
2288:	}

 856:	static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
 857:	{
 858:	        struct task_struct *tsk;
...
 865:	        tsk = alloc_task_struct_node(node);
...
 878:	        err = arch_dup_task_struct(tsk, orig);
...
 947:	        return tsk;
...
 954:	}

 841:	int __weak arch_dup_task_struct(struct task_struct *dst,
 842:	                                               struct task_struct *src)
 843:	{
 844:	        *dst = *src;
 845:	        return 0;
 846:	}
注意: Linux では、単純な fork() 以外に、PID 等をコピーして引き継げるよ うなシステム・コール clone() がある。

■GNU global

大きなソースコードを読むには、grepやgrep -r よりも GNU GLOBAL が便利。
gtags
ソースコードを解析して索引ファイル (GTAGS, GRTAGS, GPATH という名前) を作る。一度だけやれば十分。 Linux カーネルでは、make gtags で GNU global 用の索引が作られる。
global id
引数で与えられた id を「定義している」ファイルの名前を表示する。
global -x id
引数で与えられた id を「定義している」ファイルの名前と行を表示する。
global -s id
global -sx id
引数で与えられた、「定義」以外のid のファイルの名前(-xでその行)を 表示する。 Linux で、SYSCALL_DEFINE0() 等の中のシステム・コール名を探す のに使える。
global -r id
global -rx id
引数で与えられた id を利用しているファイルの名前(-xでその行)を表示 する。
実行例
$ pwd [←]
/home/prof/yas/os2/linux-5.4.7
$ ls -l G* [←]
-rw-r--r-- 1 yas prof   7782400  1月  3 16:52 GPATH
-rw-r--r-- 1 yas prof 415358976  1月  3 16:52 GRTAGS
-rw-r--r-- 1 yas prof 335749120  1月  3 16:52 GTAGS
$ global task_struct [←]
fs/proc/internal.h
include/linux/sched.h
$ global -rx task_struct | head -5 [←]
task_struct       123 arch/x86/entry/vsyscall/vsyscall_64.c     struct task_struct *tsk;
task_struct         9 arch/x86/include/asm/current.h struct task_struct;
task_struct        11 arch/x86/include/asm/current.h DECLARE_PER_CPU(struct task_struct *, current_task);
task_struct        13 arch/x86/include/asm/current.h static __always_inline struct task_struct *get_current(void)
task_struct       288 arch/x86/include/asm/elf.h struct task_struct;
$ global -rx task_struct | wc [←]
   3745   31360  369332
$ global getpid [←]
$ global -s getpid [←]
drivers/i3c/master.c
kernel/sys.c
samples/bpf/task_fd_query_user.c
samples/bpf/test_current_task_under_cgroup_user.c
samples/connector/ucon.c
samples/pidfd/pidfd-metadata.c
samples/timers/hpet_example.c
scripts/kconfig/confdata.c
$ global -sx getpid | grep SYSCALL [←]
getpid            891 kernel/sys.c     SYSCALL_DEFINE0(getpid)
$ []

◆Emacs gtags-mode

Emacs の gtags-mode を使うと、Emacs の中で、Gnu Global の機能を使って定 義に移動できる。 ~/.emacs 等に次のような設定を加える。(/usr/local3/coins/ のパス名は、 coins のみ有効。) これで、C 言語のファイルについて gtags-mode が有効になる。
(autoload 'gtags-mode "/usr/local3/coins/linux/share/gtags/gtags.el" "" t)
(setq gtags-suggested-key-mapping t)
(add-hook 'c-mode-common-hook 
	  (lambda ()
	    (gtags-mode 1))
)
gtags-mode では、次のようなキーが使えるようになる。
キー 関数 説明
M-. gtags-find-tag 定義に移動する (global -x)
M-* gtags-pop-stack 元に戻る
C-c r gtags-find-rtag 呼びだし元を探す (global -rx)
C-c s gtags-find-symbol シンボルを探す (global -sx)
マウスのボタンが使えることもある。
ボタン 関数 説明
<mouse-2> gtags-find-tag-by-event 定義に移動する
<mouse-3> gtags-pop-stack 元に戻る
参考 Emacs 以外に vim 等でも使える。

■毎週のレポートの出し方

■課題1 システムコール、プロセスとtask_struct構造体

★問題(101) システム・コールとライブラリ関数の相違点

システム・コールとライブラリ関数の相違点を述べなさい。

★問題(102) access()システムコールの引数と結果

access() システム・コールのマニュアルには、次のような記述がある。
ACCESS(2)                  Linux Programmer's Manual                 ACCESS(2)



NAME
       access - check real user's permissions for a file

SYNOPSIS
       #include <unistd.h>

       int access(const char *pathname, int mode);

DESCRIPTION
       access()  checks  whether the calling process can access the file path-
       name.  If pathname is a symbolic link, it is dereferenced.
...
RETURN VALUE
       On  success  (all requested permissions granted), zero is returned.  On
       error (at least one bit in mode asked for a permission that is  denied,
       or  some other error occurred), -1 is returned, and errno is set appro-
       priately.
...
このシステム・コールを処理する関数がカーネルの中でどのように定義されて いるか、その概略(引数と結果の宣言)を示しなさい。関数の内容は空でよい。 マクロを利用しても利用しなくてもどちらでもよい。
引数と結果の宣言
{
   /*内容*/
}

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

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

★問題(104) fork()システム・コール

fork() システムコールの実装について、本日の授業の範囲内で、次の項目を答 えなさい。

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

getgid() (Get Group ID) システム・コールを実装の概略を、今日の授業の範囲内で答えなさい。 利用する重要な変数、マクロ、構造体を列挙しなさい。そして、どのようにポ インタをたどっていくかを示しなさい。概略を記述するためには、簡易的なC 言語、日本語、または、英語を使いなさい。

なお、実際の getgid() システム・コールの実装は、名前空間の導入により複 雑になっており、今日の授業の範囲を超えている。この課題では、実際のコー ドではなく、この授業の範囲内で答えなさい。(実際のコードをそのまま回答 しても、得点を与えない。)


Last updated: 2020/01/03 19:02:05
Yasushi Shinjo / <yas@cs.tsukuba.ac.jp>