システムコール、プロセスと Linux task_struct

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

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

このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2021/2022-01-07
あるいは、次のページから手繰っていくこともできます。
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カーネルは、システム・コールのインタプリタ。

◆シラバス

◆この授業の進め方

反転授業の形態で進めたい 期末試験は、教室で実施。

◆予定

  1. 2022年1月7日(金) 3-4時限
  2. 2022年1月13日() 3-4時限
  3. 2022年1月21日(金) 3-4時限
  4. 2022年1月28日(金) 3-4時限
  5. 2022年2月4日(金) 3-4時限
  6. 2022年2月9日() 5-6時限, 期末試験
1月13日は木曜日ですが、大学としては金曜日の時間割で動きます。 1月14日金曜日は、センター試験の前日なので大学全体で授業はありません。 第5週は、試験日ですが、授業をやります。 試験は、翌週、水曜日5-6時限に行います。 時間も曜日も違うので、注意して下さい。

◆Linux

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

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

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

◆OSの構造

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

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

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

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

システムコールの例:

ライブラリの例:

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

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

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

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

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

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

◆リンク

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

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

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

◆hello.cとgcc -S

int main()
{
	printf("Hello, %s!\n","world");
}
$ ls [←]
hello.c
$ make hello [←]
cc     hello.c   -o hello
hello.c: In function 'main':
hello.c:3:2: warning: incompatible implicit declaration of built-in function 'printf' [enabled by default]
  printf("Hello, %s!\n","world");
  ^
$ ./hello [←]
Hello, world!
$ gcc -m32 -S hello.c [←]
hello.c: In function 'main':
hello.c:3:2: warning: incompatible implicit declaration of built-in function 'printf' [enabled by default]
  printf("Hello, %s!\n","world");
  ^
$ ls [←]
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:
.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

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

RedHat 系なら debuginfo-install で glibc-debuginfo をインストールすると、 printf() を含む libc のソースコードがインストールされる。 (Coins viola12, viola5)
$ rpm -qa |grep glibc-debuginfo [←]
glibc-debuginfo-2.17-323.el7_9.x86_64
glibc-debuginfo-2.17-323.el7_9.i686
glibc-debuginfo-common-2.17-323.el7_9.x86_64
$ rpm -ql glibc-debuginfo-2.17-323.el7_9.x86_64|grep '/printf\.c' [←]
/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 [←]
    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);
$ []
~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    			# 呼び出した関数へリターン
$ gdb arch/x86/entry/vdso/vdso32/system_call.o [←]
...
(gdb) x/12i __kernel_vsyscall[←]
   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[←]
$ []
linux-5.15.12/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 のカーネルのソース・コードは、 https://www.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.15.12 に展開したものがある。

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

カーネル・ソースのトップレベルのディレクトリ
ディレクトリ 説明
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.15.12/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
...
 444:	#define __NR_syscalls 449

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

linux-5.15.12/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-5.15.12/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-5.15.12/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)
...
 445:	__SYSCALL(444, sys_landlock_create_ruleset)
 446:	__SYSCALL(445, sys_landlock_add_rule)
 447:	__SYSCALL(446, sys_landlock_restrict_self)
 448:	__SYSCALL(447, sys_memfd_secret)
 449:	__SYSCALL(448, sys_process_mrelease)

◆sys_write()

linux-5.15.12/fs/read_write.c
 636:	ssize_t ksys_write(unsigned int fd, const char __user *buf, size_t count)
 637:	{
 638:	        struct fd f = fdget_pos(fd);
 639:	        ssize_t ret = -EBADF;
 640:	
 641:	        if (f.file) {
 642:	                loff_t pos, *ppos = file_ppos(f.file);
 643:	                if (ppos) {
 644:	                        pos = *ppos;
 645:	                        ppos = &pos;
 646:	                }
 647:	                ret = vfs_write(f.file, buf, count, ppos);
 648:	                if (ret >= 0 && ppos)
 649:	                        f.file->f_pos = pos;
 650:	                fdput_pos(f);
 651:	        }
 652:	
 653:	        return ret;
 654:	}
 655:	
 656:	SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
 657:	                size_t, count)
 658:	{
 659:	        return ksys_write(fd, buf, count);
 660:	}
この定義は、以下のように展開される(正確には、__x64_sys_write や __ia32_sys_write)。
asmlinkage long sys_write(unsigned int fd, const char __user *buf,
	   	size_t count)
{
	省略
}

◆エラー番号の例

linux-5.15.12/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.15.12/include/linux/syscalls.h
 209:	#define SYSCALL_DEFINE0(sname)                                  \
...
 211:	        asmlinkage long sys_##sname(void);                      \
...
 216:	#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
 217:	#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
 218:	#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
 219:	#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
 220:	#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
 221:	#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)

 225:	#define SYSCALL_DEFINEx(x, sname, ...)                          \
...
 227:	        __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

 237:	#define __SYSCALL_DEFINEx(x, name, ...)                                 \
 241:	        asmlinkage long 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"], 0x7ffd993446f0 /* 26 vars */) = 0
