TECHNOLOGY LINUX KERNEL

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

本記事の信頼性

  • リアルタイムシステムの研究歴12年.
  • 東大教員の時に,英語でOSの授業.
  • 2012年9月~2013年8月にアメリカのノースカロライナ大学チャペルヒル校コンピュータサイエンス学部2021年の世界大学学術ランキングで20位)で客員研究員として勤務.C言語でリアルタイムLinuxの研究開発
  • プログラミング歴15年以上,習得している言語: C/C++,Java,Python,Ruby,HTML/CSS/JS/PHP,MATLAB,Assembler (x64,ARM).
  • 東大教員の時に,C++言語で開発した「LLVMコンパイラの拡張」,C言語で開発した独自のリアルタイムOS「Mcube Kernel」GitHubにオープンソースとして公開

こういった私から学べます.

前回を読んでいない方はこちらからどうぞ.

第1回Linuxカーネル
【第1回】元東大教員から学ぶLinuxカーネル「Linuxカーネルとは」

こういった私から学べます. Linuxカーネルの記事一覧はこちらからどうぞ. 目次1 Linuxカーネルとは2 Linuxの歴史3 Linuxのオープンソースモデル4 Linuxカーネルのリリースサイ ...

続きを見る

Linuxカーネルの記事一覧はこちらからどうぞ.

Linuxカーネル
元東大教員から学ぶLinuxカーネル

こういった私から学べます. Linuxカーネルとは,オープンソースで広く使われているOSです. LinuxカーネルはAndroidに使われているので,スマホの中身を知りたいあなたにおすすめです. 目次 ...

続きを見る

今回は,Linuxカーネルの開発ツールとカーネル vs. ユーザプログラミングを紹介します.

なぜ開発ツールが重要なのかというと,Linuxのソースコードは膨大で非常に速く進化するからです.

ソースコードの行数は2,600万行以上で,リリース毎に約1,600人の開発者が携わっています.

以下にLinuxカーネルのバージョン5.3のソースコードでtreeコマンド(ディレクトリ構造をツリー表示)を実行した結果を示します.

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

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

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

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

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

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

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

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

必須のgitコマンド

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

gitワークフロー

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

git workflow

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

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

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

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

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

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

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

  1. カーネルの構成(configure)
    構成ファイルはコンパイルオプションを定義しています.(x86の場合は~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の場合は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の場合はarch/x86/boot/bzImageに生成)

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

カーネルのコンパイルを高速化するために,並列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)) {…}
    • if (likely(success)) {…}

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

また,x86では,小さい固定サイズの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言語を学べるおすすめのオンラインプログラミングスクール3社で自分に合うスクールを見つけましょう.

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