本記事の信頼性
- リアルタイムシステムの研究歴12年.
- 東大教員の時に,英語でOS(Linuxカーネル)の授業.
- 2012年9月~2013年8月にアメリカのノースカロライナ大学チャペルヒル校(UNC)コンピュータサイエンス学部で客員研究員として勤務.C言語でリアルタイムLinuxの研究開発.
- プログラミング歴15年以上,習得している言語: C/C++,Python,Solidity/Vyper,Java,Ruby,Go,Rust,D,HTML/CSS/JS/PHP,MATLAB,Verse(UEFN), Assembler (x64,ARM).
- 東大教員の時に,C++言語で開発した「LLVMコンパイラの拡張」,C言語で開発した独自のリアルタイムOS「Mcube Kernel」をGitHubにオープンソースとして公開.
- 2020年1月~現在はアメリカのノースカロライナ州チャペルヒルにあるGuarantee Happiness LLCのCTOとしてECサイト開発やWeb/SNSマーケティングの業務.2022年6月~現在はアメリカのノースカロライナ州チャペルヒルにあるJapanese Tar Heel, Inc.のCEO兼CTO.
- 最近は自然言語処理AIとイーサリアムに関する有益な情報発信に従事.
- (AI全般を含む)自然言語処理AIの論文の日本語訳や,AIチャットボット(ChatGPT,Auto-GPT,Gemini(旧Bard)など)の記事を50本以上執筆.アメリカのサンフランシスコ(広義のシリコンバレー)の会社でプロンプトエンジニア・マネージャー・Quality Assurance(QA)の業務委託の経験あり.
- (スマートコントラクトのプログラミングを含む)イーサリアムや仮想通貨全般の記事を200本以上執筆.イギリスのロンドンの会社で仮想通貨の英語の記事を日本語に翻訳する業務委託の経験あり.
こういった私から学べます.
前回を読んでいない方はこちらからどうぞ.
Linuxカーネルの記事一覧はこちらからどうぞ.
LinuxカーネルはC言語で書かれています.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!
今回のテーマは,「アイソレーションとシステムコール」です.
アイソレーションとシステムコールの仕組みを理解するために,まずカーネルコードの読み方を紹介します.
目次
カーネルコードの読み方
Linuxカーネルのソースコード(カーネルコード)は合計2,800万行以上と膨大で,全てを読み解くことは困難です.
そこで,自分が求める部分のカーネルコードを発見して読み解くためのトップダウン法とボトムアップ法,カーネルコードをナビゲートするための方法を解説します.
トップダウン法
トップダウン法で,カーネルコードを読み解く方法を紹介します.
例えば,ext4ファイルシステムの場合は以下になります.
- OSのファイルシステムに関する一般的な理解
- Linuxカーネルのファイルシステムの理解(後の回で紹介)
- ext4のドキュメントとディスクレイアウトのチェック
- ext4カーネルコードの読解
- モジュール毎(例:ディレクトリ,ファイル,ブロック管理)
- システムコールから開始(例:どのようにwriteシステムコールが実装されているのか?)
- LWNを検索して最新の変更点を確認(例:ext4の暗号化サポート)
ボトムアップ法
ボトムアップ法では,関数トレーサーを利用します.
- ftrace:関数トレーサーのフレームワーク
- perf(perf tools):ftraceのフロントエンドのLinuxカーネルの性能解析ツール
- trace-cmd:ftraceを操作するツール
以下に,trace-cmdでlsコマンドを実行した場合のvfs_read関数を追跡結果を示します.
trace-cmdは,ftraceによるLinuxカーネルの関数呼び出しのグラフを追跡し,呼び出された関数とタイムスタンプを表示していることがわかります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
$ sudo trace-cmd record -p function_graph -g vfs_read ls $ trace-cmd report CPU 1 is empty CPU 2 is empty CPU 3 is empty CPU 4 is empty CPU 5 is empty CPU 6 is empty CPU 7 is empty CPU 8 is empty CPU 9 is empty CPU 10 is empty CPU 11 is empty CPU 13 is empty CPU 15 is empty cpus=16 ls-6924 [014] 42716.536497: funcgraph_entry: 0.739 us | mutex_unlock(); ls-6924 [014] 42716.537169: funcgraph_entry: | vfs_read() { ls-6924 [014] 42716.537169: funcgraph_entry: | rw_verify_area() { ls-6924 [014] 42716.537170: funcgraph_entry: | security_file_permission() { ls-6924 [014] 42716.537170: funcgraph_entry: | apparmor_file_permission() { ls-6924 [014] 42716.537170: funcgraph_entry: | aa_file_perm() { ls-6924 [014] 42716.537170: funcgraph_entry: 0.093 us | rcu_read_unlock_strict(); ls-6924 [014] 42716.537170: funcgraph_exit: 0.333 us | } ls-6924 [014] 42716.537170: funcgraph_exit: 0.518 us | } ls-6924 [014] 42716.537170: funcgraph_entry: 0.089 us | __fsnotify_parent(); ls-6924 [014] 42716.537170: funcgraph_exit: 0.890 us | } ls-6924 [014] 42716.537170: funcgraph_exit: 1.058 us | } ... |
trace-cmdのGUIツール「KernelShark」を知りたいあなたはこちらからどうぞ.
カーネルコードをナビゲートする方法
カーネルコードをナビゲートするためには,cscopeを使います.
以下にcscopeを利用してvi(vimはviの高機能版)でカーネルコードをナビゲートする例を示します.
※vimのナビゲート方法の詳細は,「Browsing programs with tags」を参照して下さい.
1 2 3 4 5 6 7 8 9 10 11 12 |
$ KBUILD_ABS_SRCTREE=1 make ARCH=x86_64 cscope tags -j2 # KBUILD_ABS_SRCTREE=1 # use absolute path # ARHC=x86_64 # select CPU architcture # cscope # build cscope database # tags # build ctag database # -j2 # concurrently index source code using 2 CPUs $ vim # :tag <symbol> # search symbol definition # :cs find s <symbol> # find uses of symbol # Ctrl-] # search symbol definition on the cursor # Ctrl-t # returning after a tag jump # :bp :bn # nativate back and forth between files |
アイソレーションとシステムコール
カーネルコードの読み方がわかったところで,今回のテーマ「アイソレーションとシステムコール」を解説していきます.
第1回でOSデザインは以下の4つの役割があることを述べました.
・利便性や移植性の向上するためにハードウェアの抽象化(ユーザがハードウェアの機能を関数呼び出しで(暗黙的に)利用できること)
・複数のアプリケーション間のハードウェアの多重化(どのアプリケーションがどのハードウェアを利用しているかを関数呼び出しで(暗黙的に)利用できること)
・バグを含むアプリケーションのアイソレーション(分離して独立で実行すること)
・アプリケーション間でのデータ等の共有の許可
https://hiroyukichishiro.com/linux-kernel-vol1/#Linux
今回のテーマは,以下の2つです.
- ユーザアプリケーションをカーネルから分離するには?
- ユーザアプリケーションからカーネルに安全にアクセスするには?
それでは,アイソレーションとシステムコールを説明します.
アイソレーションとは
OSにおけるアイソレーション(プロテクション)とは,特定の処理(プロセス等)を他の処理から保護する仕組みです.
アイソレーションによる保護する状況は主に以下の3つになります.
- プロセスXがプロセスYを破壊したり,(CPU,メモリ,FD,資源の枯渇等)をスパイしたりすることから保護
- (カーネルによる分離を防止するために)プロセスがOS自体を破壊することから保護
- (例:悪いプロセスがハードウェアやカーネルを騙そうとする可能性がある)バグや悪意に直面した場合に保護
また,OSにおける分離機構は以下の4つになります.
- ユーザ・カーネルモードのフラグ(x86-64はリングプロテクション,ARM64(AARCH64)は例外レベル)
- アドレス空間(後の回で紹介)
- タイムスライス(後の回で紹介)
- システムコールのインターフェース
x86-64のアイソレーション
x86-64のアイソレーションを紹介します.
x86-64のリングプロテクションはring 0~ring 3まで,以下のように設定されています.
- ring 0:OS
- ring 1,ring 2:デバイスドライバ等(あまり利用されない)
- ring 3:アプリ
つまり,ring 0は最高の特権レベル,ring 3は最低の特権レベルとなります.
しかし,x86-64のリングプロテクションの登場後に,ring 0のOSよりアイソレーションレベルが高いハイパーバイザーやシステムマネジメントモードが登場しました.
そこで,ring 0よりアイソレーションレベルが高いring -1をハイパーバイザー,ring -2をシステムマネジメントモードに利用します.
※x86-64のリングプロテクションにはring -1,ring -2は存在せず,説明上の概念になります.
ARM64のアイソレーション
ARM64のアイソレーションを紹介します.
ARM64は例外レベル(EL:Exception Level)で特権レベルを設定し,x86-64のリングプロテクションと同様にEL0~EL3です.
しかし,x86-64のリングプロテクションとは異なり,EL0が最低の特権レベル,EL3が最高の特権レベルになります.
また,EL0がアプリ,EL1がOSを動作する実装になっているため,EL2をハイパーバイザー,EL3をTrustZoneのセキュアモニターに利用します.
※TrustZoneのセキュアモニターはx86-64のシステムマネジメントモードのようなものです.
ARM64は実際の例外レベルを利用できるので,x86-64のリングプロテクションより概念や実装が理解しやすいです.
x86-64とARM64のハードウェアアイソレーションは下表になります.
x86-64のハードウェア アイソレーション | ARM64のハードウェア アイソレーション | 用途 |
---|---|---|
ring 3 | EL0 | アプリ |
ring 0 | EL1 | OS |
ring -1 | EL2 | ハイパーバイザー |
ring -2 | EL3 | システムマネジメントモード(x86-64) TrustZoneのセキュアモニター(ARM64) |
システムコールとは
システムコールは,ユーザ空間のアプリケーションがカーネル空間に入り,OSのサービスやハードウェアへのアクセスなどの特権的な操作を要求するための唯一無二の方法です.
システムコールにより,x86-64だとring 3からring 0,ARM64だとEL0からEL1に特権レベルを切り替えることができます.
これにより,特権レベルでしかアクセスできないディスプレイのようなI/Oポートへアクセスすることができます.
システムコールは以下の機能を提供します.
- ハードウェアとユーザ空間プロセスの間のレイヤ
- ユーザ空間のための抽象的なハードウェアインタフェース
- システムのセキュリティと安定性を確保
上図にシステムコールの呼び出しと復帰の流れを示します.
プログラムはユーザモードで実行し,システムコールの呼び出しでカーネルモードに切り替わります.
そして,システムコールから復帰した時にユーザモードに戻ります.
システムコールは,以下のアーキテクチャ固有のシステムコール専用命令のラッパー関数として実装されます.
- x86-64
- ARM64
また,システムコールから戻る命令は以下になります.
- x86-64
- ARM64
システムコールの例
システムコールの例を以下に紹介します.
- プロセス管理やスケジューリング
- fork:子プロセスを生成します.
- exit:呼び出し元のプロセスを終了させます.
- execve:プログラムを実行します.
- nice:プロセスの優先度を変更します.
- getpriority:プログラムのスケジューリングの優先度を取得します.
- setpriority:プログラムのスケジューリングの優先度を設定します.
- メモリ管理
- ファイルシステム
- プロセス間通信
- 時間管理
- gettimeofday:時刻を取得します.
- settimeofday:時刻を設定します.
- その他
システムコールの実装
システムコールは,システムコールの関数ポインタの配列(syscall table)で管理されています.
x86-64の場合はlinux/arch/x86/entry/syscalls/syscall_64.tbl,ARM64(AARCH64)の場合はlinux/include/uapi/asm-generic/unistd.hにあります.
x86-64はアーキテクチャ固有のシステムコールID,ARM64は他のアーキテクチャと共通のシステムコールIDになります.
システムコール一覧はこちらがわかりやすいです.
例えば,システムコールIDは以下のような違いがあります.
- readシステムコールのID:x86-64の場合は0,ARM64の場合は3
- writeシステムコールのID:x86-64の場合は1,ARM64の場合は64
また,システムコールの配列はsys_call_tableという変数で定義され,名前はx86-64やARM64を含む全アーキテクチャ共有です.
sys_call_tableの実態は以下にあります.
- x86-64:linux/arch/x86/entry/syscall_64.c
- ARM64:linux/arch/arm/kernel/sys.c
linux/fs/read_write.cにあるreadシステムコールの実装は以下になります.
23行目のSYSCALL_DEFINE3マクロ(3つのパラメータを持つシステムコールを定義するためのマクロ)でreadシステムコールの呼び出しをカーネル側で受け取り,4行目のksys_read関数で実際の読み込み処理をします.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
/* linux/fs/read_write.c */ /* ssize_t read(int fd, void *buf, size_t count); */ ssize_t ksys_read(unsigned int fd, char __user *buf, size_t count) { struct fd f = fdget_pos(fd); ssize_t ret = -EBADF; if (f.file) { loff_t pos, *ppos = file_ppos(f.file); if (ppos) { pos = *ppos; ppos = &pos; } ret = vfs_read(f.file, buf, count, ppos); if (ret >= 0 && ppos) { f.file->f_pos = pos; } fdput_pos(f); } return ret; } |
上図にユーザ空間からのシステムコールの呼び出しを示します.
システムコールが直接呼び出されることはほとんどありません.
それらのほとんどはC言語のライブラリ(glibc等)によりラッパー関数として呼び出されます.
syscall関数を利用してアプリケーションからのシステムコールの呼び出し
1 |
long syscall(long number, ...); |
syscall関数を利用してアプリケーションからのシステムコールを呼び出します.
syscall関数は,システムコールを起動する小さなライブラリ関数です.
numberで指定されたアセンブリ言語インターフェースのシステムコールを,指定された引数を設定して実行します.
syscall関数が役に立つのは,C言語のライブラリにラッパー関数が存在しないシステムコールを呼び出したい場合です.
例えば,sched_setattr/sched_getattrシステムコールが挙げられます.
sched_setattr/sched_getattrシステムコールを利用するリアルタイムスケジューリングRMとEDFの実装を知りたいあなたはこちらからどうぞ.
x86-64のアプリケーションからのwriteシステムコールの呼び出し
x86-64のアプリケーションからのwriteシステムコールの呼び出しは以下のコードになります.
12行目で呼び出しているsyscall関数の引数は以下の意味になります.
- 第1引数の1:x86-64のシステムコールの1番(writeシステムコール)
- 第2引数の1:writeシステムコールの第1引数fd(1は標準出力)
- 第3引数のstr:writeシステムコールの第2引数buf(出力する文字列)
- 第4引数の14:writeシステムコールの第3引数count(出力する文字列"Hello World!\n"のバイト数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <unistd.h> #include <sys/syscall.h> int main(void) { char str[] = "Hello World!\n"; syscall(1, 1, str, 14); return 0; } |
syscall.cのアセンブリ言語(x86-64)は以下の手順で作成して,catコマンドでstack.sの中身を表示します.
30行目でsyscall関数を呼び出していることがわかります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
$ gcc syscall.c -S $ cat syscall.s .file "syscall.c" .text .globl main .type main, @function main: .LFB0: .cfi_startproc endbr64 pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $32, %rsp movq %fs:40, %rax movq %rax, -8(%rbp) xorl %eax, %eax movabsq $8022916924116329800, %rax movq %rax, -22(%rbp) movl $560229490, -14(%rbp) movw $10, -10(%rbp) leaq -22(%rbp), %rax movl $14, %ecx movq %rax, %rdx movl $1, %esi movl $1, %edi movl $0, %eax call syscall@PLT movl $0, %eax movq -8(%rbp), %rdx subq %fs:40, %rdx je .L3 call __stack_chk_fail@PLT .L3: leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Ubuntu 11.2.0-19ubuntu1) 11.2.0" .section .note.GNU-stack,"",@progbits .section .note.gnu.property,"a" .align 8 .long 1f - 0f .long 4f - 1f .long 5 0: .string "GNU" 1: .align 8 .long 0xc0000002 .long 3f - 2f 2: .long 0x3 3: .align 8 4: |
実行結果は以下になります.
syscall関数でwriteシステムコールを直接呼び出すことで「Hello World!」を表示していることがわかります.
※stdio.hをインクルードしてprintf関数のような標準ライブラリ関数を呼び出さずに文字列を表示していることに着目して下さい.
1 2 3 |
$ gcc syscall.c $ a.out Hello World! |
ARM64のアプリケーションからのwriteシステムコールの呼び出し
ARM64のアプリケーションからのwriteシステムコールの呼び出しは以下になります.
writeシステムコールのIDはx86-64では1ですが,ARM64では64であることに注意して下さい.
つまり,syscall関数の第1引数が1から64に変更します.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <unistd.h> #include <sys/syscall.h> int main(void) { char str[] = "Hello World!\n"; syscall(64, 1, str, 14); return 0; } |
実行結果は以下になります.同様です.
1 2 3 |
$ aarch64-linux-gnu-gcc syscall_arm64.c $ a.out Hello World! |
gettimeofdayシステムコールの実装と利用例
1 |
int gettimeofday(struct timeval *tv, struct timezone *tz); |
gettimeofdayシステムコールは,時刻を取得します.
gettimeofdayシステムコールの使い方は以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> #include <time.h> #include <sys/time.h> int main(void) { struct timeval tv; gettimeofday(&tv, NULL); printf("tv.tv_sec = %ld, tv.tv_usec = %ld\n", tv.tv_sec, tv.tv_usec); printf("localtime() = %s", asctime(localtime(&tv.tv_sec))); return 0; } |
実行結果は以下になります.
1 2 3 4 |
$ gcc gettimeofday.c $ a.out tv.tv_sec = 1658133372, tv.tv_usec = 11311 localtime() = Mon Jul 18 17:36:12 2022 |
Linuxのカーネルのgettimeofdayシステムコールの実装はlinux/kernel/time/time.cにあります.
ここで,SYSCALL_DEFINE2は,2つのパラメータを持つシステムコールを定義するためのマクロです.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
SYSCALL_DEFINE2(gettimeofday, struct __kernel_old_timeval __user *, tv, struct timezone __user *, tz) { if (likely(tv != NULL)) { struct timespec64 ts; ktime_get_real_ts64(&ts); if (put_user(ts.tv_sec, &tv->tv_sec) || put_user(ts.tv_nsec / 1000, &tv->tv_usec)) return -EFAULT; } if (unlikely(tz != NULL)) { if (copy_to_user(tz, &sys_tz, sizeof(sys_tz))) return -EFAULT; } return 0; } |
ユーザ空間とカーネル空間のメモリ転送
ユーザ空間とカーネル空間のメモリ転送を解説します.
アイソレーションにより,ユーザ空間のアプリケーションはカーネル空間のメモリにアクセスできません.
また,カーネル空間ではユーザ空間へのポインタに盲目的に従わないことが大事です.
なぜかというと,間違ったユーザアドレスにアクセスすると,カーネルがクラッシュすることがあります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
static __always_inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n) { if (likely(check_copy_size(to, n, false))) n = _copy_from_user(to, from, n); return n; } static __always_inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n) { if (likely(check_copy_size(from, n, true))) n = _copy_to_user(to, from, n); return n; } |
そこで,上記のcopy_from_user/copy_to_user関数を利用してカーネル空間からユーザ空間のメモリにアクセスします.
提供されたユーザ空間のメモリが正当でない場合,不正アクセスエラーを発生します.
ユーザ空間のメモリが存在しない場合(スワップアウトされた場合),カーネルはスワップインするまでスリープし,スワップイン後にユーザ空間のメモリにアクセスします.
新しいシステムコールの実装方法
新しいシステムコールの実装方法を知りたいあなたはこちらの記事を読みましょう!
システムコールの性能向上
システムコールの性能は多くのアプリケーションで重要です.
- Webサーバ:select,poll
- ゲームエンジン:gettimeofday
それでは,システムコールの性能向上のためのハードウェアとソフトウェアの方法を紹介していきます.
ハードウェア:Intelのsysenter/sysexit命令,AMDのsyscall/sysret命令
x86-64では,「int 0x80」(ソフトウェア割り込みを発生させるint命令の引数0x80)をシステムコールに利用していました.(ソフトウェア割り込みから戻る時はiret命令を利用します.)
1998年にIntelのPentium IIがsysenter/sysexit命令を実装したことで,Linuxカーネルはsysenter/sysexit命令に置き換えられました.(AMDはsyscall/sysret命令です.)
Intelのsysenter/sysexit命令は,int 0x80と比較してソフトウェア割り込みによるオーバヘッドを削減します.
ソフトウェア:vDSO(virtual dynamically linked shared object)
vDSO(virtual dynamically linked shared object)は,カーネル空間の関数をユーザ空間のアプリケーションにエクスポートすることで性能向上するカーネル機構です.
vDSOは,システムコールインターフェースを使って同じカーネル空間ルーチンを呼び出す場合に利用できます.
ユーザモードからカーネルモードへのモード切り替えによる性能低下を招くことなく,アプリケーションからプロセス内のカーネル空間ルーチンを呼び出せるようになります.
つまり,コンテストスイッチが不要になり,そのオーバヘッドが0になります.
vDSOの主な利用例は,gettimeofdayシステムコールです.
ソフトウェア:例外を少なくする(Exception-Lessな)システムコール
例外を少なくする(Exception-Lessな)システムコールを紹介します.
FlexSCは,FlexSCは2010年に国際会議OSDIで発表された例外を少なくするシステムコールの柔軟なシステムコールのスケジューリングです.
FlexSCの論文とGitHub(論文の著者ではない実装)は以下になります.
- 論文:FlexSC: Flexible System Call Scheduling with Exception-Less System Calls
- GitHub:Implementation of FlexSC on Linux Kernel v5.0+ and Performance Analysis
vDSOとは異なり,FlexSCは現在Linuxカーネルのメインラインにマージされていないので注意して下さい.
FlexSCによる性能向上は以下になります(アプリケーションを変更せずに実現).
- Apacheの性能を最大116%向上
- MySQLの性能を最大40%向上
- BINDの性能を最大105%向上
他には,nginxのようなWebサーバ(イベントドリブンサーバ)で利用される「Exception-Less System Calls for Event-Driven Servers」の論文もあります.
ソフトウェア:終了を少なくする(Exit-Lessな)システムコール
終了を少なくする(Exit-Lessな)システムコールに関する論文は以下になります.
これらの論文を読んで,終了を少なくするシステムコールと例外を少なくするシステムコールの違いを学びましょう!
- I/Oの仮想化:ELI: bare-metal performance for I/O virtualization
- Intel SGX(Intel Software Guard eXtension):Eleos: ExitLess OS Services for SGX Enclaves
まとめ
今回はアイソレーションとシステムコールを紹介しました.
アイソレーションによりユーザアプリケーションをカーネルから分離し,システムコールによりユーザアプリケーションからカーネルに安全にアクセスすることがわかりました.
システムコールを深く理解したいあなたは,以下の記事を読みましょう!
- Anatomy of a system call, part 1,Anatomy of a system call, part 2
- On vsyscalls and the vDSO
- System calls
LinuxカーネルはC言語で書かれています.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!
次回はこちらからどうぞ.