著者:平田 豊
LinuxというOSの中核となるのが「Linuxカーネル」というソフトウエアです。Linuxカーネルの動作を理解するには、ソースコードを追いかける必要があります。しかし、いきなりソースコードを読んでもよく分からないものです。本特集記事では、学習の「入り口」となるように、学習用の環境を構築する方法を紹介します。また、Linuxカーネルの一部の機能をピックアップして、その実装について解説します。本特集をきっかけにLinuxカーネルのソースコード読解を始めましょう。
シェルスクリプトマガジン Vol.76は以下のリンク先でご購入できます。
図1 OS レスプログラムのイメージ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
for (;;) { // スイッチが押された if (isSwitchOn()) { // 要因クリア ClearSwitch(); // LED点灯 TurnOnLED(); } // スイッチが離された if (isSwitchOff()) { // 要因クリア ClearSwitch(); // LED点灯 TurnOffLED(); } // ディレー delay(); } |
図7 QEMU の起動用シェルスクリプト
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#!/bin/sh BUILD_PATH="${HOME}/buildroot-2021.08.1" IMAGE_DIR="${BUILD_PATH}/output/images" exec qemu-system-aarch64 \ -M virt \ -cpu cortex-a53 \ -nographic \ -smp 2 \ -m 512 \ -kernel ${IMAGE_DIR}/Image \ -append "rootwait root=/dev/vda console=ttyAMA0" \ -drive file=${IMAGE_DIR}/rootfs.ext4,if=none,format=raw,id=hd0 \ -device virtio-blk-device,drive=hd0 \ -netdev user,id=eth0 \ -device virtio-net-device,netdev=eth0 \ -object rng-random,filename=/dev/urandom,id=rng0 \ -device virtio-rng-pci,rng=rng0 |
図8 Linux カーネルに追加するコード
1 2 3 4 5 6 7 |
(略) static int run_init_process(const char *init_filename) { const char *const *p; printk("hoge 0x%016lx (%zu)\n", jiffies, sizeof(jiffies)); argv_init[0] = init_filename; (略) |
図11 init デーモンを起動する箇所のソースコード
1 2 3 4 5 6 7 8 |
if (!try_to_run_init_process("/sbin/init") || !try_to_run_init_process("/etc/init") || !try_to_run_init_process("/bin/init") || !try_to_run_init_process("/bin/sh")) return 0; panic("No working init found. Try passing init= option to kernel. " "See Linux Documentation/admin-guide/init.rst for guidance."); |
図12 buildroot 環境のC ライブラリにおけるsyscall 関数の実装
1 2 3 4 5 6 7 8 9 10 11 12 13 |
ENTRY (syscall) uxtw x8, w0 mov x0, x1 mov x1, x2 mov x2, x3 mov x3, x4 mov x4, x5 mov x5, x6 mov x6, x7 svc 0x0 cmn x0, #4095 b.cs 1f RET |
図13 システムコールに対応する関数を呼び出すためのマクロ定義の例
1 2 3 4 5 6 7 8 |
#define __SYSCALL_DEFINEx(x, name, ...) \ (略) asmlinkage long __arm64_sys##name(const struct pt_regs *regs) \ { \ return __se_sys##name(SC_ARM64_REGS_TO_ARGS(x,__VA_ARGS__)); \ } \ (略) static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) |
図14 machine_halt() 関数のコード
1 2 3 4 5 6 |
void machine_halt(void) { local_irq_disable(); smp_send_stop(); while (1); } |
図15 machine_power_off() 関数のコード
1 2 3 4 5 6 7 |
void machine_power_off(void) { local_irq_disable(); smp_send_stop(); if (pm_power_off) pm_power_off(); } |
図17 schedule() 関数のコード
1 2 3 4 5 6 7 8 9 10 11 12 13 |
asmlinkage __visible void __sched schedule(void) { struct task_struct *tsk = current; sched_submit_work(tsk); do { preempt_disable(); __schedule(false); sched_preempt_enable_no_resched(); } while (need_resched()); sched_update_worker(tsk); } EXPORT_SYMBOL(schedule); |
図18 FIFO からのデータを出力するプログラムのコード
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 |
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main(int argc, char **argv){ int i, fd = -1; ssize_t sz; char *filename, buf[64]; if (argc == 1) { printf("Usage: %s fifo\n", argv[0]); exit(1); } filename = argv[1]; fd = open(filename, O_RDWR); if (fd == -1) { perror("open"); goto error; } sz = read(fd, buf, sizeof(buf)); if (sz == -1) { perror("read"); goto error; } for (i = 0 ; i < sz ; i++) { printf("%02x ", buf[i]); } printf("\n"); error: if (fd != -1) close(fd); } |
図19 wait_event_interruptible_exclusive() 関数のマクロ定義
1 2 3 4 5 6 7 8 9 10 11 12 |
#define __wait_event_interruptible_exclusive(wq, condition) \ ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 1, 0, \ schedule()) #define wait_event_interruptible_exclusive(wq, condition) \ ({ \ int __ret = 0; \ might_sleep(); \ if (!(condition)) \ __ret = __wait_event_interruptible_exclusive(wq, condition); \ __ret; \ }) |
図20 msleep() 関数のコード
1 2 3 4 5 6 7 |
void msleep(unsigned int msecs) { unsigned long timeout = msecs_to_jiffies(msecs) + 1; while (timeout) timeout = schedule_timeout_uninterruptible(timeout); } |
図21 process_timeout() 関数とschedule_timeout() 関数のコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
static void process_timeout(struct timer_list *t) { struct process_timer *timeout = from_timer(timeout, t, timer); wake_up_process(timeout->task); } (略) signed long __sched schedule_timeout(signed long timeout) { (略) expire = timeout + jiffies; timer.task = current; timer_setup_on_stack(&timer.timer, process_timeout, 0); __mod_timer(&timer.timer, expire, MOD_TIMER_NOTPENDING); schedule(); (略) } |
図22 mutex_lock() 関数のコード
1 2 3 4 5 6 7 |
void __sched mutex_lock(struct mutex *lock) { might_sleep(); if (!__mutex_trylock_fast(lock)) __mutex_lock_slowpath(lock); } |
図23 preempt_enable() 関数のマクロ定義
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#ifdef CONFIG_PREEMPTION #define preempt_enable() \ do { \ barrier(); \ if (unlikely(preempt_count_dec_and_test())) \ __preempt_schedule(); \ } while (0) #else /* !CONFIG_PREEMPTION */ #define preempt_enable() \ do { \ barrier(); \ preempt_count_dec(); \ } while (0) |
図24 ヘッダーファイルでのjiffies 変数の宣言
1 2 |
extern u64 jiffies_64; extern unsigned long volatile jiffies; |
図25 jiffies_64 変数の実体の定義
1 |
u64 jiffies_64 = INITIAL_JIFFIES; |
図26 INITIAL_JIFFIES 定数の定義
1 |
#define INITIAL_JIFFIES ((unsigned long)(unsigned int) (-300*HZ)) |