LDD3 WEEK0

这周忙于人工智能课程的作业,书本看的不多,下周多看一些多实践一些。

1.简介

主要介绍了驱动程序的角色,内核划分,以及 Linux 版本号、社区的一些基本信息。

2.建立和运行模块

hello world

obj-m += hello_world.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
#include<linux/module.h>
#include<linux/kernel.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("RogerThat");
MODULE_DESCRIPTION("A simple hello world module");
MODULE_VERSION("0.01");

static int __init hello_mod_init(void)
{
        printk(KERN_ALERT "Hello world from kernel!! \n");
        return 0;
}

static void __exit hello_mod_exit(void)
{
        printk(KERN_ALERT "Exiting hello world module from kernel !!!\n");
}

module_init(hello_mod_init);
module_exit(hello_mod_exit);

output

rt@rogerthat ~/kernel> make
make -C /lib/modules/5.19.0-46-generic/build M=/home/rt/kernel modules
make[1]: Entering directory '/usr/src/linux-headers-5.19.0-46-generic'
warning: the compiler differs from the one used to build the kernel
  The kernel was built by: x86_64-linux-gnu-gcc-12 (Ubuntu 12.2.0-3ubuntu1) 12.2.0
  You are using:           gcc-12 (Ubuntu 12.3.0-1ubuntu1~22.04) 12.3.0
  CC [M]  /home/rt/kernel/hello_world.o
  MODPOST /home/rt/kernel/Module.symvers
  CC [M]  /home/rt/kernel/hello_world.mod.o
  LD [M]  /home/rt/kernel/hello_world.ko
  BTF [M] /home/rt/kernel/hello_world.ko
Skipping BTF generation for /home/rt/kernel/hello_world.ko due to unavailability of vmlinux
make[1]: Leaving directory '/usr/src/linux-headers-5.19.0-46-generic'
rt@rogerthat ~/kernel [1]> sudo insmod hello_world.ko
rt@rogerthat ~/kernel> sudo lsmod | grep "hello"
hello_world            16384  0
rt@rogerthat ~/kernel [1]> sudo dmesg
[  489.143299] hello_world: loading out-of-tree module taints kernel.
[  489.143332] hello_world: module verification failed: signature and/or required key missing - tainting kernel
[  489.144447] Hello world from kernel!!
rt@rogerthat ~/kernel [1]> sudo rmmod hello_world

也可以用 modprob ,处理依赖。

内核符号表,这个比较新奇,之前对这个的了解比较少。

感觉可以多用一下 goto 了(,新版的 linux(6.x) 内核有不少 rust 的代码,但是怎么这么多函数都是 unsafe 的,这算一种画蛇添足吗=。=。

我的 WSL 内核版本是 5.15.90.4,在 WSL 上做这些事情是不是会有趣一些

3.字符驱动

讲字符驱动,顺便翻了一下 Linux 2.1 fs.h 中关于文件的一些数据结构

struct file {
	struct file		*f_next, **f_pprev;
	struct dentry		*f_dentry;
	struct file_operations	*f_op;
	mode_t			f_mode;
	loff_t			f_pos;
	unsigned short 		f_count, f_flags;
	unsigned long 		f_reada, f_ramax, f_raend, f_ralen, f_rawin;
	struct fown_struct	f_owner;

	unsigned long		f_version;

	/* needed for tty driver, and maybe others */
	void			*private_data;
};

可以看到第三个结构体 file_operations 也就是书中讲的。

而在 Linux 6.x 中变成了

989  struct file {
990  	union {
991  		/* fput() uses task work when closing and freeing file (default). */
992  		struct callback_head 	f_task_work;
993  		/* fput() must use workqueue (most kernel threads). */
994  		struct llist_node	f_llist;
995  		unsigned int 		f_iocb_flags;
996  	};
997  
998  	/*
999  	 * Protects f_ep, f_flags.
1000  	 * Must not be taken from IRQ context.
1001  	 */
1002  	spinlock_t		f_lock;
1003  	fmode_t			f_mode;
1004  	atomic_long_t		f_count;
1005  	struct mutex		f_pos_lock;
1006  	loff_t			f_pos;
1007  	unsigned int		f_flags;
1008  	struct fown_struct	f_owner;
1009  	const struct cred	*f_cred;
1010  	struct file_ra_state	f_ra;
1011  	struct path		f_path;
1012  	struct inode		*f_inode;	/* cached value */
1013  	const struct file_operations	*f_op;
1014  
1015  	u64			f_version;
1016  #ifdef CONFIG_SECURITY
1017  	void			*f_security;
1018  #endif
1019  	/* needed for tty driver, and maybe others */
1020  	void			*private_data;
1021  
1022  #ifdef CONFIG_EPOLL
1023  	/* Used by fs/eventpoll.c to link all the hooks to this file */
1024  	struct hlist_head	*f_ep;
1025  #endif /* #ifdef CONFIG_EPOLL */
1026  	struct address_space	*f_mapping;
1027  	errseq_t		f_wb_err;
1028  	errseq_t		f_sb_err; /* for syncfs */
1029  } __randomize_layout
1030    __attribute__((aligned(4)));	/* lest something weird decides that 2 is OK */

结构有一些变化,2.x 直接用双向链表连起了 struct file,而 6.xf_llist 是一个单向链表?多了 epoll,security 等选项。具体的一些细节回头再看看。

struct file_operations {
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
	int (*readdir) (struct file *, void *, filldir_t);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, struct dentry *);
	int (*fasync) (int, struct file *, int);
	int (*check_media_change) (kdev_t dev);
	int (*revalidate) (kdev_t dev);
	int (*lock) (struct file *, int, struct file_lock *);
};

似乎是几个 CRT 中比较常用的函数,实际上这些操作大部分实现系统调用。struct file 中包含了 f_op 方法,一种面向对象思想。

没找到 aio_read,aio_write 异步读写,可能是没支持?回头再看看。

poll 是三个系统调用的后端 poll,epollselect

ioctl 设备发出特定命令,readv, writev 发散/汇聚读和写操作?回头再看看

struct inodei_rdev,i_cdev

unsigned long copy_to_user(void __user *to,const void *from,unsigned long count);

skull

还没试过这部分代码,接着看看