Linux如何添加系统调用

系统调用是什么

系统调用本身是一个软中断,在内核初始化的时候就会对其进行中断向量注册

c 复制代码
// init/main.c
void main(void)
{
    trap_init(); // 主要的中断注册在这
    // ...
    sched_init(); // 在这个函数内注册了系统调用的中断向量
}

系统调用中断值为0x80

c 复制代码
// kernel/sched.c
void sched_init(void)
{
    // ...
    set_system_gate(0x80,&system_call)
}

set_system_gate一般是注册软中断,set_trap_gate一般是注册硬中断

系统调用其实就是一个中断,执行对应的system_call函数

system_call逻辑

直接看汇编代码, 大概逻辑就是分为三个部分,最初的中断以及系统调用将一些寄存器数据压入到栈顶,第二个就是从sys_call_table中寻找到系统调用数值对应的函数进行调用,最后就是寄存器值的还原

一些系统函数例如fork、open等其实是封装的_syscall0, _syscall1, _syscall2, _syscall3,进行系统调用,并且能够自动处理栈中的参数

s 复制代码
system_call:
	cmpl $nr_system_calls-1,%eax # 调用号如果超出范围的话就在eax中置-1并退出
	ja bad_sys_call
	push %ds # 保存原段寄存器值
	push %es
	push %fs
	pushl %edx # 一个系统调用最多带有3个参数
	pushl %ecx		# push %ebx,%ecx,%edx as parameters
	pushl %ebx		# to the system call
	movl $0x10,%edx		# set up ds,es to kernel space
	mov %dx,%ds # ds、es指向内核数据段
	mov %dx,%es
	movl $0x17,%edx		# fs points to local data space
	mov %dx,%fs
	call *sys_call_table(,%eax,4) # 计算调用地址 [_sys_call_table + %eax * 4], 间接调用指定功能C函数
	pushl %eax # 把系统调用返回值入栈
	movl current,%eax # 将当前任务数据结构地址放在eax中
	cmpl $0,state(%eax)		# 不在就绪状态则重新执行调度程序
	jne reschedule
	cmpl $0,counter(%eax)		# 在就绪状态但是时间片用完也会去执行调度程序
	je reschedule
ret_from_sys_call:
	movl current,%eax		# task[0] cannot have signals
	cmpl task,%eax
	je 3f
	cmpw $0x0f,CS(%esp)		# was old code segment supervisor ?
	jne 3f
	cmpw $0x17,OLDSS(%esp)		# was stack segment = 0x17 ?
	jne 3f
	movl signal(%eax),%ebx
	movl blocked(%eax),%ecx
	notl %ecx
	andl %ebx,%ecx
	bsfl %ecx,%ecx
	je 3f
	btrl %ecx,%ebx
	movl %ebx,signal(%eax)
	incl %ecx
	pushl %ecx
	call do_signal
	popl %eax
3:	popl %eax
	popl %ebx
	popl %ecx
	popl %edx
	pop %fs
	pop %es
	pop %ds
	iret

添加新的系统调用

定义头文件

需要注意,这里的修改需要同步到系统的sys/include/linux/sys.h下,否则在系统中编译使用会找不到该系统调用

c 复制代码
// include/linux/sys.h
extern int sys_whoami();

// ...
// 在sys_call_table中添加sys_whoami

定义新的系统调用数

s 复制代码
# kernel/system_call.s

nr_system_calls = 74 # 在原有基础上加一

定义实现

c 复制代码
#include <string.h>
#include <errno.h>
#include <asm/segment.h>

char msg[24];

int
sys_whoami(char* name,unsigned int size)
{
	int len = 0;
	int i = 0;
	for(; msg[len] != '\0'; len++ );
	if (len > size) {
		return -EINVAL;	
	}
	for (i = 0; i < size; ++i) {
		put_fs_byte(msg[i],name+i);	
		if (msg[i] == '\0') {
			break;	
		}
	}
	return i;
}

配置编译选项

makefile 复制代码
OBJS = ... who.o

# ...

who.s who.o: who.c ../include/linux/kernel.h ../include/unistd.h 

使用

c 复制代码
#include <errno.h>
#define __LIBRARY__
#include <unistd.h>

_syscall2(int, whomai, char*, name, unsigned int, size);

int main()
{
    char s[30];
    whoami(s, 30);
    printf("%s", s);
    return 0;
}
相关推荐
Chenyu_3106 分钟前
04.基于C++实现多线程TCP服务器与客户端通信
linux·服务器·网络·c++·tcp/ip·算法·visualstudio
塞尔维亚大汉10 分钟前
OpenHarmony轻量系统服务管理samgr-feature赏析及实现
操作系统·嵌入式·harmonyos
奔波霸的伶俐虫1 小时前
liunx磁盘挂载和jar启动命令
linux·运维·服务器
wwwlyj1233211 小时前
slab分配器(3):slab内存申请和释放
linux
JiMoKuangXiangQu2 小时前
Linux 网络:skb 数据管理
linux·网络·skb
做程序员的第一天2 小时前
ubuntu下在pycharm中配置已有的虚拟环境
linux·ubuntu·pycharm
唐青枫2 小时前
Linux fgrep 命令使用详解
linux
亭墨2 小时前
linux0.11内核源码修仙传第六章——中断初始化
linux·c语言·驱动开发·学习·面试
Ljw...2 小时前
序列化和反序列化(Linux)
linux·开发语言·网络·c++·tcp/ip·序列化反序列化
aiprtem3 小时前
LVGL组件设计之模拟桌面
linux·c语言·网络·物联网