Модули ядра Linux
Проектирование цифровых вычислительных систем
Прутьянов В. В.
Ядро Linux
Модули ядра Linux
Сборка модуля
Пример out-of-tree модуля
#include <linux/init.h> // For __init, __exit
#include <linux/module.h> // For any module
#include <linux/kernel.h> // For printing
MODULE_LICENSE("GPL");
MODULE_VERSION("1.0");
static int __init hello_init(void) // Entry point
{
pr_info(KBUILD_MODNAME ": ""Hello World!\n");
return 0; // Success
}
static void __exit hello_exit(void)
{
pr_info(KBUILD_MODNAME ": ""Goodbye World!\n");
}
module_init(hello_init);
module_exit(hello_exit);
obj-m := hello.o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
Makefile
hello.c
$ make
$ sudo insmod hello.ko
$ sudo rmmod hello.ko
$ dmesg
[173917.672037] hello: Hello World!
[173918.903805] hello: Goodbye World!
Kernel vs. User
Изоляция пространств пользователя и ядра
В целях безопасности, ядро и пользовательский код работают на разных уровнях привилегий
System Call Interface
Программы взаимодействуют с ядром через интерфейс системных вызовов (system call interface), предоставляющий контролируемый доступ к аппаратным ресурсам и привилегированным операциям.
Способы обмена информацией с пространством ядра
Файл устройства в /dev
Miscellaneous Device
Изоляция пространств пользователя и ядра
Пример: символьное устройство в /dev
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
static const char msg[] = "Hello from device!\n";
static ssize_t hello_read(struct file *filp,
char __user *buf, size_t count, loff_t *pos)
{
if (*pos > 0 || count < sizeof(msg)-1)
return 0;
if (copy_to_user(buf, msg, sizeof(msg)-1))
return -EFAULT;
return sizeof(msg)-1;
}
static int hello_open(struct inode *inode, struct file *filp)
{
pr_info(KBUILD_MODNAME ": ""device open\n");
return 0;
}
static int hello_release(struct inode *inode, struct file *filp)
{
pr_info(KBUILD_MODNAME ": ""device close\n");
return 0;
}
static const struct file_operations hello_fops = {
.owner = THIS_MODULE,
.read = hello_read,
.open = hello_open,
.release = hello_release,
};
static struct miscdevice hello_miscdev = {
.minor = MISC_DYNAMIC_MINOR, // Use free number
.name = KBUILD_MODNAME, // Device name
.fops = &hello_fops, // File operations
.mode = 0444, // Read only
};
static int __init hello_init(void)
{
return misc_register(&hello_miscdev);
}
static void __exit hello_exit(void)
{
misc_deregister(&hello_miscdev);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
Пример: символьное устройство в /dev
$ sudo insmod hello.ko
$ file /dev/hello
/dev/hello: character special (10/123)
$ head -n3 /dev/hello
Hello from device!
Hello from device!
Hello from device!
$ dmesg
....................
[186671.007614] hello: device open
[186671.007657] hello: device close
$ sudo rmmod hello
$ file /dev/hello
/dev/hello: cannot open `/dev/hello' (No such file or directory)
Структура file_operations
struct file_operations {
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
__poll_t (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*fasync) (int, struct file *, int);
int (*flock) (struct file *, int, struct file_lock *);
long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len);
int (*fadvise)(struct file *, loff_t, loff_t, int);
int (*uring_cmd)(struct io_uring_cmd *ioucmd, unsigned int issue_flags);
int (*uring_cmd_iopoll)(struct io_uring_cmd *, struct io_comp_batch *, unsigned int poll_flags);
.........................
}
ioctl
long (*unlocked_ioctl) (struct file *filp, unsigned int cmd, unsigned long arg);
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,argtype) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(argtype)))
#define _IOW(type,nr,argtype) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(argtype)))
#define _IOWR(type,nr,argtype) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(argtype)))
Пример: ioctl
#include "cmds.h"
static atomic_t num = {0};
static long hello_ioctl(struct file *filp,
unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case INC_NUM:
atomic_inc(&num);
return 0;
case GET_NUM:
return put_user(atomic_read(&num),
(int __user *)arg);
default:
return -ENOTTY;
}
}
static struct file_operations hello_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = hello_ioctl,
};
#include "cmds.h"
int main() {
int old = -1, new = -1;
int fd = open("/dev/hello", O_RDWR);
ioctl(fd, GET_NUM, &old);
ioctl(fd, INC_NUM);
ioctl(fd, GET_NUM, &new);
printf("%d -> %d\n", old, new);
return 0;
}
#define INC_NUM _IO('k', 0x40)
#define GET_NUM _IOR('k', 0x41, int)
$ sudo ./test
0 -> 1
$ sudo ./test
1 -> 2
hello.c
test.c
cmds.h
Управление памятью
Управление памятью
Управление памятью
Логические адреса
Источник изображения: elinux.org/images/7/77/Intro-to-memory-management.pdf
Задание
Литература