2023年01月06日
情報科学類 オペレーティングシステム II
                                       筑波大学 システム情報系 
                                       新城 靖
                                       <yas@cs.tsukuba.ac.jp>
このページは、次の URL にあります。
	http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2022/2023-01-06
あるいは、次のページから手繰っていくこともできます。
	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
$ ls -l hello.rb  ![[←]](../icons/screen-return.gif) -rw-r--r-- 1 yas prof 26 Dec  7 06:35 hello.rb
$ cat hello.rb
-rw-r--r-- 1 yas prof 26 Dec  7 06:35 hello.rb
$ cat hello.rb  ![[←]](../icons/screen-return.gif) printf("hello, world!\n")
$ ruby hello.rb
printf("hello, world!\n")
$ ruby hello.rb  ![[←]](../icons/screen-return.gif) hello, world!
$
hello, world!
$ ![[]](../icons/screen-cursor.gif) 
Ruby 言語のインタプリタ自身は、C 言語で記述されている。

図? メタレベルのプログラミング
$ ls -l hello.c ![[←]](../icons/screen-return.gif) -rw-r--r-- 1 yas prof 44 Dec  7 06:25 hello.c
$ cat hello.c
-rw-r--r-- 1 yas prof 44 Dec  7 06:25 hello.c
$ cat hello.c ![[←]](../icons/screen-return.gif) main()
{
        printf("hello, %s!\n","world");
}
$ cc hello.c -o hello
main()
{
        printf("hello, %s!\n","world");
}
$ cc hello.c -o hello ![[←]](../icons/screen-return.gif) $ ./hello
$ ./hello  ![[←]](../icons/screen-return.gif) hello, world!
$
hello, world!
$ ![[]](../icons/screen-cursor.gif) 

図? メタレベルのプログラムとしてのOSカーネル
OSカーネル自身は、普通は、ハードウェアによるインタプリタ(CPU、入出力)で 実行される。インタフェースは、機械語命令や入出力機器への操作。
OSカーネルへのインタフェースは、システム・コール。 == OSカーネルは、システム・コールのインタプリタ。

図? OSの構造
システムコールの例:
システムコールとライブラリの見分け方(Unix編)
同じUnix系OSでも、細かい所でシステム・コールとライブラリが入れ替わって いることもある。
システム・コールは、トラップ命令(trap instruction)を含む。 その部分は、アセンブリ言語。
C言語のライブラリ関数は、大部分はC言語で書かれている。 printf() のソース・プログラムがある。
C言語で記述したプログラムは、文法さえあっていれば コンパイルはできる。 ライブラリ関数やシステムコールがないと動作しない。
Microsoft Windows では、システムコールとライブラリの区別が希薄。 Win32 API。3000以上。 Unix のシステムコールは、300程度。
動的リンクを行う時には、共有ライブラリ(shared library) を使い、メモリの節約をする。
#include <stdio.h>
int main()
{
        printf("hello, %s!\n","world");
}
$ ls ![[←]](../icons/screen-return.gif) hello.c
$ make hello
hello.c
$ make hello ![[←]](../icons/screen-return.gif) cc     hello.c   -o hello
$ ls
cc     hello.c   -o hello
$ ls ![[←]](../icons/screen-return.gif) hello  hello.c
$ ./hello
hello  hello.c
$ ./hello ![[←]](../icons/screen-return.gif) hello, world!
$ cc -S hello.c
hello, world!
$ cc -S hello.c ![[←]](../icons/screen-return.gif) $ ls
$ ls ![[←]](../icons/screen-return.gif) hello  hello.c  hello.s
$
hello  hello.c  hello.s
$ ![[]](../icons/screen-cursor.gif) 
gcc -S hello.c」のように、「-S」オプションを
つけると、通常は削除されるアセンブリ言語の出力が残される。
-m32」もつける。
.file "hello.c" .text .section .rodata .LC0: .string "world" .LC1: .string "hello, %s!\n" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 leaq .LC0(%rip), %rsi leaq .LC1(%rip), %rdi movl $0, %eax call printf@PLT movl $0, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0" .section .note.GNU-stack,"",@progbitsx86 (32ビット)
.file "hello.c" .section .rodata .LC0: .string "world" .LC1: .string "Hello, %s!\n" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 andl $-16, %esp subl $16, %esp movl $.LC0, 4(%esp) movl $.LC1, (%esp) call printf leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-44)" .section .note.GNU-stack,"",@progbits
$ rpm -qa |grep glibc-debuginfo ![[←]](../icons/screen-return.gif) glibc-debuginfo-2.17-325.el7_9.i686
glibc-debuginfo-common-2.17-325.el7_9.x86_64
glibc-debuginfo-2.17-325.el7_9.x86_64
$ rpm -ql glibc-debuginfo-2.17-325.el7_9.x86_64| grep '/printf\.c'
glibc-debuginfo-2.17-325.el7_9.i686
glibc-debuginfo-common-2.17-325.el7_9.x86_64
glibc-debuginfo-2.17-325.el7_9.x86_64
$ rpm -ql glibc-debuginfo-2.17-325.el7_9.x86_64| grep '/printf\.c' ![[←]](../icons/screen-return.gif) /usr/src/debug/glibc-2.17-c758a686/stdio-common/printf.c
$ cat -n /usr/src/debug/glibc-2.17-c758a686/stdio-common/printf.c | sed 1,26d
/usr/src/debug/glibc-2.17-c758a686/stdio-common/printf.c
$ cat -n /usr/src/debug/glibc-2.17-c758a686/stdio-common/printf.c | sed 1,26d ![[←]](../icons/screen-return.gif) 27	 int
    28	 __printf (const char *format, ...)
    29	 {
    30	   va_list arg;
    31	     int done;
    32
    33	  va_start (arg, format);
    34	    done = vfprintf (stdout, format, arg);
    35	      va_end (arg);
    36
    37	  return done;
    38	  }
    39
    40	#undef _IO_printf
    41	ldbl_strong_alias (__printf, printf);
    42	/* This is for libg++.  */
    43	ldbl_strong_alias (__printf, _IO_printf);
