Linux Kernel で提供される標準ライブラリはおおよそアプリケーション向けの glibc に近い機能が提供されています。加えてビット操作と kernel 内ならではのアルゴリズム群があります。アルゴリズム群は「こんなのも有るのか」程度に知っておくと良いでしょう。アプリケーション向けに作られたライブラリが移植されていることもあるので、欲しいと思ったら Linux Kernel ソースコード検索 から探してみるのも良いでしょう。
要注意なことは、アプリケーションプログラムでは普通に除算 '/'、剰余 '%' 演算子を用いた式を long long 型に対して書けば計算可能なのに対し Linux Kernel 内では linux/math64.h からインクルードされる関数群を使用して関数呼び出し(あるいはマクロ呼び出し)の形で計算する必要があることです。
双方向リンクリストを操作するマクロです。リストを辿るループ処理を構成する list_for_each_entry() などの巧妙なマクロが定義されています。
機能単位 | 主なヘッダやソース | 備考 |
リスト操作 | linux/list.h | リンクリストメンバーからそれを保持する構造体へ変換するマクロ list_entry() は container_of() と同じです。リスト操作に関連するならば list_entry() を使用して下さい。 |
整数とビットに対するアトミック操作の機能群です。Tasklet, Work Queue などを含み、複数のスレッドや割り込みコンテキストと共有する変数、お互いの同期を取るための状態変数を操作するために使用します。ソースコード中で a++ や a |= FLAG_MASK といった式を書いたときに 変数 a は複数スレッドや割り込みコンテキストから参照・更新されるのか常に気にする必要が有ります。もし、参照・更新されるのなら spin_lock やアトミック操作が必要である可能性が高いです。
機能単位 | 主なヘッダやソース | 備考 |
整数 | linux/atomic.h asm/atomic.h | asm/atomic.h はプロセッサ毎に定義されています。asm/atomic.h は linux/atomic.h で include されます。 |
ビット | asm/bitops.h | asm/bitops.h はプロセッサ毎に定義されています。ヘッダファイルに atomic かどうか書かれています。atomic な関数・マクロは set_bit(), clear_bit(), clear_bit_unlock(), change_bit(), test_and_set_bit(), test_and_set_bit_lock(), test_and_clear_bit(), test_and_change_bit(), test_bit() atomic 関数と混在可 です。asm/bitops.h は linux/bitops.h で include されます。 |
バリア指示 barrier() とバリア同期 mb(), rmb(), wmb() の関数またはマクロ定義です。バリア指示はコンパイラに対して、barrier() を超えてメモリアクセス順を変更しないようにします。バリア同期は同期関数の前までに行ったメモリ操作 mb(), メモリ読みだし rmb(), メモリ書き込み wmb() を済ませるよう指示します。インラインアセンブラなどによってロート・ストアキューまたはキャッシュに保留されたトランザクションをメモリに反映します。Documentation/memory-barriers.txt を参照して下さい。
機能単位 | 主なヘッダやソース | 備考 |
バリア | linux/compiler.h asm/barrier.h | linux/compiler.h は linux/kernel.h より include されます。asm/barrier.h は linux/atomic.h から include されます。 |
複数の処理を一貫して行いたい場合に使う関数とマクロ群です。Spin Lock は複数のバリエーションがあり、割り込みコンテキストと共に使える機能もあります。ロックを獲得できるまで CPU が Spin (空走ループ)します。短時間で済む処理をロックして行うのに適しています。空走時間が長くなる場合 CPU の処理能力を無駄に消費します。
Semaphore, Mutex は呼び出したプロセスやスレッドなどを中断して他に実行を譲れる状況で使用します。割り込みコンテキストの中から使うことは出来ません。ロックを獲得できないならば、他に実行を譲ります。
機能単位 | 主なヘッダやソース | 備考 |
Spin Lock | linux/spinlock.h | 使い分けのドキュメントは Documentation/locking/spinlocks.txt, Documentation/DocBook/kernel-locking/index.html |
Semaphore | linux/semaphore.h | |
Mutex | linux/mutex.h | Documentation/locking/mutex-design.txt, Documentation/locking/rt-mutex-design.txt にドキュメントがあります。 |
条件が成立するまで待つ機能、完了を待つ機能が Kernel にあります。別スレッドや work queue に処理を任せたあと完了を待ったり、割り込みが発生を条件に処理を進める場合に使用します。
機能単位 | 主なヘッダやソース | 備考 |
Condition | linux/wait.h | ドキュメントは Wait Queue です。説明の中に Wait Queue を初期化する関数 init_waitqueue_head() の説明がありません。これはソースコードを見て用法を理解して下さい。初期化が済んだ後の典型的な流れは wait_event_interruptible() で条件待ちをし、条件成立をさせたならば wake_up() で起床させます。wait_event_*, wake_up_* とも変化型が多くあります目的に合う関数またはマクロを選んで下さい。 |
Completion | linux/completion.h | ドキュメントは Documentation/scheduler/completion.txt です。古いドライバでは linux/semaphore.h にある down(), up() を使って完了待ち合わせを実現しています。今はこのような実装は書き直されているはずです。 |
割り込みハンドラ登録・設定
機能単位 | 主なヘッダやソース | 備考 |
IRQ class | linux/interrupt.h |
時間待ち、タイマー
機能単位 | 主なヘッダやソース | 備考 |
Sleep | linux/delay.h | sleep の代表的な関数は msleep() です。実行中のタスクを再スケジュールします。CPU 資源を無駄にしません。割り込み処理中では使えません。 単に別の task に実行を渡す(いわゆる 0 秒待ちをする)場合は schedule() を使用します。 |
Delay | linux/delay.h | delay 関数は ndelay(), udelay(), mdelay() があります。これらは CPU を空走させて時間待ちを実現します。割り込みハンドラの中でも使えます。しかし、割り込み応答性能を悪化させます。 |
Timer | linux/timer.h linux/hrtimer.h |
時刻
機能単位 | 主なヘッダやソース | 備考 |
jiffies | linux/jiffies.h | jiffies の時間的前後関係を判定する場合は、linux/jiffies.h に定義された比較関数・マクロを使用して下さい。 |
Time | linux/time.h linux/timekeeping.h linux/sched.h | linux/sched.h の中に local_clock(), sched_clock() などの時刻関数が入っています。 |
メモリ確保
機能単位 | 主なヘッダやソース | 備考 |
任意サイズ | linux/slab.h | |
ページ単位 | linux/gfp.h linux/mm.h | ページ確保: linux/gfp.h:alloc_page() 、ページ・アドレス取得: linux/mm.h:page_address() となっています。 |
仮想-物理アドレス・マップ、DMA 転送、コピー
機能単位 | 主なヘッダやソース | 備考 |
User - Kernel Copy | asm/uaccess.h linux/uaccess.h | asm/uaccess.h は linux/uaccess.h で include されます。copy_to_user(), copy_from_user() などはアーキテクチャに最適な実装がされることがあります。 |
Virt - Phy map convert | asm/io.h linux/io.h mach/hardware.h | asm/io.h は linux/io.h で include されます。virt_to_phys(),phys_to_virt() などはアーキテクチャやプラットホームに最適な実装がされることがあります。一部のプラットホームでは周辺 IP block の仮想アドレス割り付けを単純化していて、軽量な実装の IO_ADDRESS(), IOMEM() マクロなどを経由して物理アドレスから仮想アドレスへ変換出来るよう便宜が図られています。 |
DMA mapping, Address convert, Scatter Gather | asm/dma-mapping.h asm-generic/dma-mapping-common.h | asm/dma-mapping.h, asm-generic/dma-mapping-common.h は linux/dma-mapping.h で include されます。 アーキテクチャ、プラットホームによっては DMA アドレスと物理アドレスが違っています。phys_to_dma(), dma_to_phys() 関数が用意されています。 |
メモリ・マップド・デバイス・アクセス
機能単位 | 主なヘッダやソース | 備考 |
Read-Write | asm/io.h | asm/io.h は linux/io.h で include されます。readb(), writeb(), readw(), writew(), readl(), writel() などメモリにマップされたデバイスをアクセスするための関数がアーキテクチャやプラットホームに最適化されています。 |
スレッド・軽量処理
機能単位 | 主なヘッダやソース | 備考 |
kthread | linux/kthread.h | |
work queue | linux/workqueue.h | |
tasklet | linux/interrupt.h | ヘッダファイルは linux/interrupt.h です。この中の tasklet_init() をはじめとする関数群です。 |
接続切断通知
機能単位 | 主なヘッダやソース | 備考 |
UEvent | linux/kobject.h | kobject_uevent(), kobject_uevent_env() 関数で kobject (ユーザー・ランド視点で /sys 以下のノード) から uevent を発行します。 |
External Connector (Switch) | linux/extcon.h | Android で switch と呼ばれていた抽象化デバイスです。Android も extcon に移行しています。コネクタの接続切断、キーボードとして扱わないボタン押しなどを検出して通知します。 |
リファレンスカウンタ
機能単位 | 主なヘッダやソース | 備考 |
kref | linux/kref.h | Reference Counter を構成するライブラリです。生成したインスタンスの生存期間を参照の有無によって決定する場合に使います。kref_init(), kref_get(), kref_put() が代表的関数です。 |
Kernel Object
機能単位 | 主なヘッダやソース | 備考 |
KObject, Kset | linux/kobject.h | kobject(kernel object) のインスタンスを直接生成する機会は殆ど無いと思います。kobject を利用する場面は、kobject を参照するポインタを使って /sys (sysfs) の下にあるデバイスやドライバのノードに特殊なノードを作ったり、uevent を発行するのが主な場面になるでしょう。 |
ノード形成
機能単位 | 主なヘッダやソース | 備考 |
VFS(character) | linux/fs.h linux/cdev.h | alloc_chrdev_region(), register_chrdev(), cdev_alloc(), cdev_init(), cdev_add() 辺りから使用例を探ると良いでしょう。 |
VFS(block) | linux/fs.h linux/genhd.h | register_blkdev(), blk_register_region(), alloc_disk(), add_disk() 辺りから使用例を探ると良いでしょう。 |
SCSI | scsi/scsi.h scsi/scsi_host.h scsi/scsi_driver.h scsi/scsi_device.h | block device として登録するならば SCSI device として登録した方が良い場合もあります。SCSI 周りは記述が多くなるので別記検討中です。 |
procfs | linux/proc_fs.h | |
sysfs | linux/sysfs.h linux/device.h linux/moduleparam.h | DEVICE_ATTR() は linux/device.h にあります。module_param() は linux/moduleparam.h にあります。 |
debugfs | linux/debugfs.h |
モジュールロード、アンロード、シンボル解決
機能単位 | 主なヘッダやソース | 備考 |
Module insmod rmmod | kernel/module.c | System Call として関数は実装されています。Kernel 内部から呼び出すには symbol を リンクできるように修正する必要が有ります。 |
Symbol lookup | linux/kallsyms.h linux/license.h linux/module.h | シンボルのアドレスを取得する symbol_get() と symbol_put() は linux/module.h に有ります。シンボルの解決を使うと static link された Kernel からモジュールの関数を呼び出すことができるようになります。ただし、GPL の抜け穴になるような作り込みは慎みましょう。 |
基本的なドライバ
機能単位 | 主なヘッダやソース | 備考 |
Null, Zero, Full, Mem drivers | drivers/char/mem.c | /dev/null, /dev/zero, /dev/full, /dev/mem の実装です。単純なキャラクタ型デバイスを実装したドライバです。単純ながら興味深い実装になっています。 |
基本的なファイルシステムノード
機能単位 | 主なヘッダやソース | 備考 |
pipe | fs/pipe.c | poll(select)の実装、ioctl FIONREAD の実装 は単純な挙動で特別なデバイスも無い環境でも試せるので参考になるでしょう。 |
eventfd | fs/eventfd.c | eventfd, signalfd, timerfd それぞれで system call を実装し、file descriptor を API として提供しています。独自の Kernel API を必要としているならば読んでみるのも良いでしょう。 |
signalfd | fs/signalfd.c | |
timerfd | fs/timerfd.c |
デバイス登録
機能単位 | 主なヘッダやソース | 備考 |
platform | linux/platform_device.h | platform_device_register() にてプラットホーム・デバイスを登録します。プラットホームデバイスは SoC に内蔵された IP ブロックが主に対象となります。ただし、suspend、resume、接続、切断 動作に関して何らかの上流バスの支配あるいは管轄下にある場合は、そのバスのデバイスとして登録します。 |
HID | linux/hid.h | hid_allocate_device(), hid_add_device() |
I2C | linux/i2c.h | I2C 関連の基礎的な用語 Algorithm, Adapter, Driver, Client については Documentation/i2c/summary にて解説されています。全般的なドキュメントは Documentation/i2c/ 以下のファイルと API の解説 Documentation/DocBook/device-drivers/i2c.html を参照してください。 多くの場合 i2c_register_board_info() を使ってプラットホームの初期化処理にてデバイスを登録します。動的な登録は i2c_new_device() で行います。 |
PCI | linux/pci.h | PCI 接続のデバイス登録は SoC メーカーあるいは開発環境を提供したメーカーがすでに初期化処理の中で行うよう実装しているはずです。pci_common_init(), pcibios_scan_root(), pci_bus_add_devices() 辺りからコードを追跡して確認してください。それでも意図して登録する場合は pci_bus_add_device() の用例を参考にしてください。 |
SDIO | linux/mmc/host.h | SDIO デバイスは自動でデバイスが登録されます。登録の流れは mmc_detect_change(), (delayed work を挟み) mmc_rescan(), mmc_rescan_try_freq(), mmc_attach_sdio(), mmc_attach_sd(), mmc_attach_mmc(), mmc_add_card() の様になります。 |
SPI | linux/spi/spi.h | spi_register_board_info() を使ってプラットホームの初期化処理にてデバイスを登録します。動的な登録は spi_alloc_device(), spi_add_device() で行います。 |
USB | linux/usb.h linux/usb/hcd.h | 接続処理(デバイス登録)は自動で行われます。usb_new_device(), hub_port_connect(), usb_hub_create_port_device() とその呼び出し元のコードを探索してみてください。 |
ドライバ登録
機能単位 | 主なヘッダやソース | 備考 |
platform | linux/platform_device.h | platform_driver_register(), module_platform_driver() |
I2C | linux/i2c.h | i2c_add_driver(), module_i2c_driver() |
PCI | linux/pci.h | pci_register_driver(), module_pci_driver() |
SDIO | linux/mmc/mmc.h linux/mmc/core.h linux/mmc/card.h linux/mmc/sdio.h linux/mmc/sdio_ids.h linux/mmc/sdio_func.h | ドライバ登録は sdio_register_driver() で行います。左の列に挙げたヘッダファイルは一部だけ使用する場合もあります。実装する機能と照らし合わせ include/linux/mmc の下のヘッダファイル群を取り込んで下さい。 |
SPI | linux/spi/spi.h | spi_register_driver(), module_spi_driver() |
USB | linux/usb.h | usb_register(), module_usb_driver() |
クラスデバイス登録
機能単位 | 主なヘッダやソース | 備考 |
Input, HID | linux/input.h linux/hid.h | 組み込みでは input_allocate_device(), input_register_device() のほうをよく使うと思います。Documentation/DocBook/device-drivers/input_subsystem.html を参照してください。関数名はデバイス登録を思わせる名前です。実質はドライバ登録に当たります。同じドライバソースの中にキー入力を報告(伝達)する関数 input_report_key(), input_sync() が見つかると思います。 HID 系の API は hid_allocate_device(), hid_add_device() などです。Documentation/hid/hid-transport.txt を参照してください。 hid_register_driver(), module_hid_driver() |