我们先来介绍内核进程创建函数kernel_thread()
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
struct pt_regs regs;//定义系统寄存器
memset(®s, 0, sizeof(regs));//把这些寄存器清零
regs.ARM_r1 = (unsigned long)arg;//我们把传递给可执行函数的参数先付给r1。
regs.ARM_r2 = (unsigned long)fn;//我们把可执行函数指针传递给r2。
regs.ARM_r3 = (unsigned long)do_exit;//我们把函数推出函数传递给r3。
regs.ARM_pc = (unsigned long)kernel_thread_helper;//最后我们把kernel_thread_helper()函数指针传递给pc。这样我们就会直接处理这个函数了,这个函数只是一个过渡的过程,首先他会把r1传递给r0,将r3传递给lr,最后把r2传递给pc。所以执行完这个过渡函数后,我们就会执行可执行函数了。
regs.ARM_cpsr = SVC_MODE;//我们设置当前寄存器cpsr中的超级用户模式。
return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);//这个函数很重要,就是进程创建函数,我们这里要注意的只是flags部分,这里就规定了这个子进程与父进程之间的关系。CLONE_VM 表示父子进程共享虚拟地址,CLONE_UNTRACED 表示不可以调试子程序。
}
接下来我们看下创建用户进程函数sys_fork(),这个是fork()函数系统调用的内核实现函数。
asmlinkage int sys_fork(struct pt_regs regs)
{
return do_fork(SIGCHLD, regs.esp, ®s, 0, NULL, NULL);//又可以发现这里也是调用do_fork()函数去创建一个进程,这里只是设置了SIGCHLD
}
我们接下来看下创建用户进程,父子进程共享现行地址空间的函数sys_vfork(),这个是vfork()函数系统调用的内核实现函数。
asmlinkage int sys_vfork(struct pt_regs regs)
{
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, ®s, 0, NULL, NULL);//这里我们没有看过的标志是CLONE_VFORK,它表示可以调用vfork()父进程可以睡眠标志。
}
接下来要介绍的是克隆用户进程函数sys_clone(),这个是clone()函数系统调用的内核实现函数。
asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, struct pt_regs *regs)//这个函数的参数除了我们上面一直强调的系统寄存器外,还多出了clone_flags克隆进程控制标志字,还有就是新的栈指针newsp
{
if (clone_flags & (CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID))//如果clone_flags设置了设置父进程TID标志,或是清楚子进程TID标志
return -EINVAL;//返回无效,由于clone()系统调用就是克隆出轻量级进程,也就是线程,与父进程共享同样的PID号,他们形成线程组,线程组的领头线程的TID号就是他们的PID号。
if (!newsp)//如果新的堆栈为空
newsp = regs->ARM_sp;//将传进来的堆栈寄存器的值作为新的栈指针。
return do_fork(clone_flags, newsp, regs, 0, NULL, NULL);
}
我们下来要讨论的函数就是这个创建进程函数do_fork()
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)
{
struct task_struct *p;
int trace = 0;
long pid = alloc_pidmap();//申请一个空闲PID号
if (pid < 0)//如果发现pid无效的话
return -EAGAIN;
if (unlikely(current->ptrace)) {//如果当前进程的跟踪标志不等于0,说明要继续调试
trace = fork_traceflag (clone_flags);//这个函数是根据clone_flags和current->ptrace两者的值来设置trace
if (trace)//如果trace不为0的话
clone_flags |= CLONE_PTRACE;//控制标志或上CLONE_PTREACE,表示这个进程是跟踪调试子进程
}
p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid);//根据clone_flags的设置来创建进程p的各种数据结构并做相关初始化。
if (!IS_ERR(p)) {//如果上面的操作无误
struct completion vfork;//声明完成量结构vfork
if (clone_flags & CLONE_VFORK) {//如果执行的是vfork()系统调用
p->vfork_done = &vfork;//将上面声明的变量地址传给p->vfork_done
init_completion(&vfork);//初始化完成结构变量的成员
}
if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED)) {//如果要调试进程p,或者要求将进程p的起始状态设置为暂停状态
sigaddset(&p->pending.signal, SIGSTOP);//p->pending->signal[0]=SIGSTOP其中pending表示进程p的私有挂起信号。
set_tsk_thread_flag(p, TIF_SIGPENDING);//p->thread_info->flags=TIF_SIGPENDING表示进程有挂起型号。
}
if (!(clone_flags & CLONE_STOPPED))//进一步确定是否要求进程p的起始状态设置为暂停状态。
wake_up_new_task(p, clone_flags);//如果不是的话,我们唤醒进程p,但是不进行切换。
else
p->state = TASK_STOPPED;//如果是的话,我们把进程的state设置为TASK_STOPPED表示进程现在是暂停状态。
++total_forks;//这个是个全局变量,统计系统执行do_fork()函数次数的计数器。
if (unlikely (trace)) {如果确实要调试进程p
current->ptrace_message = pid;//我们使当前进程的ptrace_message=pid,将进程p的pid号作为父进程的调试信息。
ptrace_notify ((trace << 8) | SIGTRAP);
}
if (clone_flags & CLONE_VFORK) {//如果执行的是vfork()系统调用的话
wait_for_completion(&vfork);//在此等待vfork的完成
if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE))//如果要调试vfork
ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP);
}
} else {
free_pidmap(pid);
pid = PTR_ERR(p);//把p的错误号直接返回给pid
}
return pid;
}