著者:平田 豊
LinuxというOSの中核となるのが「Linuxカーネル」というソフトウエアです。Linuxカーネルの動作を理解するには、ソースコードを追いかける必要があります。しかし、いきなりソースコードを読んでもよく分からないものです。本特集記事では、学習の「入り口」となるように、学習用の環境を構築する方法を紹介します。また、Linuxカーネルの一部の機能をピックアップして、その実装について解説します。本特集をきっかけにLinuxカーネルのソースコード読解を始めましょう。
シェルスクリプトマガジン Vol.76は以下のリンク先でご購入できます。![]()
![]()
図1 OS レスプログラムのイメージ
for (;;) {
// スイッチが押された
if (isSwitchOn()) {
// 要因クリア
ClearSwitch();
// LED点灯
TurnOnLED();
}
// スイッチが離された
if (isSwitchOff()) {
// 要因クリア
ClearSwitch();
// LED点灯
TurnOffLED();
}
// ディレー
delay();
}
図7 QEMU の起動用シェルスクリプト
#!/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 カーネルに追加するコード
(略)
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 デーモンを起動する箇所のソースコード
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 関数の実装
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 システムコールに対応する関数を呼び出すためのマクロ定義の例
#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() 関数のコード
void machine_halt(void)
{
local_irq_disable();
smp_send_stop();
while (1);
}
図15 machine_power_off() 関数のコード
void machine_power_off(void)
{
local_irq_disable();
smp_send_stop();
if (pm_power_off)
pm_power_off();
}
図17 schedule() 関数のコード
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 からのデータを出力するプログラムのコード
#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() 関数のマクロ定義
#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() 関数のコード
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() 関数のコード
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() 関数のコード
void __sched mutex_lock(struct mutex *lock)
{
might_sleep();
if (!__mutex_trylock_fast(lock))
__mutex_lock_slowpath(lock);
}
図23 preempt_enable() 関数のマクロ定義
#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 変数の宣言
extern u64 jiffies_64;
extern unsigned long volatile jiffies;
図25 jiffies_64 変数の実体の定義
u64 jiffies_64 = INITIAL_JIFFIES;
図26 INITIAL_JIFFIES 定数の定義
#define INITIAL_JIFFIES ((unsigned long)(unsigned int) (-300*HZ))