本記事の信頼性
- リアルタイムシステムの研究歴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,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社で自分に合うスクールを見つけましょう.後悔はさせません!
目次
開発ツールとカーネルプログラミング vs. ユーザプログラミング
今回は,Linuxカーネルの開発ツールとカーネル vs. ユーザプログラミングを紹介します.
なぜ開発ツールが重要なのかというと,Linuxのソースコードは膨大で非常に速く進化するからです.
ソースコードの行数は,Linuxカーネル5.15.0では約2,836万行で,リリース毎に約1,600人の開発者が携わっています.
以下にLinuxカーネル5.15.0のソースコードでtreeコマンド(ディレクトリ構造をツリー表示)を実行した結果を示します.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ tree . ├── arch │ ├── alpha │ │ ├── boot │ │ │ ├── bootloader.lds │ │ │ ├── bootp.c ... ├── lib │ ├── irqbypass.c │ ├── Kconfig │ └── Makefile └── Makefile 4868 directories, 73307 files |
まず,以下のLinuxカーネル開発ツールを紹介します.
- git,tig:バージョンコントロール
- make:カーネルの構成,ビルド,インストール
- vi,emacs:エディタ
- cscope,ctags:コードの探求
- tmux:スクリーン
最後に,カーネルとユーザプログラミングの違いを説明します.
git,tig:バージョンコントロール
Gitはバージョン管理ソフトウェアで,コンピュータのファイルの変更を追跡できます.
Linuxの創始者のリーナス・トーバルズによりLinuxカーネルのためにgitは開発され,現在は多くのソフトウェア開発で幅広く利用されています.
また,gitのサービスはGitHubで提供されています.
GitHubには,Linuxカーネルのリポジトリがあります.
gitは分散型バージョン管理システムなので,すべてのコンピュータ上のすべてのgitディレクトリは完全な履歴を持つ本格的なリポジトリになります.
必須のgitコマンド
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 |
$ # 1. install git/tig and configure $ sudo dnf install git tig # sudo apt-get install git tig $ git config --global user.name "John Doe" # set your name and email for history $ git config --global user.email johndoe@example.com $ # 2. creat a repository $ git init # create a new local repo $ git clone https://github.com/torvalds/linux.git # clone an existing repo $ # 3. tags $ git tag # list all existing tags $ git checkout v5.15 # checkout the tagged version $ # 4. commit history (or use tig for prettier output) $ git log # show all commit history $ git log <file> # show changes over time for a file $ git blame <file> # who changed what and when in <file> $ # 5. local changes $ git status # show changed files $ git diff # show changed lines $ git add <file> # add <file> to the next commit $ git commit # commit previously staged files to my local repo $ # 6. publish and update $ git push # publish a committed local changes to a remote repo $ git pull # update a local repo |
他にも役に立つgitチュートリアルは以下になります.
- Git Tutorials and Training | Atlassian Git Tutorial
- Set up Git
- Git Tutorial | Tutorialspoint
- gittutorial(7) | Linux Kernel
- Pro Git
gitワークフロー
Pro Gitを参照して,gitのワークフローについて解説します.
上図にgitプロジェクトのワークフローを記載します.
- gitディレクトリ(リポジトリ)からプロジェクトをチェックアウトします.
- 作業ディレクトリでステージの修正をして,ステージエリアに移行します.
- ステージングエリアでコミットして.gitディレクトリ(リポジトリ)を更新します.
上図にgitプロジェクト内のファイルのワークフローを示します.
- ファイルを追加すると未追跡ファイルからステージファイルに移行します.
- 既存のファイルを修正すると,未修正ファイルから修正ファイルに移行します.
- 修正ファイルをステージ化するとステージファイルに移行します.
- 既存のファイルを削除すると,未修正ファイルから未追跡ファイルに移行します.
- ステージファイルをコミットするとリポジトリが更新され,未修正ファイルに移行します.
tig:端末上でgit操作ができるツール
tigは,端末上でgit操作ができるツールです.
tigを使いこなすとgit操作の効率が向上するので,是非習得しましょう!
tigの使い方は,以下の記事がわかりやすいです.
make:カーネルの構成,ビルド,インストール
カーネルのソースコードをダウンロード,バージョン5.15.0のチェックアウト,treeコマンドの実行を以下に示します.
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 |
$ git clone https://github.com/torvalds/linux.git # clone the kernel repo $ cd linux $ git checkout v5.15 $ tree -d -L 1 . |-- Documentation |-- LICENSES |-- arch |-- block |-- certs |-- crypto |-- drivers |-- fs |-- include |-- init |-- ipc |-- kernel |-- lib |-- mm |-- net |-- samples |-- scripts |-- security |-- sound |-- tools |-- usr `-- virt 22 directories |
カーネルのビルド手順は以下になります.
- カーネルの構成(configure)
構成ファイルはコンパイルオプションを定義しています.(x86-64の場合は~3,700行くらい) - カーネルのコンパイル
カーネルのソースコードのコンパイルとリンク - 新しいカーネルのインストール
コンパイルした新しいカーネルをシステムにインストール
他のmakeオプションはmake helpコマンドで見られます.
- make menuconfig(libncursesが必要)
- sudo apt-get install libncurses5-dev # Debian/Ubuntu
- sudo dnf install ncurses-devel # Fedora/CentOS/RedHat
- make defconfig
- 実行中のプラットフォームのデフォルトの構成の生成
- x86-64の場合はlinux/arch/x86/configs/x86_64_defconfigに生成
- make oldconfig
- 実行中のカーネルにおける構成ファイルの利用
- 新しい構成の問い合わせ(もし構成の内容がよくわからない場合,デフォルトのオプションの利用を推奨)
- make localmodconfig
- ロードされていないモジュールを無効にして現在の構成を更新
カーネル構成ファイルは,カーネルのソースのルートにある.configにあります.
.configにはソースコード中にあるプリプロセッサのフラグを設定します.
以下にプリプロセッサの例を示します.
1 2 3 4 |
# linux/.config # CONFIG_XEN_PV is not set CONFIG_KVM_GUEST=y CONFIG_XFS_FS=m |
上記のCONFIG_KVM_GUESTとCONFIG_XFS_FSは,linux/arch/x86/kernel/cpu/hypervisor.cで利用されています.
1 2 3 4 5 6 7 8 9 10 |
/* linux/arch/x86/kernel/cpu/hypervisor.c */ static const __initconst struct hypervisor_x86 * const hypervisors[] = { #ifdef CONFIG_XEN_PV &x86_hyper_xen_pv, #endif #ifdef CONFIG_KVM_GUEST &x86_hyper_kvm, #endif }; |
カーネルをコンパイルするには,makeを実行します.
すると,カーネルのソースコードをコンパイルしてカーネルのイメージが作成されます.(x86-64の場合はarch/x86/boot/bzImageに生成)
次に,make modulesを実行して,モジュールをコンパイルします.
カーネルのコンパイルを高速化するために,並列makeという手法があります.
以下に例を示します.
1 2 3 |
$ make <target> -j<number of CPUs to use> # E.g. $ make -j4 |
新しいカーネルのインストールや確認方法は以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# Install the new kernel modules (if you change modules) $ sudo make modules_install $ ls /lib/modules/ # Install the new kernel image $ sudo make install $ ls /boot/*5.15* /boot/System.map-5.15.0 /boot/config-5.15.0 /boot/initrd.img-5.15.0 /boot/vmlinuz-5.15.0 # Reboot the machine $ sudo reboot # Check if a system boots with the new kernel $ uname -a Linux aria 5.15.0 #21 SMP Wed Mar 30 15:54:22 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux # See kernel log $ dmesg $ dmesg -w # wait for new kernel messages |
C言語でMakeの使い方を知りたいあなたはこちらからどうぞ.
vi,emacs:エディタ
カーネルのソースコードの編集には,vi(Linuxには上位互換のvimを搭載)やemacsが主に利用されます.
viは軽くて速くてLinuxに普通は入っているテキストエディタです.
viは以下の場合に役に立ちます.
- 設定ファイルの変更の時
- emacs等がない環境で急遽編集が必要な時(深刻なエラーに陥ったときのデータ救済時等)
また,日常的な文章作成業務を全てviでやる方もいます.
viは非常にクセのあるエディタで習熟曲線は極端です(下図参照).
viは初心者にはかなり使いづらいですが,慣れると編集処理が非常に速くなります.
viとemacsのどちらが良いのか興味があるあなたは,是非「エディタ戦争」を読みましょう.宗教戦争ですね...
ちなみに私はemacs派です.理由は機能性が高いからです.
もちろんemacsが入っていないLinux環境もありますので,viも習得しています.
viとemacsを両方使いこなせると便利ですので,是非習得しましょう!
viやemacsを利用してコードを効率的に探求するためには,cscopeやctagsを利用します.
次の章でcscopeとctagsの利用方法を説明します.
コードの探求
コードの探求方法として,以下が挙げられます.
- Linux Cross Reference(LXR)
- viとcscopeやctagsの利用
- emacsとcscopeの利用
Linux Cross Reference(LXR)
Linux Cross Reference(LXR)は,Webサービスを利用したコードのインデックスツールです.
LXRは以下の機能があります.
- 異なるLinuxのバージョンのコードを閲覧
- (関数や変数等の)識別子の検索
- 関数の宣言/定義を素早く検索
以下にLXRの利用例を示します.
cscope
cscopeは,(大規模になる可能性のある)C言語を閲覧するコマンドラインツールです.
インストールとビルド方法は以下になります.コード変更後にはリビルドが必要になります.
1 2 3 4 5 6 7 |
# 1. install cscope $ sudo apt-get install cscope # dnf install cscope # choose 2.a or 2.b ## 2.a configure cscope for all architectures $ KBUILD_ABS_SRCTREE=1 make cscope # for all architectures ## 2.b configure cscope for x86 architectures $ KBUILD_ABS_SRCTREE=1 ARCH=x86 make cscope # for x86 architecture |
cscopeは以下を検索できます.
- C言語の識別子(変数名,関数名,typedef,struct,label)
- 関数や変数の定義
- 関数funcに呼ばれたり(called by),呼んだり(calling)する関数
- 文字列
cscopeを終了したい時はCtrl + dを入力します.
cscopeやctagsを利用したvi
viはctagsと同様にcscopeのタグデータベースを利用できます.
1 2 |
$ sudo dnf install cscope ctags # Fedora/CentOS/RedHat $ sudo apt-get install cscope exuberant-ctags # Debian/Ubuntu |
データベースを生成します.
1 |
$ make cscope tags -j2 |
viを起動してファイル(以下の例ではinit/main.c)を開きます.
1 |
$ vi init/main.c |
関数定義/変数宣言を検索するためには:tagや:cs find globalを利用して以下のように入力します.
1 2 |
# search for start_kernel function :tag start_kernel or :cs find global start_kernel |
ctagsやcscopeのヘルプを読みたい場合は,以下のように入力します.
1 2 |
# help for ctags and cscope :help tag or :help cs |
関数定義/変数宣言を見つける別の方法は,シンボル名にカーソルを合わせ,Ctrl + ]を押します.
ファイル間を行ったり来たりするためには,:bp(前のファイルに移動)や:bn(次のファイルに移動)と入力します.
tmux:スクリーン
tmuxは仮想コンソールを管理するためのツールです.
tmuxにはウインドウ(window:ある一つの画面)とペイン(pane:ある一つの領域)という概念があります.
下図にtmuxでペインを横(上下)に分割して,main.cとcore.cファイルを編集している様子を示します.
必須のtmuxコマンドは以下になります.
ここで,Ctrl + b, %は,Ctrlを押しながらbを入力した後に%を入力します.
- tmux:新しいtmuxセッションの開始
- Ctrl + b, %:ウインドウのペインを縦(左右)に分割
- Ctrl + b, ":ウインドウのペインを横(上下)に分割
- Ctrl + b, o:次のペインに移動
- Ctrl + b, z:ペインのズーム(またはアンズーム)
- Ctrl + b, c:新しいウィンドウの生成
- Ctrl + b, N:ウインドウN(0~9)に移動
- Ctrl + b, d:セッションからデタッチ
- tmux a:既存のセッションにアタッチ
カーネルプログラミング vs. ユーザプログラミング
カーネルプログラミングとユーザプログラミングの大きな違いは,カーネルプログラミングにはlibcと標準ヘッダファイルの両方がないことです.
代わりに多くのlibc-likeな関数がカーネルに実装されています.
例えば,以下の表になります.
カーネルプログラミング | ユーザプログラミング |
---|---|
#include <linux/string.h> | #include <string.h> |
kmalloc(64, GFP_KERNEL); | malloc(64); |
printk(KERN_INFO, "Hello!"); | printf("hello!"); |
カーネルプログラミングはコンパイラはGCCを想定しているので,GCC拡張を利用しています.
また,LLVMはバージョン9.0.0からasm gotoをサポートしたことで,カーネルをコンパイルできます.
ユーザプログラミングでもGCC拡張を利用できますが,GCC(またはLLVM9.0.0以上)に依存するコードになり,他のコンパイラ(Visual Studio C/C++ CompilerやIntel C/C++ Compiler等)ではコンパイルできません.
GCC拡張の例は以下になります.
- インライン関数
- static inline void func()
- インラインアセンブラ(全体の2%以下のコードで利用)
- asm volatile("rdtsc" : "=a" (l), "=d" (h));
- ブランチアノテーション:より良い最適化のためのヒント
- if (unlikely(error)) {…}
- errorが偽になる可能性が高いという前提で最適化
- if (likely(success)) {…}
- successが真になる可能性が高いという前提で最適化
- if (unlikely(error)) {…}
カーネルプログラミングでは,浮動小数点数を利用することは簡単ではありません.
また,x86-64では,小さい固定サイズの8KB(標準は1ページ4KBなので,2ページ分)を利用します.
メモリ保護がなく,SIGSEGVでカーネルパニックが発生します.
カーネルパニックの例は,「Random kernel panic after re-installing Arch Linux」になります.
カーネルプログラミングでは,同期と並行性が重要なテーマになります.
- タスク間の同期の必要性
- マルチコアプロセッサでは,カーネルのコードが複数のコアで実行するため
- プリエンプティブなマルチタスクでは,いつでもタスクがスケジュール(もしくはリスケジュール)されるため
- 割り込みが発生した時に割り込みハンドラ内の同期
- 資源アクセス等の実行中に発生する可能性あり
Linuxカーネルのコーディングスタイルは,以下になります.
- インデント1タブは8文字幅(スペース8個ではない)
- キャメルケース(Camel Case)ではなくアンダースコアの利用(SpinLock → spin_lock)
- Cスタイルの利用:/* こちらのコメントを利用 */ // こちらのコメントではない
- 行幅:80文字
- 他のカーネルコードと似たようなスタイルでコードのライティング(詳細は「Linux kernel coding style」)
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 |
/* * a multi-lines comment * (no C++ '//' !) */ struct foo { int member1; double member2; }; /* no typedef ! */ #ifdef CONFIG_COOL_OPTION int cool_function(void) { return 42; } #else int cool_function(void) { } #endif /* CONFIG_COOL_OPTION */ void my_function(int the_param, char *string, int a_long_parameter, int another_long_parameter) { int x = the_param % 42; if (!the_param) { do_stuff(); } switch (x % 3) { case 0: do_some_stuff(); cool_function(); break; case 1: /* Fall through */ default: do_other_stuff(); cool_function(); } } |
まとめ
今回は,Linuxカーネルの開発ツールとカーネルとユーザプログラミングの違いについて紹介しました.
具体的には,以下の内容を解説しました.
- 開発ツール
- git,tig:バージョンコントロール
- make:カーネルの構成,ビルド,インストール
- vi,emacs:エディタ
- cscope,ctags:コードの探求
- tmux:スクリーン
- カーネルプログラミング vs. ユーザプログラミング
LinuxカーネルはC言語で書かれています.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!
次回はこちらからどうぞ.