linux 0.11 系统调用过程

系统调用,就是int 0x80;函数定义一般使用三个宏来进行定义:

#define _syscall0(type,name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
	: "=a" (__res) \
	: "0" (__NR_##name)); \
if (__res >= 0) \
	return (type) __res; \
errno = -__res; \
return -1; \
}

#define _syscall1(type, name, atype, a) \
type name(atype a) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
	: "=a" (__res) \
	: "0" (__NR_##name),"b" ((long)(a))); \
if (__res >= 0) \
	return (type) __res; \
errno = -__res; \
return -1; \
}

#define _syscall2(type,name,atype,a,btype,b) \
type name(atype a,btype b) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
	: "=a" (__res) \
	: "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b))); \
if (__res >= 0) \
	return (type) __res; \
errno = -__res; \
return -1; \
}

#define _syscall3(type,name,atype,a,btype,b,ctype,c) \
type name(atype a,btype b,ctype c) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
	: "=a" (__res) \
	: "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)),"d" ((long)(c))); \
if (__res>=0) \
	return (type) __res; \
errno=-__res; \
return -1; \
}

本质上来讲就是使用int 0x80触发系统中断,而该系统中断是在init.c/main()的sched_init()中进行定义的,即设置为system_call(system_call.s中定义);功能号采用了类似BIOS的中断方式,即eax寄存器中存放功能号,而后如果需要传递参数的话,参数列表从左向右依次存放在ebx,ecx,edx中,最多传递3个参数;功能号的定义格式为_NR_##name,进行宏替换后,就是一系列熊调用功能号,定义在unistd.h中,这些功能号的作用时作为sys.h中系统调用函数的索引,用于进行对应的系统调用函数调用;

那么在触发了系统调用中断后,就会通过门描述符调用system_call程序,此时进入内核层;eax存放了功能号,先判定功能号时候合法,合法的话就将寄存器状态入栈,然后将ds、es段寄存器指向内核数据段,而fs段寄存器指向用户数据段;然后调用系统调用数组程序(通过函数指针数组和数组索引号进行寻址);这里需要留意的就是函数参数的传递方法,对于一般的函数调用,首先就是将参数入栈,然后将eip入栈,接着跳转目标位置进行程序执行,这里就是手动操作填入了参数到栈中,然后使用call跳转程序,至于函数中如何获取到参数数据这个问题,这个涉及到esp、ebp在调用程序过程中这两个寄存器的使用,ebp的意义作为一个函数调用的锚点,当函数执行完毕要返回调用函数位置处,就要将esp赋值为ebp,然后进行ret返回原先的程序环境进行程序的继续执行;esp是控制一个函数调用过程中栈区的资源分配,需要进行空间分配的时候,esp就自减,然后填充数据,在程序返回进行栈区资源释放只需要esp=ebp即可;而进行函数调用的时候,他首先会将函数参数入栈,那么在call之后,只需要基于ebp进行地址运算即可获取到需要的第n个参数的数据;另外,程序返回时,返回值将设置到eax寄存器中;

在调用系统调用程序之后,判断一下当前任务是否还是运行态,以及时间片有没有耗光,如果耗光了或者任务不是运行态了,就要执行程序调度,进行其他程序的执行;

如果程序可以继续执行,那么就开始系统调用的返回过程,也就是ret_from_system_call,说是系统调用的返回,其实基本从内核态跳转用户态都会通过该程序进行;之后就是对任务的信号进行检测处理,任务0不进行信号处理,跳转到系统调用之前是内核态的话也不处理,处理信号的话首先就是获取到需要处理信号的编号,然后根据这个编号去获取信号处理的函数指针;首先需要了解两个宏定义:

#define SIG_DFL		((void (*)(int))0)	/* default signal handling */
#define SIG_IGN		((void (*)(int))1)	/* ignore signal */

当函数句柄为SIG_IGN的时候,直接返回,无需继续设置什么,如果是SIG_DEL默认处理的话,处理子进程退出信号对于其余信号直接退出;另外还判定这个信号是不是只是当次有效(SA_ONESLOT位置位),如果是的话需要重置信号句柄;最终经过这个检测过程后,开始对信号处理环境进行设置,这里之所以称之为设置是由于这里的操作并不是直接调用函数的,这是由于函数一般定义在用户层,内核层中没有对应的程序,因此设置内核返回用户层的eip指向信号处理程序。至此,就信号处理在内核中的操作就完成,然后从内核态跳转会用户层,开始执行需要的信号处理函数,但是这就涉及到另外一个问题,信号处理函数执行完毕后如何跳转会原本的eip位置,于是就有了信号处理程序中对用户层栈区的数据插入,就是将sa_restorer等一系列参数入栈,然后当信号处理函数执行完毕之后,就会返回到sa_restorer函数中继续执行,该函数最后会将进入系统调用的eip进行恢复,且将eax等寄存器恢复,最终完成原本的eip环境复原。

经过以上过程后,用户程序可以继续执行;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值