$
    27	 int
    28	 __printf (const char *format, ...)
    29	 {
    30	   va_list arg;
    31	     int done;
    32
    33	  va_start (arg, format);
    34	    done = vfprintf (stdout, format, arg);
    35	      va_end (arg);
    36
    37	  return done;
    38	  }
    39
    40	#undef _IO_printf
    41	ldbl_strong_alias (__printf, printf);
    42	/* This is for libg++.  */
    43	ldbl_strong_alias (__printf, _IO_printf);
$ ![[]](../icons/screen-cursor.gif) 
~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(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: # 引数が edi, esi, edx レジスタにセットされている ... mov $0x1,%eax # システム・コールの番号を eax レジスタへ syscall # カーネル空間へ制御を移動 cmp $0xfffffffffffff001,%rax # エラーが起きたら jae __syscall_error # __syscall_errorへジャンプ (errno等の保存) retq # 呼び出した関数へリターン
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 # 呼び出した関数へリターン
$ gdb arch/x86/entry/vdso/vdso32/system_call.o ![[←]](../icons/screen-return.gif) ...
(gdb) x/12i __kernel_vsyscall
...
(gdb) x/12i __kernel_vsyscall![[←]](../icons/screen-return.gif) 0x0 <__kernel_vsyscall>:     push   %ecx
   0x1 <__kernel_vsyscall+1>:   push   %edx
   0x2 <__kernel_vsyscall+2>:   push   %ebp
   0x3 <__kernel_vsyscall+3>:   nop
   0x4 <__kernel_vsyscall+4>:   nop
   0x5 <__kernel_vsyscall+5>:   nop
   0x6 <__kernel_vsyscall+6>:   nop
   0x7 <__kernel_vsyscall+7>:   int    $0x80
   0x9 <__kernel_vsyscall+9>:   pop    %ebp
   0xa <__kernel_vsyscall+10>:  pop    %edx
   0xb <__kernel_vsyscall+11>:  pop    %ecx
   0xc <__kernel_vsyscall+12>:  ret
(gdb) quit
   0x0 <__kernel_vsyscall>:     push   %ecx
   0x1 <__kernel_vsyscall+1>:   push   %edx
   0x2 <__kernel_vsyscall+2>:   push   %ebp
   0x3 <__kernel_vsyscall+3>:   nop
   0x4 <__kernel_vsyscall+4>:   nop
   0x5 <__kernel_vsyscall+5>:   nop
   0x6 <__kernel_vsyscall+6>:   nop
   0x7 <__kernel_vsyscall+7>:   int    $0x80
   0x9 <__kernel_vsyscall+9>:   pop    %ebp
   0xa <__kernel_vsyscall+10>:  pop    %edx
   0xb <__kernel_vsyscall+11>:  pop    %ecx
   0xc <__kernel_vsyscall+12>:  ret
(gdb) quit![[←]](../icons/screen-return.gif) $
$ ![[]](../icons/screen-cursor.gif) 
linux-6.1.2/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
https://postd.cc/the-definitive-guide-to-linux-system-calls/, Linuxシステムコール徹底ガイド, PackageCloud, 2016
https://blog.packagecloud.io/eng/2016/04/05/the-definitive-guide-to-linux-system-calls/, The Definitive Guide to Linux System Calls, PackageCloud, 2016.
2.6 以前には、2.4, 2.2, 2.0 等が広く使われた。
https://www.kernel.org/ に保存されている。
展開の仕方
$ tar xf linux-X.Y.Z.tar.xz ![[←]](../icons/screen-return.gif) $ tar xf linux-X.Y.Z.tar.bz2
$ tar xf linux-X.Y.Z.tar.bz2 ![[←]](../icons/screen-return.gif) 
tar コマンドが圧縮形式を認識できない時の展開の仕方。
$ xz    -d < linux-X.Y.Z.tar.xz  | tar xf - ![[←]](../icons/screen-return.gif) $ bzip2 -d < linux-X.Y.Z.tar.bz2 | tar xf -
$ bzip2 -d < linux-X.Y.Z.tar.bz2 | tar xf - ![[←]](../icons/screen-return.gif) 
~yas/os2/linux-6.1.2 に展開したものがある。
| ディレクトリ | 説明 | 
|---|---|
| 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 | 仮想化インフラ | 
(自動生成されたもの)
linux-6.1.2/arch/x86/include/generated/uapi/asm/unistd_64.h 1: #ifndef _UAPI_ASM_UNISTD_64_H 2: #define _UAPI_ASM_UNISTD_64_H 3: 4: #define __NR_read 0 5: #define __NR_write 1 6: #define __NR_open 2 7: #define __NR_close 3 8: #define __NR_stat 4 9: #define __NR_fstat 5 10: #define __NR_lstat 6 11: #define __NR_poll 7 12: #define __NR_lseek 8 13: #define __NR_mmap 9 14: #define __NR_mprotect 10 15: #define __NR_munmap 11 16: #define __NR_brk 12 ... 363: #define __NR_process_mrelease 448 364: #define __NR_futex_waitv 449 365: #define __NR_set_mempolicy_home_node 450 ... 368: #define __NR_syscalls 451
linux-6.1.2/arch/x86/include/generated/uapi/asm/unistd_32.h 1: #ifndef _UAPI_ASM_UNISTD_32_H 2: #define _UAPI_ASM_UNISTD_32_H 3: 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 ... 437: #define __NR_landlock_create_ruleset 444 438: #define __NR_landlock_add_rule 445 439: #define __NR_landlock_restrict_self 446 440: #define __NR_memfd_secret 447 441: #define __NR_process_mrelease 448 442: #define __NR_futex_waitv 449 443: #define __NR_set_mempolicy_home_node 450 ... 446: #define __NR_syscalls 451
linux-6.1.2/arch/x86/include/asm/syscall.h
  19:	typedef long (*sys_call_ptr_t)(const struct pt_regs *);
  20:	extern const sys_call_ptr_t sys_call_table[];
linux-6.1.2/arch/x86/entry/syscall_64.c
  14:	#define __SYSCALL(nr, sym) __x64_##sym,
  15:	
  16:	asmlinkage const sys_call_ptr_t sys_call_table[] = {
  17:	#include <asm/syscalls_64.h>
  18:	};
linux-6.1.2/arch/x86/um/sys_call_table_64.c
...
  29:	const sys_call_ptr_t sys_call_table[] ____cacheline_aligned = {
  30:	#include <asm/syscalls_64.h>
  31:	};
linux-6.1.2/arch/x86/include/generated/asm/syscalls_64.h
   1:	__SYSCALL(0, sys_read)
   2:	__SYSCALL(1, sys_write)
   3:	__SYSCALL(2, sys_open)
   4:	__SYSCALL(3, sys_close)
   5:	__SYSCALL(4, sys_newstat)
   6:	__SYSCALL(5, sys_newfstat)
   7:	__SYSCALL(6, sys_newlstat)
   8:	__SYSCALL(7, sys_poll)
   9:	__SYSCALL(8, sys_lseek)
  10:	__SYSCALL(9, sys_mmap)
  11:	__SYSCALL(10, sys_mprotect)
  12:	__SYSCALL(11, sys_munmap)
  13:	__SYSCALL(12, sys_brk)
...
 447:	__SYSCALL(446, sys_landlock_restrict_self)
 448:	__SYSCALL(447, sys_memfd_secret)
 449:	__SYSCALL(448, sys_process_mrelease)
 450:	__SYSCALL(449, sys_futex_waitv)
 451:	__SYSCALL(450, sys_set_mempolicy_home_node)
sys_ で始まる。
ユーザ空間のシステム・コールと紛れないようにするため。
例: ユーザの write() システム・コールは、カーネル内の
sys_write() という関数で処理される。
## は、シンボルの結合の意味。
sys_call_ptr_t sys_call_table[__NR_syscall_max+1];
...
{
    long n = システム・コールの番号;
    long f = sys_call_table[n];
    (*f)( 引数0, 引数1, 引数2 ); // 関数と思って呼ぶ
}
linux-6.1.2/arch/x86/include/asm/syscall.h
  19:	typedef long (*sys_call_ptr_t)(const struct pt_regs *);
  20:	extern const sys_call_ptr_t sys_call_table[];
...
  23:	#define ia32_sys_call_table sys_call_table
linux-6.1.2/arch/x86/entry/syscall_32.c
  16:	#define __SYSCALL(nr, sym) extern long __ia32_##sym(const struct pt_regs *);
  17:	
  18:	#include <asm/syscalls_32.h>
  19:	#undef __SYSCALL
  20:	
  21:	#define __SYSCALL(nr, sym) __ia32_##sym,
  22:	
  23:	__visible const sys_call_ptr_t ia32_sys_call_table[] = {
  24:	#include <asm/syscalls_32.h>
  25:	};
linux-6.1.2/arch/x86/include/generated/asm/syscalls_32.h
   1:	__SYSCALL(0, sys_restart_syscall)
   2:	__SYSCALL(1, sys_exit)
   3:	__SYSCALL(2, sys_fork)
   4:	__SYSCALL(3, sys_read)
   5:	__SYSCALL(4, sys_write)
   6:	__SYSCALL_WITH_COMPAT(5, sys_open, compat_sys_open)
   7:	__SYSCALL(6, sys_close)
   8:	__SYSCALL(7, sys_waitpid)
   9:	__SYSCALL(8, sys_creat)
  10:	__SYSCALL(9, sys_link)
  11:	__SYSCALL(10, sys_unlink)
  12:	__SYSCALL_WITH_COMPAT(11, sys_execve, compat_sys_execve)
  13:	__SYSCALL(12, sys_chdir)
...
 447:	__SYSCALL(446, sys_landlock_restrict_self)
 448:	__SYSCALL(447, sys_memfd_secret)
 449:	__SYSCALL(448, sys_process_mrelease)
 450:	__SYSCALL(449, sys_futex_waitv)
 451:	__SYSCALL(450, sys_set_mempolicy_home_node)
linux-6.1.2/fs/read_write.c
 626:	ssize_t ksys_write(unsigned int fd, const char __user *buf, size_t count)
 627:	{
 628:	        struct fd f = fdget_pos(fd);
 629:	        ssize_t ret = -EBADF;
 630:	
 631:	        if (f.file) {
 632:	                loff_t pos, *ppos = file_ppos(f.file);
 633:	                if (ppos) {
 634:	                        pos = *ppos;
 635:	                        ppos = &pos;
 636:	                }
 637:	                ret = vfs_write(f.file, buf, count, ppos);
 638:	                if (ret >= 0 && ppos)
 639:	                        f.file->f_pos = pos;
 640:	                fdput_pos(f);
 641:	        }
 642:	
 643:	        return ret;
 644:	}
 645:	
 646:	SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
 647:	                size_t, count)
 648:	{
 649:	        return ksys_write(fd, buf, count);
 650:	}
