Linux内核系统调用开发流程?
省流:
1、kernel/sys.c里实现函数
2、Include/linux/syscalls.h在系统调用表里声明
3、arch/x86/entry/syscalls/syscall_64.tbl里映射:462 ——> sys_get_process_memory_info
4、make clean && make -j$(nproc)编译内核
5、make install && update-grub安装新内核,更新GRUB

获取源码:从Linux官网或Git仓库克隆内核源码。
环境准备:安装编译工具和相关依赖。
配置内核:使用make menuconfig
等工具配置内核选项。 *****这一步就是开发过程
编译内核:通过make
命令编译内核和模块。
安装内核:使用make install
安装内核和make modules_install
安装模块。
更新引导程序:更新GRUB配置,确保系统使用新内核。
测试内核:重启并验证新内核是否正常加载。
提交补丁:编写和提交补丁,经过审查后合并到主线代码中。
以实现简单的系统调用以读取进程内存信息的实验为例
获取源码
从Linux官网或Git仓库克隆内核源码。
环境准备
安装编译工具和相关依赖。
配置内核
使用make menuconfig
等工具配置内核选项。 *****这一步就是开发过程
(1)实现新的系统调用
在kernel/sys.c
文件中实现sys_get_process_memory_info()
函数
系统调用是用户空间程序与内核空间交互的接口,允许用户程序请求内核执行特定操作。sys.c
文件就是实现这些系统调用的地方。
系统调用服务例程:每个系统调用都对应一个内核服务例程来实现系统调用的功能
在kernel/sys.c
中如何编程?
SYSCALL_DEFINE2(get_process_memory_info, pid_t, pid, struct mem_info __user *, info)
#include <linux/sched.h> #include <linux/mm.h> #include <linux/uaccess.h> #include <uapi/linux/mem_info.h> SYSCALL_DEFINE2(get_process_memory_info, pid_t, pid, struct mem_info __user *, info){ struct task_struct *task; struct mm_struct *mm; struct mem_info mem_info;
task = find_task_by_vpid(pid); if (!task) { return -ESRCH; }
mm = get_task_mm(task); if (!mm) { return -EINVAL; }
mem_info.start_code = mm->start_code; mem_info.end_code = mm->end_code; mem_info.start_data = mm->start_data; mem_info.end_data = mm->end_data; mem_info.start_stack = mm->start_stack; mem_info.start_brk = mm->start_brk; mem_info.brk = mm->brk;
mmput(mm);
if (copy_to_user(info, &mem_info, sizeof(struct mem_info))) { return -EFAULT; }
return 0; }
|
#include <linux/sched.h>:进程调度相关。task_struct任务结构体(进程状态信息) #include <linux/mm.h>:内存管理相关。 #include <linux/uaccess.h>:内核空间和用户空间之间数据交换。copy_to_user, copy_from_user #include <uapi/linux/mem_info.h>:自定义的头文件。
struct task_struct *find_task_by_vpid(pid_t vnr);
|
在编写过程中如何查询库函数的源码?也就是包含的头文件中的函数的源码
cscope -Rb
cscope -d
struct task_struct *find_task_by_vpid(pid_t vnr);
|
(2)在系统调用表里声明函数
系统调用表的作用是将用户程序发出的系统调用请求映射到相应的内核函数。当用户程序通过中断或陷阱进入内核模式时,操作系统会查找系统调用表,并根据系统调用号调用相应的内核函数来执行具体的操作。
系统调用表在Include/linux/syscalls.h
,编写格式是asmlinkage long sys_get_process_memory_info(pid_t pid, struct mem_info __user *info);
#include <uapi/linux/mem_info.h> asmlinkage long sys_get_process_memory_info(pid_t pid, struct mem_info __user *info);
header-y += mem_info.h
#ifndef _UAPI_LINUX_MEM_INFO_H #define _UAPI_LINUX_MEM_INFO_H
struct mem_info { unsigned long start_code, end_code; unsigned long start_data, end_data; unsigned long start_stack; unsigned long start_brk, brk; }; #endif
|
(3)系统调用映射表
arch/x86/entry/syscalls/syscall_64.tbl
,系统调用映射表映射了每个系统调用号与内核中相应的系统调用函数之间的关系,当用户在应用程序中用到系统调用号时,会向内核请求调用映射的相应系统调用函数。
编写格式:
462 common get_process_memory_info sys_get_process_memory_info
|
编译内核
通过make
命令编译内核和模块。
make clean && make -j$(nproc)
|
安装内核
使用make install
安装内核和make modules_install
安装模块。
更新引导程序
更新GRUB配置,确保系统使用新内核。
make install && update-grub
|
测试内核
重启并验证新内核是否正常加载,uname -r
。
本实验需要编写一个应用程序调用我们实现的系统调用函数,同时也是对系统调用函数的检验。
#include <stdio.h> #include <sys/syscall.h> #include <unistd.h> #include <errno.h> #include <string.h> #define SYS_GET_PROCESS_MEMORY_INFO 462 struct mem_info { unsigned long start_code, end_code; unsigned long start_data, end_data; unsigned long start_stack; unsigned long start_brk, brk; }; int main() { pid_t pid = getpid(); struct mem_info info; printf("Enter PID:"); scanf("%d", &pid); long result = syscall(SYS_GET_PROCESS_MEMORY_INFO, pid, &info); if (result == 0) { printf("Memory info for process %d:\n", pid); printf(" Code: 0x%lx - 0x%lx\n", info.start_code, info.end_code); printf(" Data: 0x%lx - 0x%lx\n", info.start_data, info.end_data); printf(" Stack: 0x%lx\n", info.start_stack); printf(" Heap: 0x%lx - 0x%lx\n", info.start_brk, info.brk); } else { printf("Failed to retrieve memory info for process %d, error: %s\n", pid, strerror(-result)); } return 0; }
|
成功。
遇到的问题
1、安装新内核5.10.234之后,WiFi模块似乎不兼容了,无法联网
2、写好系统调用实现之后编译时出现报错,undefined reference to __x64___x64_sys_get_process_memory_info
,内核无法引用
3、运行用户空间程序,报错:Failed to retrieve memory info for process 35328, error: -1
解决方法
1、重新尝试安装内核6.8.1,WiFi终于可以用了
2、在syscall_64.tbl文件里注册时尝试修改之前的注册,注意到报错信息多了一个__x64_,在注册时改成
3、因为我错误地使用旧内核运行了该程序,使得调用函数失败,需要切换内核