实验四
题目
实验四
名称:Linux内核模块
实验内容:
-
学习Linux内核模块的基本概念,掌握开发一个简单的内核模块的基本方法
-
在内核模块中添加遍历task_struct链表的代码,输出当前运行的进程的ID序列
解析
关于Linux内核模块
Linux Kernel Module(LKM),即Linux内核模块。
Linux内核是宏内核,简单来说就是内核就是一个整体,可以理解成为一个可执行程序,内核包揽了所有的系统功能,比如,进程调度、内核管理、文件系统、设备管理等等。这样势必出现一个问题,那就是如果开启所有的功能,会使得内核变得十分的臃肿。而LKM,可以从一定程度上,解决这个问题。通过将内核功能模块实现为LKM,可以对内核进行精简,只保留最为核心的功能,然后,其他功能模块,可以随着后续需求,进行动态的加载。Linux内核通过LKM可以动态的管理内核功能,使内核功能就要U盘一样,支持热插拔,这样做不仅可以节约内核运行时的硬件要求,同时可以满足内核功能的实时扩展性。
————————————————(以上话来源如下)
版权声明:本文为CSDN博主「奔跑的码仔」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lhl_blog/article/details/105904368
说白了就是:Linux包含那么多功能没啥用,内核只管最核心的,剩下的按需加载。有点创意工坊那味了
编译内核模块 - 前置学习
由于内核模块是Linux规定的,所以我们开发要按照Linux开发的规则。
由于我们此处只是简单的学习一下,所以我们丢出一个Hello World示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <linux/init.h> #include <linux/module.h>
static int __init module_init_func(void) { printk("hello, world.\n"); return 0; }
static void __exit module_exit_func(void) { return; }
MODULE_LICENSE("GPL v2"); MODULE_VERSION("v0.1"); MODULE_AUTHOR("lhl"); MODULE_DESCRIPTION("LKM, helloworld.");
module_init(module_init_func); module_exit(module_exit_func);
|
其配套一个简单的Makefile:
1 2 3 4 5 6 7 8 9
| ifneq ($(KERNELRELEASE),) obj-m :=改这里的名称.o else KDIR :=/lib/modules/$(shell uname -r)/build all: make -C $(KDIR) M=$(PWD) modules clean: rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order endif
|
这个makefile的主要作用就是将模块编译到内核里去。
基本含义如下:
obj-m:用于定义内核模块的二进制名称,注意,该文件名称与内核模块的c程序文件名进行对应。
KERS:用于声明内核编译环境的位置。
all:用于定义目标文件的编译规则,make -C ( K E R S ) 表 示 进 入 内 核 编 译 环 境 , M = (KERS)表示进入内核编译环境,M=(KERS)表示进入内核编译环境,M=(shell pwd)指定当前LKM的路径,modules表示进行LKM的编译。
clean:与all目标类似,这里用于定义清理LKM编译过程中产生的文件,包括.ko文件。
————————————————(来源同样是这位大佬)
版权声明:本文为CSDN博主「奔跑的码仔」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lhl_blog/article/details/105904368
编译内核模块 - 输出ID序列
首先我们要知道:
Linux内核的进程控制块PCB是一个task_struct结构体。所有运行在系统中的进程都以task_struct链表的形式,存放在内核中。
关于结构体内的定义,请参阅本文末。其过长,不予完全展示。
我们需要获取这个结构体内的pid并输出。
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
| #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h>
static int hello_init(void) { struct task_struct *p; printk(KERN_ALERT"进程号\t"); for_each_process(p) { printk(KERN_ALERT"%d\n",p->pid); } return 0; }
static void hello_exit(void) { printk(KERN_ALERT"goodbye!\n"); }
module_init(hello_init); module_exit(hello_exit);
MODULE_LICENSE("GPL");
|
准备好上述代码后,进入虚拟机,进入你做完实验三的内核(否则无法编译)。
之后新建文件夹:
1 2
| mkdir mingzisuibianqi cd mingzisuibianqi
|
(文件夹名随便起,只是怕冲突了而已)
之后随便创建一个c语言文件:
在里面写入如下代码:
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
| #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h>
static int hello_init(void) { struct task_struct *p; printk(KERN_ALERT"进程号\t"); for_each_process(p) { printk(KERN_ALERT"%d\n",p->pid); } return 0; }
static void hello_exit(void) { printk(KERN_ALERT"goodbye!\n"); }
module_init(hello_init); module_exit(hello_exit);
MODULE_LICENSE("GPL");
|
进入文件夹后,创建Makefile文件:
在里面输入
1 2 3 4 5 6 7 8 9
| ifneq ($(KERNELRELEASE),) obj-m :=对应上面的C语言名字.o else KDIR :=/lib/modules/$(shell uname -r)/build all: make -C $(KDIR) M=$(PWD) modules clean: rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order endif
|
之后输入make编译。
内核模块的编译分为两种:单独编译和嵌入到内核代码中编译。一般情况下,LKM的编译选择单独编译,这种编译方式有两种优势:
- 编译速度快;
- 便于代码的维护。
- 单独编译需要提前构建内核编译环境,构建方式有两种:
下载内核源码,编译完成后,执行make modules_install安装。
如果是Ubuntu系统,可以使用sudo apt-get install linux-headers-$(uname -r)安装适配于当前内核的构建环境。
(然后注意,上面的我们在实验三已经做过了)
编译完成后,在文件夹内输入如下代码测试:
1 2
| sudo insmod 对应上面的C语言名字.ko dmesg
|
移除命令:
如图所示。
附录:task_struct的结构体定义
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
| task_struct
Struct task_struct
{
1、支持对称多处理器方式(SMP)时的数据成员
(1)int processor;
(2)int last_processor;
(3)int lock_depth;
2、其他数据成员
(1)unsigned short used_math;
(2)char comm[16];
(3)struct rlimit rlim[RLIM_NLIMITS];
(4)int errno;
(5)long debugreg[8];
(6)struct exec_domain *exec_domain;
(7)unsigned long personality;
(8)struct linux_binfmt *binfmt;
(9)int exit_code,exit_signal;
(10)int dumpable:1;
(11)int did_exec:1;
(12) int tty_old_pgrp;
(13) struct tty_struct *tty;
(14) struct wait_queue *wait_chldexit;
3、调度数据成员
(1) volatile long states;
ASK_RUNNING:正在运行或在就绪队列run-queue中准备运行的进程,实际参与进程调度。
TASK_INTERRUPTIBLE:处于等待队列中的进程,待资源有效时唤醒,也可由其它进程通过信号(signal)或定时中断唤醒后进入就绪队列run-queue。
TASK_UNINTERRUPTIBLE:处于等待队列中的进程,待资源有效时唤醒,不可由其它进程通过信号(signal)或定时中断唤醒。
TASK_ZOMBIE:表示进程结束但尚未消亡的一种状态(僵死状态)。此时,进程已经结束运行且释放大部分资源,但尚未释放进程控制块。
TASK_STOPPED:进程被暂停,通过其它进程的信号才能唤醒。导致这种状态的原因有二,或者是对收到SIGSTOP、SIGSTP、SIGTTIN或SIGTTOU信号的反应,或者是受其它进程的ptrace系统调用的控制而暂时将CPU交给控制进程。
TASK_SWAPPING: 进程页面被交换出内存的进程。
(2) unsigned long flags;
PF_ALIGNWARN 打印“对齐”警告信息。
PF_PTRACED 被ptrace系统调用监控。
PF_TRACESYS 正在跟踪。
PF_FORKNOEXEC 进程刚创建,但还没执行。
PF_SUPERPRIV 超级用户特权。
PF_DUMPCORE dumped core。
PF_SIGNALED 进程被信号(signal)杀出。
PF_STARTING 进程正被创建。
PF_EXITING 进程开始关闭。
PF_USEDFPU 该进程使用FPU(SMP only)。
PF_DTRACE delayed trace (used on m68k)。
(3)long priority;
(4)unsigned long rt_priority;
(5)long counter;
(6)unsigned long policy;
SCHED_OTHER 0 非实时进程,基于优先权的轮转法(round robin)。 SCHED_FIFO 1 实时进程,用先进先出算法。 SCHED_RR 2 实时进程,用基于优先权的轮转法。 4、信号处理 (1) unsigned long signal; (2) unsigned long blocked; (3) struct signal_struct *sig; 5、进程队列指针 (1)struct task_struct *next_task,*prev_task; (2)struct task_struct *next_run,*prev_run;
(3)struct task_struct *p_opptr,*p_pptr;和struct task_struct *p_cptr,*p_ysptr,*p_osptr;
6、进程标识
(1) unsigned short uid,gid;
(2) int groups[NGROUPS];
(3) unsigned short euid,egid;
(4) unsigned short fsuid,fsgid;
(5) unsigned short suid,sgid;
(6) int pid,pgrp,session;
(7) int leader;
7、 时间数据成员
(1) unsigned long timeout;
(2) unsigned long it_real_value,it_real_iner;
(3) struct timer_list real_timer;
(4) unsigned long it_virt_value,it_virt_incr;
(5) unsigned long it_prof_value,it_prof_incr;
(6) long utime,stime,cutime,cstime,start_time;
8、 信号量数据成员
(1) struct sem_undo *semundo;
(2) struct sem_queue *semsleeping;
9、 进程上下文环境
(1) struct desc_struct *ldt;
(2) struct thread_struct tss;
(3) unsigned long saved_kernel_stack;
(4) unsigned long kernel_stack_page;
10、 文件系统数据成员
(1) struct fs_struct *fs;
struct files_struct *files;
(3) int link_count;
11、 内存数据成员
(1) struct mm_struct *mm;
12、页面管理
(1) int swappable:1;
(2) unsigned long swap_address;
(3)unsigned long min_flt,maj_flt;
(4) unsigned long nswap;
(5) unsigned long cmin_flt,cmaj_flt,cnswap;
(6) unsigned long old_maj_flt,dec_flt;
(7) unsigned long swap_cnt;
13. 进程队列的全局变量
(1) current;
(2) struct task_struct init_task;
(3) struct task_struct *task[NR_TASKS];
(4) unsigned long volatile jiffies;
(5) int need_resched;
(6) unsigned long intr_count; ———————————————— 版权声明:本文为CSDN博主「IT小书生」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https:
|