この定義は、以下のように展開される(__x64_sys_write, __ia32_sys_write)。
asmlinkage long sys_write(unsigned int fd, const char __user *buf,
	   	size_t count)
{
	省略
}
E で始
まる名前(マクロ)が付けられている。
定義は、
<errno.h> (include/uapi/asm-generic/errno-base.h) 等に含まれてい
る。
sys_xxx() は、成功すると
正の値を返す。
エラーが発生すると、カーネル内では、エラー
番号を負の値にしたものを返す。
errno を見ると分かる。
(マルチスレッドのプログラムでは、スレッド固有データ)
x86-64 では、__syscall_error という手続きが
レジスタにあるエラー番号を グローバル変数 errno に
セットする。
write()システム・コール(ユーザ空間)
参照。
linux-6.1.2/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 */ ...
linux-6.1.2/include/linux/syscalls.h
 210:	#define SYSCALL_DEFINE0(sname)                                  \
...
 214:	        asmlinkage long sys_##sname(void)
 217:	#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
 218:	#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
 219:	#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
 220:	#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
 221:	#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
 222:	#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
 226:	#define SYSCALL_DEFINEx(x, sname, ...)                          \
...
 228:	        __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
 238:	#define __SYSCALL_DEFINEx(x, name, ...)                                 \