strace: [ Process PID=28805 runs in 32 bit mode. ]
brk(NULL)                               = 0x9582000
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf7749000
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=95431, ...}) = 0
mmap2(NULL, 95431, PROT_READ, MAP_PRIVATE, 3, 0) = 0xf7731000
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\0000\244\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=2107628, ...}) = 0
mmap2(NULL, 1878556, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xf7566000
mprotect(0xf772a000, 4096, PROT_NONE)   = 0
mmap2(0xf772b000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c4000) = 0xf772b000
mmap2(0xf772e000, 10780, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xf772e000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf7565000
set_thread_area({entry_number=-1, base_addr=0xf7565700, limit=0x0fffff, seg_32bit=1, contents=0, read_exec_only=0, limit_in_pages=1, seg_not_present=0, useable=1}) = 0 (entry_number=12)
mprotect(0xf772b000, 8192, PROT_READ)   = 0
mprotect(0x8049000, 4096, PROT_READ)    = 0
stat("/etc/sysconfig/64bit_strstr_via_64bit_strstr_sse2_unaligned", 0xfffb0a3c) = -1 ENOENT (No such file or directory)
mprotect(0xf776d000, 4096, PROT_READ)   = 0
munmap(0xf7731000, 95431)               = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf7748000
write(1, "Hello, world!\n", 14Hello, world!
)         = 14
exit_group(14)                          = ?
+++ exited with 14 +++
$ []

◆ltrace コマンド

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

◆gdb デバッガ

$ gdb hello [←]
<省略>
(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[←]				# 実行(run)
Starting program: /home/prof/yas/coins/syspro-2010/cc/os2/hello

Breakpoint 1, 0xf7ee0910 in write () from /lib/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.17-317.el7.i686
(gdb) bt[←]				# バックトレース
#0  0xf7ee0910 in write () from /lib/libc.so.6
#1  0xf7e679a1 in _IO_new_file_write () from /lib/libc.so.6
#2  0xf7e6948a in __GI__IO_do_write () from /lib/libc.so.6
#3  0xf7e6999a in __GI__IO_file_overflow () from /lib/libc.so.6
#4  0xf7e682d7 in __GI__IO_file_xsputn () from /lib/libc.so.6
#5  0xf7e39f86 in vfprintf () from /lib/libc.so.6
#6  0xf7e4362f in printf () from /lib/libc.so.6
#7  0x0804842a in main ()
(gdb) c[←]				# 実行継続(continue)
Continuing.
Hello, world!
[Inferior 1 (process 28895) exited with code 016]
(gdb) q[←]				# gdb 終了
$ []

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

■プロセスとは

秋AB開講「オペレーティングシステム(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),1180(c-gakusei)
$ []

◆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,1180,
$ []

◆UID、GIDとアクセス制御

1年生の「コンピュータリテラシ」の試料も、参考になる。

■Linux task構造体

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

Linux の特殊事情

◆task_struct構造体

linux-5.15.12/include/linux/sched.h
 723:	struct task_struct {
...
 731:	        unsigned int                    __state;
...
 866:	        int                             exit_state;
 867:	        int                             exit_code;
...
 943:	        pid_t                           pid;
 944:	        pid_t                           tgid;
...
 957:	        struct task_struct __rcu        *real_parent;
...
 960:	        struct task_struct __rcu        *parent;
...
 965:	        struct list_head                children;
 966:	        struct list_head                sibling;
 967:	        struct task_struct              *group_leader;
...
 979:	        struct pid                      *thread_pid;
 980:	        struct hlist_node               pid_links[PIDTYPE_MAX];
...
1040:	        const struct cred __rcu         *cred;
...
1054:	        char                            comm[TASK_COMM_LEN];
...
1506:	};

◆state (__state)

プロセスの状態。psコマンドで STAT の部分に現れる。 一般的に、プロセスは、 3つの状態を持つ。 Linux のプロセスの状態はもう少し多い。主に task_struct 構造体の stateと いうフィールドでプロセスの状態を表ている。(補助的に task_struct の exit_state も使う)。
linux-5.15.12/include/linux/sched.h
  82:	/* Used in tsk->state: */
  83:	#define TASK_RUNNING                    0x0000
  84:	#define TASK_INTERRUPTIBLE              0x0001
  85:	#define TASK_UNINTERRUPTIBLE            0x0002
  86:	#define __TASK_STOPPED                  0x0004
  87:	#define __TASK_TRACED                   0x0008
  88:	/* Used in tsk->exit_state: */
  89:	#define EXIT_DEAD                       0x0010
  90:	#define EXIT_ZOMBIE                     0x0020
  91:	#define EXIT_TRACE                      (EXIT_ZOMBIE | EXIT_DEAD)
  92:	/* Used in tsk->state again: */
  93:	#define TASK_PARKED                     0x0040
  94:	#define TASK_DEAD                       0x0080
  95:	#define TASK_WAKEKILL                   0x0100
  96:	#define TASK_WAKING                     0x0200
  97:	#define TASK_NOLOAD                     0x0400
  98:	#define TASK_NEW                        0x0800
  99:	/* RT specific auxilliary flag to mark RT lock waiters */
 100:	#define TASK_RTLOCK_WAIT                0x1000
 101:	#define TASK_STATE_MAX                  0x2000
一般的な状態 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 では、/proc/sys/kernel/pid_max で設定できる。 64 ビットのシステムでは、最大、2^22 になっている(man 5 proc参照)。 FreeBSD や macOS のように、10 進数で表記した時に 5桁以内になるように、 99999 や 99998 になっているシステムもある。

32,768を超えたら、1に戻って使われていない番号を探すという操作は単純に実装すると重たい。 増える一方で済むなら、その方が速い。 PID の桁数が増えたら動作しなくなるプログラムもあると思われる。

linux-5.15.12/include/linux/types.h
  22:	typedef __kernel_pid_t          pid_t;

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

◆struct pid

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

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

◆pidとtgid

Linux には、PID が何種類かある。カーネル・レベルのスレッドを生成すると、 task_struct も新たに作られる。その時、task_struct にある pid というフィー ルドも変わる。getpid() などのユーザ空間の API の実装は、カーネル内では、 TGID (Thread Group ID)に対応させる。
linux-5.10.3/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.15.12/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-5.15.12/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.15.12/include/linux/types.h
  32:	typedef __kernel_uid32_t        uid_t;
  33:	typedef __kernel_gid32_t        gid_t;

linux-5.15.12/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.15.12/kernel/sys.c
 952:	SYSCALL_DEFINE0(getuid)
 953:	{
 954:	        /* Only we change this so SMP safe */
 955:	        return from_kuid_munged(current_user_ns(), current_uid());
 956:	}

linux-5.15.12/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:	})

linux-5.15.12/include/linux/cred.h
 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-5.15.12/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:	}
