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;
}
相关推荐
ice___Cpu8 分钟前
Linux 基本使用和 web 程序部署 ( 8000 字 Linux 入门 )
linux·运维·前端
z2023050810 分钟前
linux 之0号进程、1号进程、2号进程
linux·运维·服务器
狐心kitsune42 分钟前
erlang学习:Linux常用命令1
linux·学习·erlang
DREAM依旧1 小时前
《深入了解 Linux 操作系统》
linux
阿赭ochre2 小时前
Linux环境变量&&进程地址空间
linux·服务器
Iceberg_wWzZ2 小时前
数据结构(Day14)
linux·c语言·数据结构·算法
可儿·四系桜2 小时前
如何在多台Linux虚拟机上安装和配置Zookeeper集群
linux·服务器·zookeeper
Flying_Fish_roe2 小时前
linux-软件包管理-包管理工具(Debian 系)
linux·运维·debian
大广-全栈开发3 小时前
centos 7 安装gitlab
linux·git·centos
666786663 小时前
Mysql高级篇(中)—— SQL优化
linux·运维·服务器·数据库·sql·mysql