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;
}
相关推荐
Yana.nice17 分钟前
openssl将证书从p7b转换为crt格式
java·linux
AI逐月22 分钟前
tmux 常用命令总结:从入门到稳定使用的一篇实战博客
linux·服务器·ssh·php
小白跃升坊1 小时前
基于1Panel的AI运维
linux·运维·人工智能·ai大模型·教学·ai agent
跃渊Yuey1 小时前
【Linux】线程同步与互斥
linux·笔记
舰长1151 小时前
linux 实现文件共享的实现方式比较
linux·服务器·网络
zmjjdank1ng1 小时前
Linux 输出重定向
linux·运维
路由侠内网穿透.1 小时前
本地部署智能家居集成解决方案 ESPHome 并实现外部访问( Linux 版本)
linux·运维·服务器·网络协议·智能家居
VekiSon2 小时前
Linux内核驱动——基础概念与开发环境搭建
linux·运维·服务器·c语言·arm开发
zl_dfq2 小时前
Linux 之 【进程信号】(signal、kill、raise、abort、alarm、Core Dump核心转储机制)
linux
Ankie Wan2 小时前
cgroup(Control Group)是 Linux 内核提供的一种机制,用来“控制、限制、隔离、统计”进程对系统资源的使用。
linux·容器·cgroup·lxc