分析Linux内核创建一个新进程的过程

task_struct数据结构

linux进程管理分析

In user space, the process is the process identifier (PID) said. From the user’s perspective, a PID is a numeric value, can uniquely identify a process. A PID does not change during the whole life process, but PID can be re used in the process is destroyed, so they are not always ideal cache.

In user space, to create the process can be used in several ways. You can execute a program (which leads to create a new process), also can be in within the program, called a fork or Exec System Call. Fork calls will lead to create a child process, and the exec call will use the new program instead of the current process context.

在用户空间中,每个进程都有一个独一无二的PID。从用户的角度来看,PID在整个进程过程中都不能够改变,但当进程被回收后,PID则可以再次被利用。

在用户态中创建一个进程有多种方法。

  1. 你可以运行一个程序,而程序就会自动创建一个进程。

  2. 也可以在一个进程中庸系统调用创建一个进程。例如:Fork可以创建一个子进程。

精简的task_struct:

Task_struct 在/linux/include/linux/sched.h.

Task_struct

{

  • Volatile long state;
    
  • Long counter;
    
  • Long priority
    
  • Unsigned long signals;  // pending sigs
    
  • Unsigned long blocked; //masked sigs
    
  • Int pid, pgrp, uid, euid, gid, egid;
    
  • Struct linux_binfmt;
    
  • Struct task_struct p_opptr;    // ptr to original parent
    
  • Struct task_struct p_pptr;     // ptr to immediate parent
    
  • Struct task_struct p_cptr;     // ptr to most recent child
    
  • Struct task_struct p_ysptr;    // ptr to following sibling
    
  • Struct task_struct p_osptr;    // ptr to previous sibling
    
  • Struct task_struct *next_task;  // in process list
    
  • Struct task_struct *prev_task;  // in process list
    
  • Struct task_struct *next_run; // in ready queue
    
  • Struct task_struct *prev_run; //in ready queue
    
  • Struct mm_struct  mm[1];
    
  • Unsigned long kernel_stack_page;
    
  • Unsigned long saved_kernel_stack;
    
  • Struct fs_struct fs[1];
    
  • Long utime, stime, cutime, cstime, start_time;
    
  • Struct sem_queue *semsleeping;
    
  • Struct wait_queue *wait_chldexit;
    
  • Struct sigaction sigaction[32];
    
  • Struct rlimit rlim[RLIM_NLIMITS];
    
  • Struct thread_struct tss;  // includes saved registers
    
  • Unsigned long policy;  // SCHED_FIFO,SCHED_RR,SCHED_OTHER
    
  • Unsigned long rt_priority;
    
  • // for SMPs
    
  • Int processor, last processor;
    
  • Int lock_depth;
    
  • }

进程的状态:

task_struct field:

long state;

5 States (see include/linux/sched.h):

  • #define TASK_RUNNING            0
    
  • #define TASK_INTERRUPTIBLE      1
    
  • #define TASK_UNINTERRUPTIBLE    2
    
  • #define TASK_ZOMBIE             4
    
  • #define TASK_STOPPED            8
    

进程的创建:

从该图可以看出do_fork是创建进程的基本函数。do_fork在/linux/kernel/fork.c,包括copy_process().

所以我们主要来看看do_fork:

在 Linux 内核中,供用户创建进程的系统调用fork()函数的响应函数是 sys_fork()、sys_clone()、sys_vfork()。这三个函数都是通过调用内核函数 do_fork() 来实现的。根据调用时所使用的 clone_flags 参数不同,do_fork() 函数完成的工作也各异。

Clone flags = CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_SIGHAND, CLONE_PID

/

/do_fork中的主要函数调用

do_fork(clone_flags, unsigned long usp, struct pt_regs *regs)

{

  • p = copy_process();//调用该函数用来设置进程的信息,包括进程描述、其他数据等
    
    
    :
    
  • wake_up_new_task(p, clone_flags);
    

}

参数说明:

clone_flags:这和 clone( )的flags参数相同。

stack_start:这也和clone()中的child_stack参数一样。

regs:Pointer to the values of the general purpose registers saved into the Kernel Mode stack when switching from User Mode to Kernel Mode.

stack_size:我查到的资料并不太明确知道这个参数具体是做什么的。通常将它设置为0.

parent_tidptr, child_tidptr:这和clone()中的ptid and ctid参数一样。

clone主要的flags:

CLONE_PTRACE:P.S.: If CLONE_PTRACE is specified, and the calling process is being traced, then trace the child also.

CLONE_STOPPED:强制子进程从TASK_STOPPED状态开始。

CLONE_UNTRACED:Set by the kernel to override the value of the CLONE_PTRACE flag

CLONE_VM:Shares the memory descriptor and all page tables

所调用到的clone()函数是为了复制进程的描述。而该函数的主要功能是:

The copy_process( ) function sets up

  • the process descriptor

  • any other kernel data structure required for a child’s execution.

  • Its parameters are the same as do_fork( ), plus the PID of the child.

static task_t *copy_process( )

{       :

p = dup_task_struct(current);

:

retval = copy_thread(0,clone_flags,…, regs);

:

sched_fork§;

:

}

==================================================================================================================

==================分割线==============================

好乱,我自己都看的好乱。。。

不管了,直接上实验截图了。一知半解的。

cd LinuxKernel

rm -rf menu

git clone https://github.com/mengning/menu.git

cd menu

mv test_fork.c test.c

make

结合之前查的资料,虽然实验有些失败,但是还是可以得出这样一个过程:

fork() -> sys_clone() -> do_fork() -> dup_task_struct() -> copy_process() -> copy_thread() -> ret_from_fork()

好吧不得不承认,这章我不懂。。。为了证书只能在这里瞎掰了。

sunfy + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

发表评论

电子邮件地址不会被公开。 必填项已用*标注