linux下的系统调用(linux2.6.11.12)

应用程序在用户态执行时可能会请求系统调用(类似于中断,进行调用前需要保护现场,完成后需要恢复现场),比如读写磁盘的I/O操作,这时就需要int 0x80(十进制128)指令。每个系统调用具有固定的系统调用号(即使这个系统调用失效,这个系统 系统调用号也不会给别人)。

         Int0x80之后就进入到内核代码了,在内核中首先执行到system_call的位置,pushl %eax的意思是把eax寄存器的值压入程序内核态堆栈,eax中存放着系统调用号。用户态程序传递给系统调用的参数存放 在ebx、ecx、edx等等寄存器中,而ebx、ecx、edx等寄存器的值通过SAVE_ALL宏压入内核堆栈。

下面为fork系统调用的过程:

在用户态fork系统调用,产生0x80号中断,在进入中断处理程序之前 保护现场,将它的系统调用号存入eax寄存器,通过寄存器传给内核,进入内核空间,通过系统调用号在系统调用表中找到相应的系统调用sys_fork,

int sys_fork(struct pt_regs regs)
{
return do_fork(SIGCHLD, regs.esp, &regs, 0, NULL, NULL);
}

//struct pt_regs中前9个是存放通过SAVE_ALL压入的寄存器值。 orig_eax存放的是           中断系统调用号. 后面的几个是处理器自动压入的寄存器值。

long do_fork(unsigned long clone_flags,unsigned long stack_start,struct

pt_regs *regs, unsigned long stack_size,int __user *parent_tidptr,
     int __user *child_tidptr)

1.先申请一个task_struct(进程控制块的描述符)的结构体,表示即将生成新的进程,调用alloc_pidmap,为

新的子进程分配pid。如果pid<0,reutrn -EAGAIN(EAGIN表示危险已经过去)。如果父进程正在百跟踪,就检查debugger

程序是否想跟踪子进程(fork_traceflag),并且子进程不是内核进程(即CLONE_UNTRACE未被设置),那么就

设置CLONE_PTRACE。

alloc_pidmap的具体实现过程:

RESERVED_PIDS = 300,前300pid是固定的,不可以分配。

offset为pid在页框中的偏移量,因为pid可能大于一个页框所能容纳的最大位数,即BIRTS_PER_PAGE,所以

pid要对BITS_PER_PAGE取模,(offset = pid&BITS_PER_PAGE)。

pid/BITS_PER_PAGE求得页面在位图中的下标。

max_scan = (pid_max _BITS_PER_PAGE -1)/ BITS_PER_PAGE - !offset;

//max_scan位最大的扫描次数,如果offset为0,则扫描一次,否则两次。

for循环开始扫描,如果页面不存在则分配页面

如果该页框位数大于0,如果该offset空闲,则返回该值。

否则继续扫描该页框其余位。

然后根据下标的offset重新算出pid。

如果页框分配失败,则继续扫描剩余的页框。

2.调copy_process复制进程描述符,具体实现为:

p = dup_task_struct(current),为先进城创建一个内核栈、thread_info和task_struct,这里完全拷贝父进程的

内容,所以到目前为止,父进程与子进程是完全没有区别的。

检查所有的进程数目是否已经超出了系统规定的最大进程数,如果没有的话,那么就开始设置进程描述符

中的初始值,从这开始,父进程和子进程就开始区别开了。

设置子进程的状态为不可被TASK_UNINTERRUPTIBLE,从而保证这个进程现在不能被投入运行,因为还有

很多的标志位、数据等没有被设置。

复制标志位(flags成员)以及权限位(PE_SUPERPRIV)和其他的一些标志位。

调用get_pid给子进程获取一个有效的并且是唯一的进程表示服PID。

根据传入 cloning flags对相应的内容进行copy,比如说打开的文件符号、信号等。

父子进程均分父进程剩余的时间片。

return p;返回一个指向子进程的指针。

3.如果设置了CLONE_STOPPED,或者必须跟踪子进程,就设置子进程为TASJ_STOPPED状态,并发送SIGSTOP

信号挂起它。

4.没有设置CLONE_STOPPED,就调用wake_up_new_task它调整父进程和子进程的调度参数.
 如果父子进程运行在同一个CPU上,并且不能共享同一组页表(CLONE_VM标志被清0).那么,就把子进程插入父进程

运行队列.并且子进程插在父进程之前.这样做的目的是:如果子进程在创建之后执行新程序,就可以避免写时复制机制

执行不必要时页面复制.否则,如果运行在不同的CPU上,或者父子进程共享同一组页表.就把子进程插入父进程运行队列

的队尾。

5.如果进程正被跟踪,则把子进程的PID插入到父进程的ptrace_message,并调用ptrace_notify
ptrace_notify使当前进程停止运行,并向当前进程的父进程发送SIGCHLD信号.子进程的祖父进程是跟踪父进程的

debugger进程.dubugger进程可以通过ptrace_message获得被创建子进程的PID。

6.如果设置了LCONE_CFORK,就把父进程插入等待队列,并挂起父进程直到子进程结束或者执行了新的程序。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值