TECHNOLOGY LINUX KERNEL

【第2回】元東大教員から学ぶLinuxカーネル「開発ツールとカーネルプログラミング vs. ユーザプログラミング」

2021年2月6日

本記事の信頼性

  • リアルタイムシステムの研究歴12年.
  • 東大教員の時に,英語でOS(Linuxカーネル)の授業.
  • 2012年9月~2013年8月にアメリカのノースカロライナ大学チャペルヒル校(UNC)コンピュータサイエンス学部で客員研究員として勤務.C言語でリアルタイムLinuxの研究開発.
  • プログラミング歴15年以上,習得している言語: C/C++PythonSolidity/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コマンド(ディレクトリ構造をツリー表示)を実行した結果を示します.

まず,以下のLinuxカーネル開発ツールを紹介します.

  • git,tig:バージョンコントロール
  • make:カーネルの構成,ビルド,インストール
  • vi,emacs:エディタ
  • cscope,ctags:コードの探求
  • tmux:スクリーン

最後に,カーネルとユーザプログラミングの違いを説明します.

git,tig:バージョンコントロール

Gitはバージョン管理ソフトウェアで,コンピュータのファイルの変更を追跡できます.

Linuxの創始者のリーナス・トーバルズによりLinuxカーネルのためにgitは開発され,現在は多くのソフトウェア開発で幅広く利用されています.

また,gitのサービスはGitHubで提供されています.

GitHubには,Linuxカーネルのリポジトリがあります.

gitは分散型バージョン管理システムなので,すべてのコンピュータ上のすべてのgitディレクトリは完全な履歴を持つ本格的なリポジトリになります.

必須のgitコマンド

他にも役に立つgitチュートリアルは以下になります.

gitワークフロー

Pro Gitを参照して,gitのワークフローについて解説します.

git workflow

上図にgitプロジェクトのワークフローを記載します.

  1. gitディレクトリ(リポジトリ)からプロジェクトをチェックアウトします.
  2. 作業ディレクトリでステージの修正をして,ステージエリアに移行します.
  3. ステージングエリアでコミットして.gitディレクトリ(リポジトリ)を更新します.

git workflow2

上図にgitプロジェクト内のファイルのワークフローを示します.

  1. ファイルを追加すると未追跡ファイルからステージファイルに移行します.
  2. 既存のファイルを修正すると,未修正ファイルから修正ファイルに移行します.
  3. 修正ファイルをステージ化するとステージファイルに移行します.
  4. 既存のファイルを削除すると,未修正ファイルから未追跡ファイルに移行します.
  5. ステージファイルをコミットするとリポジトリが更新され,未修正ファイルに移行します.

tig:端末上でgit操作ができるツール

tigは,端末上でgit操作ができるツールです.

tigを使いこなすとgit操作の効率が向上するので,是非習得しましょう!

tigの使い方は,以下の記事がわかりやすいです.

make:カーネルの構成,ビルド,インストール

カーネルのソースコードをダウンロード,バージョン5.15.0のチェックアウト,treeコマンドの実行を以下に示します.

カーネルのビルド手順は以下になります.

  1. カーネルの構成(configure)
    構成ファイルはコンパイルオプションを定義しています.(x86-64の場合は~3,700行くらい)
  2. カーネルのコンパイル
    カーネルのソースコードのコンパイルとリンク
  3. 新しいカーネルのインストール
    コンパイルした新しいカーネルをシステムにインストール

他の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にはソースコード中にあるプリプロセッサのフラグを設定します.

以下にプリプロセッサの例を示します.

上記のCONFIG_KVM_GUESTとCONFIG_XFS_FSは,linux/arch/x86/kernel/cpu/hypervisor.cで利用されています.

カーネルをコンパイルするには,makeを実行します.

すると,カーネルのソースコードをコンパイルしてカーネルのイメージが作成されます.(x86-64の場合はarch/x86/boot/bzImageに生成)

次に,make modulesを実行して,モジュールをコンパイルします.

カーネルのコンパイルを高速化するために,並列makeという手法があります.

以下に例を示します.

新しいカーネルのインストールや確認方法は以下になります.

C言語でMakeの使い方を知りたいあなたはこちらからどうぞ.

vi,emacs:エディタ

カーネルのソースコードの編集には,vi(Linuxには上位互換のvimを搭載)やemacsが主に利用されます.

viは軽くて速くてLinuxに普通は入っているテキストエディタです.

viは以下の場合に役に立ちます.

  • 設定ファイルの変更の時
  • emacs等がない環境で急遽編集が必要な時(深刻なエラーに陥ったときのデータ救済時等)

また,日常的な文章作成業務を全てviでやる方もいます.

viは非常にクセのあるエディタで習熟曲線は極端です(下図参照).

vi vs. emacs

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の利用例を示します.

LXR

cscope

cscopeは,(大規模になる可能性のある)C言語を閲覧するコマンドラインツールです.

インストールとビルド方法は以下になります.コード変更後にはリビルドが必要になります.

cscopeは以下を検索できます.

  • C言語の識別子(変数名,関数名,typedef,struct,label)
  • 関数や変数の定義
  • 関数funcに呼ばれたり(called by),呼んだり(calling)する関数
  • 文字列

cscopeを終了したい時はCtrl + dを入力します.

cscope
赤色が入力エリア,黄色が出力エリア

cscope2
Cシンボルでspin_lockを入力

cscope3
spin_lockの定義一覧の表示

cscope4
グローバルの定義にspin_lockを入力

cscope5
spinlock.hの6を選択

cscope6
spin_lock関数の定義

cscopeやctagsを利用したvi

viはctagsと同様にcscopeのタグデータベースを利用できます.

データベースを生成します.

viを起動してファイル(以下の例ではinit/main.c)を開きます.

関数定義/変数宣言を検索するためには:tagや:cs find globalを利用して以下のように入力します.

ctagsやcscopeのヘルプを読みたい場合は,以下のように入力します.

関数定義/変数宣言を見つける別の方法は,シンボル名にカーソルを合わせ,Ctrl + ]を押します.

ファイル間を行ったり来たりするためには,:bp(前のファイルに移動)や:bn(次のファイルに移動)と入力します.

tmux:スクリーン

tmuxは仮想コンソールを管理するためのツールです.

tmuxにはウインドウ(window:ある一つの画面)とペイン(pane:ある一つの領域)という概念があります.

下図にtmuxでペインを横(上下)に分割して,main.cとcore.cファイルを編集している様子を示します.

tmux
tmuxの利用

必須の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が真になる可能性が高いという前提で最適化

カーネルプログラミングでは,浮動小数点数を利用することは簡単ではありません.

また,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」

まとめ

今回は,Linuxカーネルの開発ツールとカーネルとユーザプログラミングの違いについて紹介しました.

具体的には,以下の内容を解説しました.

  • 開発ツール
    • git,tig:バージョンコントロール
    • make:カーネルの構成,ビルド,インストール
    • vi,emacs:エディタ
    • cscope,ctags:コードの探求
    • tmux:スクリーン
  • カーネルプログラミング vs. ユーザプログラミング

LinuxカーネルはC言語で書かれています.

私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.

私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!

友だち追加

独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!

次回はこちらからどうぞ.

-TECHNOLOGY, LINUX KERNEL
-, , , , , , , ,