...
 247:	        asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__))  \
 248:	        {                                                               \
 249:	                long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\
...
 252:	                return ret;                                             \
 253:	        }                                                               \
...
 255:	        static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))
SYSCALL_DEFINEn マクロにより、システム・コールを処理する関
数が定義される。
sys_」 というプレフィックスが付けられる。
##」 は、Cプリプロセッサの命令で、文字列の結合を意味する。
__se_sys##name() と
__do_sys_##name() を定義し、
前者から後者を呼び出している。
インライン展開されて、実際のコードには残らない。
se は sign-extends 32-bit ints to longs の意味。
図? hello world プログラムでのシステム・コールの実行
$ ./hello ![[←]](../icons/screen-return.gif) hello, world!
$ strace ./hello
hello, world!
$ strace ./hello ![[←]](../icons/screen-return.gif) execve("./hello", ["./hello"], 0x7ffe3d1a5250 /* 29 vars */) = 0
brk(NULL)                               = 0x556e21190000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=99328, ...}) = 0
mmap(NULL, 99328, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f526501d000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240\35\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030928, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f526501b000
mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f5264a1c000
mprotect(0x7f5264c03000, 2097152, PROT_NONE) = 0
mmap(0x7f5264e03000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f5264e03000
mmap(0x7f5264e09000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f5264e09000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7f526501c4c0) = 0
mprotect(0x7f5264e03000, 16384, PROT_READ) = 0
mprotect(0x556e20a50000, 4096, PROT_READ) = 0
mprotect(0x7f5265036000, 4096, PROT_READ) = 0
munmap(0x7f526501d000, 99328)           = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
brk(NULL)                               = 0x556e21190000
brk(0x556e211b1000)                     = 0x556e211b1000
write(1, "hello, world!\n", 14hello, world!
)         = 14
exit_group(0)                           = ?
+++ exited with 0 +++
$
execve("./hello", ["./hello"], 0x7ffe3d1a5250 /* 29 vars */) = 0
brk(NULL)                               = 0x556e21190000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=99328, ...}) = 0
mmap(NULL, 99328, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f526501d000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240\35\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030928, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f526501b000
mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f5264a1c000
mprotect(0x7f5264c03000, 2097152, PROT_NONE) = 0
mmap(0x7f5264e03000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f5264e03000
mmap(0x7f5264e09000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f5264e09000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7f526501c4c0) = 0
mprotect(0x7f5264e03000, 16384, PROT_READ) = 0
mprotect(0x556e20a50000, 4096, PROT_READ) = 0
mprotect(0x7f5265036000, 4096, PROT_READ) = 0
munmap(0x7f526501d000, 99328)           = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
brk(NULL)                               = 0x556e21190000
brk(0x556e211b1000)                     = 0x556e211b1000
write(1, "hello, world!\n", 14hello, world!
)         = 14
exit_group(0)                           = ?
+++ exited with 0 +++
$ ![[]](../icons/screen-cursor.gif) 
$ ltrace ./hello ![[←]](../icons/screen-return.gif) printf("hello, %s!\n", "world"hello, world!
)                  = 14
+++ exited (status 0) +++
$
printf("hello, %s!\n", "world"hello, world!
)                  = 14
+++ exited (status 0) +++
$ ![[]](../icons/screen-cursor.gif) 
$ gdb hello ![[←]](../icons/screen-return.gif) <省略>
(gdb) b write
<省略>
(gdb) b write![[←]](../icons/screen-return.gif) # ブレークポイントの設定
Function "write" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
				# ブレークポイントの設定
Function "write" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y![[←]](../icons/screen-return.gif) Breakpoint 1 (write) pending.
(gdb) r
Breakpoint 1 (write) pending.
(gdb) r![[←]](../icons/screen-return.gif) # 実行(run)
Starting program: /home/prof/yas/.../hello
Breakpoint 1, __GI___libc_write (fd=1, buf=0x555555756260, nbytes=14)
    at ../sysdeps/unix/sysv/linux/write.c:27
27      ../sysdeps/unix/sysv/linux/write.c: No such file or directory.
(gdb) bt
				# 実行(run)
Starting program: /home/prof/yas/.../hello
Breakpoint 1, __GI___libc_write (fd=1, buf=0x555555756260, nbytes=14)
    at ../sysdeps/unix/sysv/linux/write.c:27
27      ../sysdeps/unix/sysv/linux/write.c: No such file or directory.
(gdb) bt![[←]](../icons/screen-return.gif) # バックトレース
#0  __GI___libc_write (fd=1, buf=0x555555756260, nbytes=14)
    at ../sysdeps/unix/sysv/linux/write.c:27
#1  0x00007ffff7a6d15d in _IO_new_file_write (
    f=0x7ffff7dce760 <_IO_2_1_stdout_>, data=0x555555756260, n=14)
    at fileops.c:1203
#2  0x00007ffff7a6ef01 in new_do_write (to_do=14,
    data=0x555555756260 "hello, world!\n", fp=0x7ffff7dce760 <_IO_2_1_stdout_>)
    at fileops.c:457
