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;
}
相关推荐
筱源源10 分钟前
Kafka-linux环境部署
linux·kafka
算法与编程之美1 小时前
文件的写入与读取
linux·运维·服务器
xianwu5431 小时前
反向代理模块
linux·开发语言·网络·git
Amelio_Ming1 小时前
Permissions 0755 for ‘/etc/ssh/ssh_host_rsa_key‘ are too open.问题解决
linux·运维·ssh
Ven%2 小时前
centos查看硬盘资源使用情况命令大全
linux·运维·centos
TeYiToKu3 小时前
笔记整理—linux驱动开发部分(9)framebuffer驱动框架
linux·c语言·arm开发·驱动开发·笔记·嵌入式硬件·arm
dsywws3 小时前
Linux学习笔记之时间日期和查找和解压缩指令
linux·笔记·学习
yeyuningzi3 小时前
Debian 12环境里部署nginx步骤记录
linux·运维·服务器
上辈子杀猪这辈子学IT3 小时前
【Zookeeper集群搭建】安装zookeeper、zookeeper集群配置、zookeeper启动与关闭、zookeeper的shell命令操作
linux·hadoop·zookeeper·centos·debian
minihuabei4 小时前
linux centos 安装redis
linux·redis·centos