文章目录
一、设计思路
-  
下载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添加系统调用函数并编译
 