#3  _IO_new_do_write (fp=0x7ffff7dce760 <_IO_2_1_stdout_>,
    data=0x555555756260 "hello, world!\n", to_do=14) at fileops.c:433
#4  0x00007ffff7a6d98d in _IO_new_file_xsputn (
    f=0x7ffff7dce760 <_IO_2_1_stdout_>, data=
				# バックトレース
#0  __GI___libc_write (fd=1, buf=0x555555756260, nbytes=14)
    at ../sysdeps/unix/sysv/linux/write.c:27
#1  0x00007ffff7a6d15d in _IO_new_file_write (
    f=0x7ffff7dce760 <_IO_2_1_stdout_>, data=0x555555756260, n=14)
    at fileops.c:1203
#2  0x00007ffff7a6ef01 in new_do_write (to_do=14,
    data=0x555555756260 "hello, world!\n", fp=0x7ffff7dce760 <_IO_2_1_stdout_>)
    at fileops.c:457
#3  _IO_new_do_write (fp=0x7ffff7dce760 <_IO_2_1_stdout_>,
    data=0x555555756260 "hello, world!\n", to_do=14) at fileops.c:433
#4  0x00007ffff7a6d98d in _IO_new_file_xsputn (
    f=0x7ffff7dce760 <_IO_2_1_stdout_>, data=, n=2)
    at fileops.c:1266
#5  0x00007ffff7a3d97a in _IO_vfprintf_internal (
    s=0x7ffff7dce760 <_IO_2_1_stdout_>, format=0x5555555546fa "hello, %s!\n",
    ap=ap@entry=0x7fffffffe670) at vfprintf.c:1674
#6  0x00007ffff7a46ee6 in __printf (format=) at printf.c:33
#7  0x0000555555554666 in main ()
(gdb) c![[←]](../icons/screen-return.gif) # 実行継続(continue)
Continuing.
hello, world!
[Inferior 1 (process 13764) exited normally]
(gdb) q
				# 実行継続(continue)
Continuing.
hello, world!
[Inferior 1 (process 13764) exited normally]
(gdb) q![[←]](../icons/screen-return.gif) # gdb 終了
$
				# gdb 終了
$ ![[]](../icons/screen-cursor.gif) 
  
| 状態 | 説明 | 
|---|---|
| 新規(New) | プロセスが作られつつある。 | 
| 実行待ち(Ready) | CPUがあれば実行できるが CPU がないので実行されていない。CPUが割り当てられるのを待っている。 | 
| 実行中(Running) | CPUが実際に割り当てられ、実行されている。 | 
| 待機中(Waiting、Blocked) | プロセスが、I/Oの完了やシグナルの受信といった事象(event)が 起きてるのを待っている。 | 
| 終了(Terminated) | プロセスが実行を終えた。 | 

