系统调用是什么
系统调用本身是一个软中断,在内核初始化的时候就会对其进行中断向量注册
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;
}