まとめると、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.15.12/kernel/fork.c
2641:	SYSCALL_DEFINE0(fork)
2642:	{
...
2648:	        return kernel_clone(&args);
...
2653:	}

2544:	pid_t kernel_clone(省略)
2545:	{
...
2548:	        struct pid *pid;
2549:	        struct task_struct *p;
...
2551:	        pid_t nr;
...
2585:	        p = copy_process(省略);
...
2597:	        pid = get_task_pid(p, PIDTYPE_PID);
2598:	        nr = pid_vnr(pid);
...
2621:	        return nr;
2622:	}

1930:	static __latent_entropy struct task_struct *copy_process(省略)
1935:	{
1936:	        int pidfd = -1, retval;
1937:	        struct task_struct *p;
...
2026:	        p = dup_task_struct(current, node);
...
2066:	        retval = copy_creds(p, clone_flags);
...
2082:	        INIT_LIST_HEAD(&p->children);
2083:	        INIT_LIST_HEAD(&p->sibling);
...
2210:	                pid = alloc_pid(p->nsproxy->pid_ns_for_children, args->set_tid,
2211:	                                args->set_tid_size);
...
2267:	        p->pid = pid_nr(pid);


...
2272:	                p->group_leader = p;
2273:	                p->tgid = p->pid;

...
2325:	                p->real_parent = current;
...
2383:	                        list_add_tail(&p->sibling, &p->real_parent->children);
...
2418:	        return p;
...
2481:	}

 874:	static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
 875:	{
 876:	        struct task_struct *tsk;
...
 883:	        tsk = alloc_task_struct_node(node);
...
 896:	        err = arch_dup_task_struct(tsk, orig);
...
 973:	        return tsk;
...
 980:	}

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

■GNU global

大きなソースコードを読むには、grepやgrep -r よりも GNU GLOBAL が便利。 (Linux カーネル以外でも使える)
gtags
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.15.12
$ ls -l G* [←]
-rw-r--r-- 1 yas prof   8912896 12月 31 16:54 GPATH
-rw-r--r-- 1 yas prof 452157440 12月 31 16:54 GRTAGS
-rw-r--r-- 1 yas prof 570753024 12月 31 16:54 GTAGS
$ global task_struct [←]
fs/proc/internal.h
include/linux/sched.h
$ global -rx task_struct | head -5 [←]
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 [←]
   4117   34711  407362
$ 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            924 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 システムコール、プロセスと Linux task_struct

授業時間中に Manaba の小テストに回答しなさい。

なるべく授業時間中に次の問題を解き、Manaba の「レポート」で回答しなさい。

★問題(101) 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.
...
このシステム・コールを処理する関数がカーネルの中でどのように定義されて いるか、その概略(引数と結果の宣言)を示しなさい。関数の内容は空でよい。 マクロを利用しても利用しなくてもどちらでもよい。
引数と結果の宣言
{
   /*内容*/
}

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

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

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


Last updated: 2022/01/04 21:33:27
Yasushi Shinjo / <yas@cs.tsukuba.ac.jp>