図? プロセスの5状態
スレッドとは、1つのプロセス(のアドレス空間)の内部にふくまれている論 理的な並列処理の単位。
| 表示 | 説明 | 
| STAT | State。状態。 | 
| PID | Process ID。プロセス1つ1つに重複ないように(unique)割り当てた番号。 | 
| PPID | Parent PID。親プロセスのPID。 | 
| UID | User ID。プロセスを生成した利用者の識別子。 | 
$ ps l ![[←]](../icons/screen-return.gif) 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
$
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
$ ![[]](../icons/screen-cursor.gif) 
/proc/PID; cat /proc/PID/status
/proc の下に、カーネル内のデータを取り出すための疑似的なファイ
ルが存在する。特に、/proc/PID の下には、プロセス識別子
がPID のプロセスの情報が現れる。詳しくは、man procを見
なさい。
$ echo $$ ![[←]](../icons/screen-return.gif) 23069
$ ls /proc/$$
23069
$ ls /proc/$$ ![[←]](../icons/screen-return.gif) 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
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  ![[←]](../icons/screen-return.gif) 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 
$
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 
$ ![[]](../icons/screen-cursor.gif) 
   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 ![[←]](../icons/screen-return.gif) cc     fork-pid.c   -o fork-pid
$ ./fork-pid
cc     fork-pid.c   -o fork-pid
$ ./fork-pid ![[←]](../icons/screen-return.gif) pid=1005
pid=1006
$ ./fork-pid
pid=1005
pid=1006
$ ./fork-pid ![[←]](../icons/screen-return.gif) pid=1011
pid=1012
$
pid=1011
pid=1012
$ ![[]](../icons/screen-cursor.gif) 

図? 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 ![[←]](../icons/screen-return.gif) cc     proc-pid-ppid.c   -o proc-pid-ppid
$ echo $$
cc     proc-pid-ppid.c   -o proc-pid-ppid
$ echo $$ ![[←]](../icons/screen-return.gif) 10771
$ ./proc-pid-ppid
10771
$ ./proc-pid-ppid  ![[←]](../icons/screen-return.gif) pid=10873, ppid=10771
$ ./proc-pid-ppid
pid=10873, ppid=10771
$ ./proc-pid-ppid  ![[←]](../icons/screen-return.gif) pid=10874, ppid=10771
$ ./proc-pid-ppid
pid=10874, ppid=10771
$ ./proc-pid-ppid  ![[←]](../icons/screen-return.gif) pid=10875, ppid=10771
$
pid=10875, ppid=10771
$ ![[]](../icons/screen-cursor.gif) 
   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 ![[←]](../icons/screen-return.gif) cc     fork-hello.c   -o fork-hello
$ ./fork-hello
cc     fork-hello.c   -o fork-hello
$ ./fork-hello  ![[←]](../icons/screen-return.gif) hello
hello
hello
hello
$ hello
hello
hello
hello
hello
hello
hello
hello
$ hello
hello
hello
hello
![[]](../icons/screen-cursor.gif) 

図? fork()システム・コールによるプロセスのコピー
Unixでは、全てのファイルやプロセスは、あるユーザの所有物である。 ファイルとプロセスには、UID が付加されている。
1人のユーザが複数のグループに属することができる。
$ id ![[←]](../icons/screen-return.gif) uid=1013(yas) gid=510(prof) groups=510(prof),20(games),1020(c-admin),1065(c-spec),1150(tebiki),1180(c-gakusei)
$
uid=1013(yas) gid=510(prof) groups=510(prof),20(games),1020(c-admin),1065(c-spec),1150(tebiki),1180(c-gakusei)
$ ![[]](../icons/screen-cursor.gif) 
   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 ![[←]](../icons/screen-return.gif) $ ./id-simple
