linux如何获得祖父进程id,Linux内核源码分析之do_fork()函数的执行过程

66b52468c121889b900d4956032f1009.png

8种机械键盘轴体对比

本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?

Linux内核版本2.6.11

do_fork函数概括建立进程控制结构并赋初值,使其成为进程映像。

为新进程的执行设置跟踪进程执行情况的相关内核数据结构。包括 任务数组、自由时间列表 tarray_freelist 以及 pidhash[] 数组。

启动调度程序,使子进程获得运行的机会。

执行过程首先分析该函数的参数:long (unsigned long clone_flags,

unsigned long stack_start,

struct pt_regs *regs,

unsigned long stack_size,

int __user *parent_tidptr,

int __user *child_tidptr)

注:[clone_flags : 宏定义clone标志,例如CLONE_VM表示与父进程共享内存描述符(线程的创建)

stack_start : 将用户态栈指针值给子进程的esp寄存器(给子进程分配新堆栈)

regs : 通用寄存器的地址(用户态->内核态,寄存器的值保存至内核栈中)

stack_size : 置0

parent_tidptr : 父进程用户态变量地址

child_tidptr : 新进程用户态变量地址]通过查找pidmap_array位图,给子进程分配新的id号(由于PID数量有限,内核必须管理位图来表示PID空闲或者已分配):long pid = alloc_pidmap();如果父进程正在被跟踪(即current->ptrace不为0时),检查debugger程序是否想跟踪子进程,并且子进程不是内核进程(CLONE_UNTRACED未设置)那么就设置CLONE_PTRACE标志,即子进程也被跟踪:if (unlikely(current->ptrace)) {

trace = fork_traceflag (clone_flags);

if (trace)

clone_flags |= CLONE_PTRACE;

}

//fork_traceflag 函数:

static inline int fork_traceflag (unsigned clone_flags)

{

if (clone_flags & CLONE_UNTRACED)

return 0;

else if (clone_flags & CLONE_VFORK) {

if (current->ptrace & PT_TRACE_VFORK)

return PTRACE_EVENT_VFORK;

} else if ((clone_flags & CSIGNAL) != SIGCHLD) {

if (current->ptrace & PT_TRACE_CLONE)

return PTRACE_EVENT_CLONE;

} else if (current->ptrace & PT_TRACE_FORK)

return PTRACE_EVENT_FORK;

return 0;

}调用copy_process函数复制进程描述符,如果所有必须的资源都是可用的,该函数返回刚创建的task_struct描述符的地址:(以下是函数大概的执行过程)copy_process函数先检查检查clone_flags所传标志的一致性,如果不符合要求则会直接返回错误代码,创建失败。符合要求则调用dup_task_struct()为子进程创建内核栈、thread_info和进程描述符(此时父子进程描述符完全一致,方式为写时拷贝)。

确保子进程创建后当前用户拥有的进程数目没有超出分配资源的限制后,将子进程描述符内大部分成员变量置0,以将父进程区分开来。

子进程状态设置为TASK_UNINTERRUPTIBLE

调用copy_flags()以更新子进程task_struct的flags成员

根据clone flag,拷贝或共享打开的文件,文件系统及信息,进程地址空间等

返回一个指向子进程描述符的指针p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid)

{

//...

p = dup_task_struct(current);

//...

}如果进程描述符的地址小于(unsigned long)MAX_ERRNO–4095,则做下段代码片以后的工作,否则,将PID置为空闲,并将越界的地址赋给pid字段:if (!IS_ERR(p)){...}

else {

free_pidmap(pid);

pid = PTR_ERR(p);

} // << end if !IS_ERR(p) >>如果设置了CLONE_VFORK,表示这是在使用vfork()系统调用,将vfork字段中的done置0,并调用init_waitqueue_head(),初始化list_head链表:if (clone_flags & CLONE_VFORK) {

p->vfork_done = &vfork;

init_completion(&vfork);

}如果设置了CLONE_STOPPED,或必须跟踪子进程(PT_PTRACED==1),就设置子进程为TASK_STOPPED状态,并发送SIGSTOP信号挂起它:if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED)) {

/*

* We'll start up with an immediate SIGSTOP.

*/

sigaddset(&p->pending.signal, SIGSTOP);

set_tsk_thread_flag(p, TIF_SIGPENDING);

}如果没有设置CLONE_STOPPED,调用wake_up_new_task函数(其作用是:调整父进程和子进程的调度参数,若父、子运行在同一个CPU上,并且不能共享同一组页表(CLONE_VM==0),把子进程插入父进程运行队列,如果运行在不同的CPU上,或父子进程共享同一组页表,就把子进程插入父进程运行队列的队尾):if (!(clone_flags & CLONE_STOPPED))

wake_up_new_task(p, clone_flags);

else // 如果CLONE_STOPPED标志被设置,就把子进程设置为TASK_STOPPED状态

p->state = TASK_STOPPED;如果(父)进程正被跟踪,则把子进程的PID插入到父进程的ptrace_message字段,并调用ptrace_notify(ptrace_notify使当前进程停止运行,并向当前进程的父进程发送SIGCHLD信号。子进程的祖父进程是跟踪父进程的debugger进程。dubugger进程可以通过ptrace_message获得被创建子进程的PID):if (unlikely (trace)) {

current->ptrace_message = pid;

ptrace_notify ((trace << 8) | SIGTRAP);

}如果设置了CLONE_VFORK,就把父进程插入等待队列,并挂起父进程直到子进程结束或者执行了新的程序:if (clone_flags & CLONE_VFORK) {

wait_for_completion(&vfork);

if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE))

ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP);

}

// << end if !IS_ERR(p) >> if !IS_ERR(p)语句执行完毕,退出判断返回子进程的pid,do_fork()函数执行完毕。return pid;

参考文献

正文结束,如需转载,请标明出处!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值