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;
}
相关推荐
A小辣椒1 天前
TShark:Wireshark CLI 功能
linux
A小辣椒1 天前
TShark:基础知识
linux
AlfredZhao1 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪2 天前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5203 天前
Linux 11 动态监控指令top
linux
不会C语言的男孩3 天前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言