$ ./id-simple ![[←]](../icons/screen-return.gif) uid=1013 gid=510 groups=20,510,1020,1065,1150,1180,
$
uid=1013 gid=510 groups=20,510,1020,1065,1150,1180,
$ ![[]](../icons/screen-cursor.gif) 
Linux の特殊事情
linux-6.1.2/include/linux/sched.h
 737:	struct task_struct {
...
 745:	        unsigned int                    __state;
...
 876:	        int                             exit_state;
 877:	        int                             exit_code;
...
 967:	        pid_t                           pid;
 968:	        pid_t                           tgid;
...
 981:	        struct task_struct __rcu        *real_parent;
...
 984:	        struct task_struct __rcu        *parent;
...
 989:	        struct list_head                children;
 990:	        struct list_head                sibling;
 991:	        struct task_struct              *group_leader;
...
1003:	        struct pid                      *thread_pid;
1004:	        struct hlist_node               pid_links[PIDTYPE_MAX];
...
1064:	        const struct cred __rcu         *cred;
...
1078:	        char                            comm[TASK_COMM_LEN];
...
1546:	};
STAT の部分に現れる。
一般的に、プロセスは、
3つの状態を持つ。
Linux のプロセスの状態はもう少し多い。主に task_struct 構造体の stateと
いうフィールドでプロセスの状態を表ている。(補助的に task_struct の
exit_state も使う)。
linux-6.1.2/include/linux/sched.h 84: /* Used in tsk->state: */ 85: #define TASK_RUNNING 0x00000000 86: #define TASK_INTERRUPTIBLE 0x00000001 87: #define TASK_UNINTERRUPTIBLE 0x00000002 88: #define __TASK_STOPPED 0x00000004 89: #define __TASK_TRACED 0x00000008 90: /* Used in tsk->exit_state: */ 91: #define EXIT_DEAD 0x00000010 92: #define EXIT_ZOMBIE 0x00000020 93: #define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD) 94: /* Used in tsk->state again: */ 95: #define TASK_PARKED 0x00000040 96: #define TASK_DEAD 0x00000080 97: #define TASK_WAKEKILL 0x00000100 98: #define TASK_WAKING 0x00000200 99: #define TASK_NOLOAD 0x00000400 100: #define TASK_NEW 0x00000800 101: #define TASK_RTLOCK_WAIT 0x00001000 102: #define TASK_FREEZABLE 0x00002000 103: #define __TASK_FREEZABLE_UNSAFE (0x00004000 * IS_ENABLED(CONFIG_LOCKDEP)) 104: #define TASK_FROZEN 0x00008000 105: #define TASK_STATE_MAX 0x00010000
| 一般的な状態 | 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 | 既に終了していて、終了処理の完了を待ってる。 | 
Linux では、/proc/sys/kernel/pid_max で設定できる。 64 ビットのシステムでは、最大、2^22 になっている(man 5 proc参照)。 FreeBSD や macOS のように、10 進数で表記した時に 5桁以内になるように、 99999 や 99998 になっているシステムもある。
32,768を超えたら、1に戻って使われていない番号を探すという操作は単純に実装すると重たい。 増える一方で済むなら、その方が速い。 PID の桁数が増えたら動作しなくなるプログラムもあると思われる。
linux-6.1.2/include/linux/types.h 22: typedef __kernel_pid_t pid_t; linux-6.1.2/include/uapi/asm-generic/posix_types.h 28: typedef int __kernel_pid_t;
linux-6.1.2/include/linux/pid.h
  59:	struct pid
  60:	{
...
  70:	        struct upid numbers[1];
  71:	};
  54:	struct upid {
  55:	        int nr;
...
  57:	};
linux-6.1.2/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 コマンドで表示される。

図? プロセスの木構造

図? uid, gid, groups の保持方法
linux-6.1.2/include/linux/cred.h
 110:	struct cred {
..
 119:	        kuid_t          uid;            /* real UID of the task */
 120:	        kgid_t          gid;            /* real GID of the task */
...
 147:	        struct group_info *group_info;  /* supplementary groups for euid/fsgid */
...
 153:	} __randomize_layout;
  25:	struct group_info {
...
  27:	        int             ngroups;
  28:	        kgid_t          gid[];
  29:	} __randomize_layout;
linux-6.1.2/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-6.1.2/include/linux/types.h
  32:	typedef __kernel_uid32_t        uid_t;
  33:	typedef __kernel_gid32_t        gid_t;
linux-6.1.2/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) を保持するための構造体として定義されている。
(システム・コールの結果を返す所で、名前空間によるマッピングが行われることがある。)
直感的には、次のような大域変数があると思ってよい(実際には、CPUごとに異なる値を持つ)。
struct task_struct *current;

図? current変数によるtask_structの参照
linux-6.1.2/kernel/sys.c
 968:	SYSCALL_DEFINE0(getuid)
 969:	{
 970:	        /* Only we change this so SMP safe */
 971:	        return from_kuid_munged(current_user_ns(), current_uid());
 972:	}
linux-6.1.2/include/linux/cred.h
 381:	#define current_uid()           (current_cred_xxx(uid))
 382:	#define current_gid()           (current_cred_xxx(gid))
 376:	#define current_cred_xxx(xxx)                   \
 377:	({                                              \
 378:	        current_cred()->xxx;                    \
 379:	})
 298:	#define current_cred() \
 299:	        rcu_dereference_protected(current->cred, 1)
 393:	extern struct user_namespace init_user_ns;
 394:	#ifdef CONFIG_USER_NS
 395:	#define current_user_ns()       (current_cred_xxx(user_ns))
 396:	#else
 397:	static inline struct user_namespace *current_user_ns(void)
 398:	{
 399:	        return &init_user_ns;
 400:	}
 401:	#endif
linux-6.1.2/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 */
...
  34:	static inline uid_t __kuid_val(kuid_t uid)
  35:	{
  36:	        return uid.val;
  37:	}
SYSCALL_DEFINE0(getuid)
{
	kuid_t kuid;
	uid_t uid;
	kuid = current->cred->uid;
	uid = kuid.val;
	return uid;
}
linux-6.1.2/kernel/fork.c
2756:	SYSCALL_DEFINE0(fork)
2757:	{
...
2763:	        return kernel_clone(&args);
...
2768:	}
2635:	pid_t kernel_clone(省略)
2636:	{
...
2639:	        struct pid *pid;
2640:	        struct task_struct *p;
...
2676:	        p = copy_process(省略);
...
2688:	        pid = get_task_pid(p, PIDTYPE_PID);
2689:	        nr = pid_vnr(pid);
...
2719:	        return nr;
2720:	}
1991:	static __latent_entropy struct task_struct *copy_process(省略)
1996:	{
1997:	        int pidfd = -1, retval;
1998:	        struct task_struct *p;
...
2087:	        p = dup_task_struct(current, node);
...
2140:	        INIT_LIST_HEAD(&p->children);
2141:	        INIT_LIST_HEAD(&p->sibling);
...
2272:	                pid = alloc_pid(p->nsproxy->pid_ns_for_children, args->set_tid,
2273:	                                args->set_tid_size);
...
2329:	        p->pid = pid_nr(pid);
...
2334:	                p->group_leader = p;
2335:	                p->tgid = p->pid;
...
2401:	                p->real_parent = current;
...
2459:	                        list_add_tail(&p->sibling, &p->real_parent->children);
...
2498:	        return p;
...
2562:	}
 962:	static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
 963:	{
 964:	        struct task_struct *tsk;
...
 969:	        tsk = alloc_task_struct_node(node);
...
 973:	        err = arch_dup_task_struct(tsk, orig);
...
1053:	        return tsk;
...
1061:	}
 947:	int __weak arch_dup_task_struct(struct task_struct *dst,
 948:	                                               struct task_struct *src)
 949:	{
 950:	        *dst = *src;
 951:	        return 0;
 952:	}
