郑钢《操作系统真像还原》——系统调用

一、系统调用方式
1、系统调用入口:0x80中断,中断描述符表中
2、系统调用的第4号功能调用:_NR_write
3、调用“系统调用”有两种方式。
( 1) 将系统调用指令封装为 c 库函数,通过库函数进行系统调用,操作简单。
(2 )不依赖任何库函数,直接通过汇编指令 int 与操作系统通信。int 0x80,子功能号保存在eax中。
二、Linux系统调用概述
1、Linux 系统调用是用中断门来实现的,通过软中断指令 int 来主动发起中断信号。
2、Linux 在寄存器 eax 中写入子功能号,例如系统调open 和 close 都是不同的子功能号,当用户程序通过int 0x80 进行系统调用时,对应的中断处理例程会根据 eax 的值来判断用户进程申请哪种系统调用。
3、Linux 中的系统调用是用寄存器来传递参数的,主要原因是可以减少3特权级进入0特权级的栈的多次访问内存操作,使陷入系统更简单更快。

#define _syscall3(type, name, typel, argl, type2, arg2, type3, arg3)
type name(typel argl, type2 arg2, type3 arg3) {
    long _res;
    __asm__ volatile {"push %%ebx;movl %2, %%ebx;int $0x80;pop %%ebx"
              : "=a"(__res)
              :"0"(__NR_##name),"r1"((long) (argl)),"c"((long) (arg2)), 
               "d"((long) (arg3 )) : "memory");
__syscall_return(type, _ res);
}

四、系统调用的简单实现流程(最多支持三个参数)
(1)用中断门实现系统调用,效仿 Linux用0x80号中断作为系统调用的入口。
(2)在 IDT 中安装 0x80 号中断对应的描述符,在该描述符中注册系统调用对应的中断处理例程。
(3)建立系统调用子功能表 syscall_table,利用eax寄存器中的子功能号在该表中索引相应的处理函数。
(4)用宏实现用户空间系统调用接口_syscall ,最大支持3个参数的系统调用,故只需要完成_syscall[0-3]。寄存器传递参数, eax为子功能号, ebx保存第一参数,ecx 保存第二个参数,edx 保存第三个参数。
五、进程系统调用获取PID主要流程

process_execute(u_prog_a, "user_prog_a");
/* 测试用户进程 */
void u_prog_a(void) {
    prog_a_pid = getpid();
    while(1);
}

1、执行进程:void process_execute(void* filename, char* name)
为进程创建线程,该线程运行start_process(filename):thread_create(thread, start_process, filename)
2、void thread_create(struct task_struct* pthread, thread_func function, void* func_arg)
struct thread_stack* kthread_stack = (struct thread_stack*)pthread->self_kstack;
kthread_stack->eip = kernel_thread; //static void kernel_thread(thread_func* function, void* func_arg)
kthread_stack->function = function;
kthread_stack->func_arg = func_arg;
3、加入就绪队列:list_append(&thread_ready_list, &thread->general_tag)
4、切换到创建的线程:switch_to(cur, next)
5、start_process(filename)
void* function = filename_;
struct task_struct* cur = running_thread();
struct intr_stack* proc_stack = (struct intr_stack*)cur->self_kstack;
proc_stack->eip = function;
asm volatile (“movl %0, %%esp; jmp intr_exit” : : “g” (proc_stack) : “memory”);
6、intr_exit
通过iretd执行proc_stack->eip = function中function函数,即上面的void u_prog_a(void)
7、getpid();
_syscall0(SYS_GETPID); // SYS_GETPID是一个枚举类型,现等于0。return _syscall0(SYS_GETPID);
8、执行int 0x80中断,进入中断
#define _syscall0(NUMBER) ({
int retval;
asm volatile (
“int $0x80”
: “=a” (retval)
: “a” (NUMBER)
: “memory”
);
retval;
})
9、在初始化中断描述符时已经将0x80中断初始化到中断描述符中
static void idt_desc_init(void)
make_idt_desc(&idt[lastindex], IDT_DESC_ATTR_DPL3, syscall_handler); // lastindex=0x80,触发中断运行syscall_handler函数
10、syscall_handler()
call [syscall_table + eax*4] // syscall_table为保存函数地址的数组,eax为子功能号(SYS_GETPID值存入eax中)
11、syscall syscall_table[syscall_nr] ——> syscall_table[SYS_GETPID] = sys_getpid;
uint32_t sys_getpid(void) {
return running_thread()->pid;
}
这里的return running_thread()->pid返回保存到第8点的retval中,然后返回retval。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值