文章目录
一、设计思路
-
下载linux内核源码
-
在系统调用表中添加系统调用号
-
系统调用头文件中写出自定义的系统调用函数
-
修改内核源码,添加自定义系统调用函数的实现
-
安装依赖工具,重新编译内核,并使用新的内核
-
编写测试代码并测试
二、实现过程
1. 下载linux内核源码
官网下载内核源码,网址为:https://mirrors.edge.kernel.org/pub/linux/kernel/
国内源码下载地址:http://ftp.sjtu.edu.cn/sites/ftp.kernel.org/pub/linux/kernel/
查看自己系统的内核版本,然后找一个相近的linux版本进行内核升级,这里我选择的是5.14.1
下载源码包,解压至 /usr/src
mv linux-5.14.1.tar.gz /usr/src
tar xvf linux-5.14.1.tar.gz
2. 在系统调用表中添加系统调用号
系统调用表路径:/usr/src/linux-5.14.1/arch/x86/entry/syscalls/syscall_64.tbl
<number> <abi> <name> <entry point>
331 common pkey_free sys_pkey_free
332 common statx sys_statx
333 common io_pgetevents sys_io_pgetevents
334 common rseq sys_rseq
335 64 print_all_process sys_print_all_process
3. 系统调用头文件中写出自定义的系统调用函数
头文件中加入自定义系统调用声明,头文件路径:/usr/src/linux-5.14.1/arch/x86/include/asm/syscalls.h
#ifndef _ASM_X86_SYSCALLS_H
#define _ASM_X86_SYSCALLS_H
/* Common in X86_32 and X86_64 */
/* kernel/ioport.c */
long ksys_ioperm(unsigned long from, unsigned long num, int turn_on);
/* added by me */
asmlinkage long __x64_sys_print_all_process(void);
#endif /* _ASM_X86_SYSCALLS_H */
4. 修改内核源码,添加自定义系统调用函数的实现
在 /usr/src/linux-5.14.1/kernel/sys.c
中添加即可,注意不能写到宏里面
// 335 64 print_all_process sys_print_all_process
static void print_children(struct task_struct* task, int level){
struct list_head* pos;
struct task_struct* ptr;
// 树形结构输出
int i;
for(i = level; i > 0; i--){
printk(KERN_CONT " ");
}
// 进程名称以及pid
printk(KERN_CONT "|————");
printk(KERN_CONT "%s[%d]\n", task->comm, task->pid);
// 利用父task_struct的children双向链表,遍历其子进程
list_for_each(pos, &task->children){
// 返回task_struct指针,寻找pos对应的task_struct,便宜相当于sibling
ptr = list_entry(pos, struct task_struct, sibling);
if(ptr != NULL){
level++;
// 递归打印子进程树
print_children(ptr, level);
level--;
}
}
}
asmlinkage long __x64_sys_print_all_process(void){
printk("The Process Tree Is As Follows:");
print_children(&init_task, 0);
return 0;
}
5. 安装依赖工具,重新编译内核,并使用新的内核
apt update
apt install libncurses5-dev libssl-dev
apt install build-essential openssl
apt install zlibc minizip
apt install libidn11-dev libidn11
apt install flex bison
apt install libdw-dev
apt install libncurses-dev
apt install dwarves
apt-get install libdw-dev
make mrproper
make clean
make menuconfig
弹出配置菜单,Save->ok->exit->exit即可
编译内核,这个过程非常长,还会有各种bug,我使用的版本为5.14.1,至少需要60G磁盘,编译至少4小时
cd /usr/src/linux-5.14.1
编译内核:make
编译完成后在控制台中输入命令开始安装模块:sudo make modules_install
模块安装完成后在控制台中输入命令安装内核:sudo make install
安装完成后:reboot
可以看到内核版本已经更新为5.14.1
6. 编写测试代码并测试
#include <linux/unistd.h>
#include <sys/syscall.h>
int main(){
syscall(335);
return 0;
}
由于添加的系统调用的输出语句为printk写的,它将消息送到系统日志中,所以控制台没有输出,输入命令查看日志:dmesg
三、编译内核遇到的问题以及解决方法
-
编译错误 error New address family defined, please update secclass_map.解决
-
没有libelf.h libdw.h
-
编译Linux内核kernel时遇到的问题与解决方案
-
BTF: .tmp_vmlinux.btf: pahole (pahole) is not available
-
arch/x86/boot/compressed/vmlinux.bin.lzma] Error 1
四、参考资料
-
Linux内核分析
-
Ubuntu18向内核增加一个系统调用实验
-
Linux添加系统调用函数并编译