make gtags で GNU global 用の索引が作られる。
$ pwd ![[←]](../icons/screen-return.gif) /home/prof/yas/os2/linux-6.1.2
$ ls -l G*
/home/prof/yas/os2/linux-6.1.2
$ ls -l G* ![[←]](../icons/screen-return.gif) -rw-r--r-- 1 yas prof  10461184  1月  2 17:41 GPATH
-rw-r--r-- 1 yas prof 497926144  1月  2 17:41 GRTAGS
-rw-r--r-- 1 yas prof 783851520  1月  2 17:41 GTAGS
$ global task_struct
-rw-r--r-- 1 yas prof  10461184  1月  2 17:41 GPATH
-rw-r--r-- 1 yas prof 497926144  1月  2 17:41 GRTAGS
-rw-r--r-- 1 yas prof 783851520  1月  2 17:41 GTAGS
$ global task_struct ![[←]](../icons/screen-return.gif) fs/proc/internal.h
include/linux/sched.h
tools/testing/selftests/bpf/progs/strobemeta.h
tools/testing/selftests/bpf/progs/test_core_reloc_kernel.c
tools/testing/selftests/bpf/progs/test_core_retro.c
$ global -rx task_struct | head -5
fs/proc/internal.h
include/linux/sched.h
tools/testing/selftests/bpf/progs/strobemeta.h
tools/testing/selftests/bpf/progs/test_core_reloc_kernel.c
tools/testing/selftests/bpf/progs/test_core_retro.c
$ global -rx task_struct | head -5 ![[←]](../icons/screen-return.gif) task_struct       126 arch/x86/entry/vdso/vma.c int vdso_join_timens(struct task_struct *task, struct time_namespace *ns)
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)
$ global -rx task_struct | wc
task_struct       126 arch/x86/entry/vdso/vma.c int vdso_join_timens(struct task_struct *task, struct time_namespace *ns)
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)
$ global -rx task_struct | wc ![[←]](../icons/screen-return.gif) 4320   36333  429616
$ global getuid
   4320   36333  429616
$ global getuid ![[←]](../icons/screen-return.gif) $ global -s  getuid
$ global -s  getuid ![[←]](../icons/screen-return.gif) kernel/sys.c
tools/power/acpi/tools/pfrut/pfrut.c
...
tools/thermal/tmon/tmon.c
$ global -sx  getuid | grep SYSCALL
kernel/sys.c
tools/power/acpi/tools/pfrut/pfrut.c
...
tools/thermal/tmon/tmon.c
$ global -sx  getuid | grep SYSCALL ![[←]](../icons/screen-return.gif) getuid            968 kernel/sys.c     SYSCALL_DEFINE0(getuid)
$
getuid            968 kernel/sys.c     SYSCALL_DEFINE0(getuid)
$ ![[]](../icons/screen-cursor.gif) 
(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 | 元に戻る | 
2023年1月11日(水) 5-6時限
情報科学類 オペレーティングシステムII 課題<n> 学籍番号 ________ 名前________ 提出日________
図? 授業内容、課題、世の中で求められていること、自分の興味の関係
MKDIR(2)                      Linux Programmer's Manual                     MKDIR(2)
NAME
       mkdir, mkdirat - create a directory
SYNOPSIS
       #include <sys/stat.h>
       #include <sys/types.h>
       int mkdir(const char *pathname, mode_t mode);
...
DESCRIPTION
       mkdir() attempts to create a directory named pathname.
       The  argument  mode  specifies the mode for the new directory (see inode(7)).
       It is modified by the process's umask in the usual way: in the absence  of  a
       default  ACL,  the  mode  of the created directory is (mode & ~umask & 0777).
       Whether other mode bits are honored for the created directory depends on  the
       operating system.  For Linux, see NOTES below.
...
RETURN VALUE
       mkdir()  and mkdirat() return zero on success, or -1 if an error occurred (in
       which case, errno is set appropriately).
...
このシステム・コールを処理する関数がカーネルの中でどのように定義されて
いるか、その概略(引数と結果の宣言)を示しなさい。関数の内容は空で解答しなさい。
マクロを利用しても利用しなくてもどちらでもよい。
引数と結果の宣言
{
   /*内容*/
}
なお、実際の getgid() システム・コールの実装は、名前空間の導入により複 雑になっており、今日の授業の範囲を超えている。この課題では、実際のコー ドではなく、この授業の範囲内で答えなさい。(実際のコードをそのまま回答 しても、得点を与えない。)