美文网首页
6进程的描述和进程的创建

6进程的描述和进程的创建

作者: 夏天的篮球 | 来源:发表于2017-03-31 14:45 被阅读0次

    安大大 + 原创作品转载请注明出处 + 《Linux操作系统分析》MOOC课程

    进程控制块PCB——task_struct

    为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息。


    进程的描述

    操作系统的三大功能:进程管理,内存管理和文件系统。最核心的是进程管理。

    进程描述符 task_struct数据结构

    理解部分在代码注释中

    struct task_struct {
        volatile long state;    /*运行状态 -1 unrunnable, 0 runnable, >0 stopped */
        void *stack;    //指定进程的内核堆栈 
        atomic_t usage;
        unsigned int flags; /* 每个进程的标识符  per process flags, defined below */
        unsigned int ptrace;
    
    #ifdef CONFIG_SMP
        //条件编译,多处理器用 
        struct llist_node wake_entry;
        int on_cpu;
        struct task_struct *last_wakee;
        unsigned long wakee_flips;
        unsigned long wakee_flip_decay_ts;
    
        int wake_cpu;
    #endif
        //下边一段是与优先级调度相关 ,在不同的环境下不同的调度方式和标识 
        int on_rq;  //运行队列run queue 
    
        int prio, static_prio, normal_prio;
        unsigned int rt_priority;
        const struct sched_class *sched_class;
        struct sched_entity se;
        struct sched_rt_entity rt;
    #ifdef CONFIG_CGROUP_SCHED
        struct task_group *sched_task_group;
    #endif
        struct sched_dl_entity dl;
    
    #ifdef CONFIG_PREEMPT_NOTIFIERS
        /* list of struct preempt_notifier: */
        struct hlist_head preempt_notifiers;
    #endif
    
    #ifdef CONFIG_BLK_DEV_IO_TRACE
        unsigned int btrace_seq;
    #endif
    
        unsigned int policy;
        int nr_cpus_allowed;
        cpumask_t cpus_allowed;
    
    #ifdef CONFIG_PREEMPT_RCU
        int rcu_read_lock_nesting;
        union rcu_special rcu_read_unlock_special;
        struct list_head rcu_node_entry;
    #endif /* #ifdef CONFIG_PREEMPT_RCU */
    #ifdef CONFIG_TREE_PREEMPT_RCU
        struct rcu_node *rcu_blocked_node;
    #endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
    #ifdef CONFIG_TASKS_RCU
        unsigned long rcu_tasks_nvcsw;
        bool rcu_tasks_holdout;
        struct list_head rcu_tasks_holdout_list;
        int rcu_tasks_idle_cpu;
    #endif /* #ifdef CONFIG_TASKS_RCU */
    
    #if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT)
        struct sched_info sched_info;
    #endif
    
        struct list_head tasks; //进程的链表,当前所有的进程都用链表连起来,是一个双向循环链表 
    #ifdef CONFIG_SMP
        struct plist_node pushable_tasks;
        struct rb_node pushable_dl_tasks;
    #endif
    
        struct mm_struct *mm, *active_mm;//和内存管理,进程的地址空间相关,每个进程的代码段,数据段都和这个相关 
    #ifdef CONFIG_COMPAT_BRK
        unsigned brk_randomized:1;
    #endif
        /* per-thread vma caching */
        u32 vmacache_seqnum;
        struct vm_area_struct *vmacache[VMACACHE_SIZE];
    #if defined(SPLIT_RSS_COUNTING)
        struct task_rss_stat    rss_stat;
    #endif
    /* task state 任务的状态*/
        int exit_state;
        int exit_code, exit_signal;
        int pdeath_signal;  /*  The signal sent when the parent dies  */
        unsigned int jobctl;    /* JOBCTL_*, siglock protected */
    
        /* Used for emulating ABI behavior of previous Linux versions */
        unsigned int personality;
    
        unsigned in_execve:1;   /* Tell the LSMs that the process is doing an
                     * execve */
        unsigned in_iowait:1;
    
        /* Revert to default priority/policy when forking */
        unsigned sched_reset_on_fork:1;
        unsigned sched_contributes_to_load:1;
    
        unsigned long atomic_flags; /* Flags needing atomic access. */
    
        pid_t pid;  //进程的pid,来标识某一个进程 
        pid_t tgid;
    
    #ifdef CONFIG_CC_STACKPROTECTOR
        /* Canary value for the -fstack-protector gcc feature */
        unsigned long stack_canary;
    #endif
        /* 下边是进程的父子关系管理,都是通过双向链表链接的 
         * pointers to (original) parent process, youngest child, younger sibling,
         * older sibling, respectively.  (p->father can be replaced with
         * p->real_parent->pid)
         */
        struct task_struct __rcu *real_parent; /* real parent process */
        struct task_struct __rcu *parent; /* recipient of SIGCHLD, wait4() reports */
        /*
         * children/sibling forms the list of my natural children
         */
        struct list_head children;  /* list of my children */
        struct list_head sibling;   /* linkage in my parent's children list */
        struct task_struct *group_leader;   /* threadgroup leader */
    
        /*
         * ptraced is the list of tasks this task is using ptrace on.
         * This includes both natural children and PTRACE_ATTACH targets.
         * p->ptrace_entry is p's link on the p->parent->ptraced list.
         */
        struct list_head ptraced;   //调试用 
        struct list_head ptrace_entry;
    
        /* PID/PID hash table linkage. */
        struct pid_link pids[PIDTYPE_MAX];  //pid的哈希表,用来方便查找 
        struct list_head thread_group;
        struct list_head thread_node;
    
        struct completion *vfork_done;      /* for vfork() */
        int __user *set_child_tid;      /* CLONE_CHILD_SETTID */
        int __user *clear_child_tid;        /* CLONE_CHILD_CLEARTID */
     
        cputime_t utime, stime, utimescaled, stimescaled;   //下边是和时间相关的代码
        cputime_t gtime;
    #ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
        struct cputime prev_cputime;
    #endif
    #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
        seqlock_t vtime_seqlock;
        unsigned long long vtime_snap;
        enum {
            VTIME_SLEEPING = 0,
            VTIME_USER,
            VTIME_SYS,
        } vtime_snap_whence;
    #endif
        unsigned long nvcsw, nivcsw; /* context switch counts */
        u64 start_time;     /* monotonic time in nsec */
        u64 real_start_time;    /* boot based time in nsec */
    /* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
        unsigned long min_flt, maj_flt;
    
        struct task_cputime cputime_expires;
        struct list_head cpu_timers[3];
    
    /* process credentials */
        const struct cred __rcu *real_cred; /* objective and real subjective task
                         * credentials (COW) */
        const struct cred __rcu *cred;  /* effective (overridable) subjective task
                         * credentials (COW) */
        char comm[TASK_COMM_LEN]; /* executable name excluding path
                         - access with [gs]et_task_comm (which lock
                           it with task_lock())
                         - initialized normally by setup_new_exec */
    /* file system info */
        int link_count, total_link_count;
    #ifdef CONFIG_SYSVIPC
    /* ipc stuff */
        struct sysv_sem sysvsem;
        struct sysv_shm sysvshm;
    #endif
    #ifdef CONFIG_DETECT_HUNG_TASK
    /* hung task detection */
        unsigned long last_switch_count;
    #endif
    /* CPU-specific state of this task 和CPU相关的状态,进程上下文切换时起着关键作用*/
        struct thread_struct thread;
    /* filesystem information 文件系统相关的数据结构*/
        struct fs_struct *fs;
    /* open file information 打开的文件描述符列表*/
        struct files_struct *files;
    /* namespaces */
        struct nsproxy *nsproxy;
    /* signal handlers 和信号处理相关的*/
        struct signal_struct *signal;
        struct sighand_struct *sighand;
    
        sigset_t blocked, real_blocked;
        sigset_t saved_sigmask; /* restored if set_restore_sigmask() was used */
        struct sigpending pending;
    
        unsigned long sas_ss_sp;
        size_t sas_ss_size;
        int (*notifier)(void *priv);
        void *notifier_data;
        sigset_t *notifier_mask;
        struct callback_head *task_works;
    
        struct audit_context *audit_context;
    #ifdef CONFIG_AUDITSYSCALL
        kuid_t loginuid;
        unsigned int sessionid;
    #endif
        struct seccomp seccomp;
    
    /* Thread group tracking */
        u32 parent_exec_id;
        u32 self_exec_id;
    /* Protection of (de-)allocation: mm, files, fs, tty, keyrings, mems_allowed,
     * mempolicy */
        spinlock_t alloc_lock;
    
        /* Protection of the PI data structures: */
        raw_spinlock_t pi_lock;
    
    #ifdef CONFIG_RT_MUTEXES    //mutexes互斥锁,互斥器 
        /* PI waiters blocked on a rt_mutex held by this task */
        struct rb_root pi_waiters;
        struct rb_node *pi_waiters_leftmost;
        /* Deadlock detection and priority inheritance handling */
        struct rt_mutex_waiter *pi_blocked_on;
    #endif
    
    #ifdef CONFIG_DEBUG_MUTEXES
        /* mutex deadlock detection */
        struct mutex_waiter *blocked_on;
    #endif
    #ifdef CONFIG_TRACE_IRQFLAGS    //和调试相关 
        unsigned int irq_events;
        unsigned long hardirq_enable_ip;
        unsigned long hardirq_disable_ip;
        unsigned int hardirq_enable_event;
        unsigned int hardirq_disable_event;
        int hardirqs_enabled;
        int hardirq_context;
        unsigned long softirq_disable_ip;
        unsigned long softirq_enable_ip;
        unsigned int softirq_disable_event;
        unsigned int softirq_enable_event;
        int softirqs_enabled;
        int softirq_context;
    #endif
    #ifdef CONFIG_LOCKDEP
    # define MAX_LOCK_DEPTH 48UL
        u64 curr_chain_key;
        int lockdep_depth;
        unsigned int lockdep_recursion;
        struct held_lock held_locks[MAX_LOCK_DEPTH];
        gfp_t lockdep_reclaim_gfp;
    #endif
    
    /* journalling filesystem info */
        void *journal_info;
    
    /* stacked block device info */
        struct bio_list *bio_list;
    
    #ifdef CONFIG_BLOCK
    /* stack plugging */
        struct blk_plug *plug;
    #endif
    
    /* VM state */
        struct reclaim_state *reclaim_state;
    
        struct backing_dev_info *backing_dev_info;
    
        struct io_context *io_context;
    
        unsigned long ptrace_message;
        siginfo_t *last_siginfo; /* For ptrace use.  */
        struct task_io_accounting ioac;
    #if defined(CONFIG_TASK_XACCT)
        u64 acct_rss_mem1;  /* accumulated rss usage */
        u64 acct_vm_mem1;   /* accumulated virtual memory usage */
        cputime_t acct_timexpd; /* stime + utime since last update */
    #endif
    #ifdef CONFIG_CPUSETS
        nodemask_t mems_allowed;    /* Protected by alloc_lock */
        seqcount_t mems_allowed_seq;    /* Seqence no to catch updates */
        int cpuset_mem_spread_rotor;
        int cpuset_slab_spread_rotor;
    #endif
    #ifdef CONFIG_CGROUPS
        /* Control Group info protected by css_set_lock */
        struct css_set __rcu *cgroups;
        /* cg_list protected by css_set_lock and tsk->alloc_lock */
        struct list_head cg_list;
    #endif
    #ifdef CONFIG_FUTEX
        struct robust_list_head __user *robust_list;
    #ifdef CONFIG_COMPAT
        struct compat_robust_list_head __user *compat_robust_list;
    #endif
        struct list_head pi_state_list;
        struct futex_pi_state *pi_state_cache;
    #endif
    #ifdef CONFIG_PERF_EVENTS
        struct perf_event_context *perf_event_ctxp[perf_nr_task_contexts];
        struct mutex perf_event_mutex;
        struct list_head perf_event_list;
    #endif
    #ifdef CONFIG_DEBUG_PREEMPT
        unsigned long preempt_disable_ip;
    #endif
    #ifdef CONFIG_NUMA
        struct mempolicy *mempolicy;    /* Protected by alloc_lock */
        short il_next;
        short pref_node_fork;
    #endif
    #ifdef CONFIG_NUMA_BALANCING
        int numa_scan_seq;
        unsigned int numa_scan_period;
        unsigned int numa_scan_period_max;
        int numa_preferred_nid;
        unsigned long numa_migrate_retry;
        u64 node_stamp;         /* migration stamp  */
        u64 last_task_numa_placement;
        u64 last_sum_exec_runtime;
        struct callback_head numa_work;
    
        struct list_head numa_entry;
        struct numa_group *numa_group;
    
        /*
         * Exponential decaying average of faults on a per-node basis.
         * Scheduling placement decisions are made based on the these counts.
         * The values remain static for the duration of a PTE scan
         */
        unsigned long *numa_faults_memory;
        unsigned long total_numa_faults;
    
        /*
         * numa_faults_buffer records faults per node during the current
         * scan window. When the scan completes, the counts in
         * numa_faults_memory decay and these values are copied.
         */
        unsigned long *numa_faults_buffer_memory;
    
        /*
         * Track the nodes the process was running on when a NUMA hinting
         * fault was incurred.
         */
        unsigned long *numa_faults_cpu;
        unsigned long *numa_faults_buffer_cpu;
    
        /*
         * numa_faults_locality tracks if faults recorded during the last
         * scan window were remote/local. The task scan period is adapted
         * based on the locality of the faults with different weights
         * depending on whether they were shared or private faults
         */
        unsigned long numa_faults_locality[2];
    
        unsigned long numa_pages_migrated;
    #endif /* CONFIG_NUMA_BALANCING */
    
        struct rcu_head rcu;
    
        /*
         * cache last used pipe for splice  和管道相关的 
         */
        struct pipe_inode_info *splice_pipe;
    
        struct page_frag task_frag;
    
    #ifdef  CONFIG_TASK_DELAY_ACCT
        struct task_delay_info *delays;
    #endif
    #ifdef CONFIG_FAULT_INJECTION
        int make_it_fail;
    #endif
        /*
         * when (nr_dirtied >= nr_dirtied_pause), it's time to call
         * balance_dirty_pages() for some dirty throttling pause
         */
        int nr_dirtied;
        int nr_dirtied_pause;
        unsigned long dirty_paused_when; /* start of a write-and-pause period */
    
    #ifdef CONFIG_LATENCYTOP
        int latency_record_count;
        struct latency_record latency_record[LT_SAVECOUNT];
    #endif
        /*
         * time slack values; these are used to round up poll() and
         * select() etc timeout values. These are in nanoseconds.
         */
        unsigned long timer_slack_ns;
        unsigned long default_timer_slack_ns;
    
    #ifdef CONFIG_FUNCTION_GRAPH_TRACER
        /* Index of current stored address in ret_stack */
        int curr_ret_stack;
        /* Stack of return addresses for return function tracing */
        struct ftrace_ret_stack *ret_stack;
        /* time stamp for last schedule */
        unsigned long long ftrace_timestamp;
        /*
         * Number of functions that haven't been traced
         * because of depth overrun.
         */
        atomic_t trace_overrun;
        /* Pause for the tracing */
        atomic_t tracing_graph_pause;
    #endif
    #ifdef CONFIG_TRACING
        /* state flags for use by tracers */
        unsigned long trace;
        /* bitmask and counter of trace recursion */
        unsigned long trace_recursion;
    #endif /* CONFIG_TRACING */
    #ifdef CONFIG_MEMCG /* memcg uses this to do batch job */
        unsigned int memcg_kmem_skip_account;
        struct memcg_oom_info {
            struct mem_cgroup *memcg;
            gfp_t gfp_mask;
            int order;
            unsigned int may_oom:1;
        } memcg_oom;
    #endif
    #ifdef CONFIG_UPROBES
        struct uprobe_task *utask;
    #endif
    #if defined(CONFIG_BCACHE) || defined(CONFIG_BCACHE_MODULE)
        unsigned int    sequential_io;
        unsigned int    sequential_io_avg;
    #endif
    };
    

    当前任务的CPU相关的状态,它在进程上下文切换的时候起着关键的作用,其中有sp、ip以及其它的和CPU相关的状态:

    struct thread_struct {
        /* Cached TLS descriptors: */
        struct desc_struct  tls_array[GDT_ENTRY_TLS_ENTRIES];
        unsigned long       sp0;
        unsigned long       sp;
    #ifdef CONFIG_X86_32
        unsigned long       sysenter_cs;
    #else
        unsigned long       usersp; /* Copy from PDA */
        unsigned short      es;
        unsigned short      ds;
        unsigned short      fsindex;
        unsigned short      gsindex;
    #endif
    #ifdef CONFIG_X86_32
        unsigned long       ip;
    #endif
    #ifdef CONFIG_X86_64
        unsigned long       fs;
    #endif
        unsigned long       gs;
        /* Save middle states of ptrace breakpoints */
        struct perf_event   *ptrace_bps[HBP_NUM];
        /* Debug status used for traps, single steps, etc... */
        unsigned long           debugreg6;
        /* Keep track of the exact dr7 value set by the user */
        unsigned long           ptrace_dr7;
        /* Fault info: */
        unsigned long       cr2;
        unsigned long       trap_nr;
        unsigned long       error_code;
        /* floating point and extended processor state */
        struct fpu      fpu;
    #ifdef CONFIG_X86_32
        /* Virtual 86 mode info */
        struct vm86_struct __user *vm86_info;
        unsigned long       screen_bitmap;
        unsigned long       v86flags;
        unsigned long       v86mask;
        unsigned long       saved_sp0;
        unsigned int        saved_fs;
        unsigned int        saved_gs;
    #endif
        /* IO permissions: */
        unsigned long       *io_bitmap_ptr;
        unsigned long       iopl;
        /* Max allowed port in the bitmap, in bytes: */
        unsigned        io_bitmap_max;
        /*
         * fpu_counter contains the number of consecutive context switches
         * that the FPU is used. If this is over a threshold, the lazy fpu
         * saving becomes unlazy to save the trap. This is an unsigned char
         * so that after 256 times the counter wraps and the behavior turns
         * lazy again; this to deal with bursty apps that only use FPU for
         * a short time
         */
        unsigned char fpu_counter;
    };
    

    进程的创建

    fork一个子进程的代码。在MenuOS里fork调用的是sys_clone,但是最终都是调用的do_fork.

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    int main(int argc, char * argv[])
    {
        int pid;
        /* fork another process */
        pid = fork();
        if (pid < 0) 
        { 
            /* error occurred */
            fprintf(stderr,"Fork Failed!");
            exit(-1);
        } 
        else if (pid == 0) 
        {
            /* child process */
            printf("This is Child Process!\n");
        } 
        else 
        {  
            /* parent process  */
            printf("This is Parent Process!\n");
            /* parent will wait for the child to complete*/
            wait(NULL);
            printf("Child Complete!\n");
        }
    }
    

    创建一个新进程在内核中的执行过程

    • fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建;
    • Linux通过复制父进程来创建一个新进程,那么这就给我们理解这一个过程提供一个想象的框架:
      • 复制一个PCB——task_struct
    err = arch_dup_task_struct(tsk, orig);//复制父进程的task_struct数据结构
    
    • 要给新进程分配一个新的内核堆栈
    ti = alloc_thread_info_node(tsk, node);
    tsk->stack = ti;//分配内核堆栈
    setup_thread_stack(tsk, orig); //这里只是复制thread_info,而非复制内核堆栈
    
    • 要修改复制过来的进程数据,比如pid、进程链表等等都要改改吧,见copy_process内部。
    • 从用户态的代码看fork();函数返回了两次,即在父子进程中各返回一次,父进程从系统调用中返回比较容易理解,子进程从系统调用中返回,那它在系统调用处理过程中的哪里开始执行的呢?这就涉及子进程的内核堆栈数据状态和task_struct中thread记录的sp和ip的一致性问题,这是在哪里设定的?copy_thread in copy_process
    *childregs = *current_pt_regs(); //复制内核堆栈
    childregs->ax = 0; //为什么子进程的fork返回0,这里就是原因!
     
    p->thread.sp = (unsigned long) childregs; //调度到子进程时的内核栈顶
    p->thread.ip = (unsigned long) ret_from_fork; //调度到子进程时的第一条指令地址
    

    进程的创建概览及fork一个进程的用户态代码


    创建新进程是通过复制当前进程来实现的

    进程的创建是复制当前进程的进程信息,复制出来一个进程,即fork出一个进程。这个新创建出来的子进程和父进程的绝大部分信息是一样的,但是也有不同的,比如说pid,链表,内核堆栈,记录ip、sp的thread等。

    设想创建新进程的过程中需要做哪些事

    父进程创建子进程,会在一个地方复制父进程的PCB(task_struct),同时还有很多地方修改这个PCB,因为子进程有自己独立的信息,还会有一个地方分配一个新的内核堆栈。因为子进程是fork返回到用户态,所以它的内核堆栈中的一部分也要从父进程中拷贝过来,否则内核堆栈无法返回。此外,根据拷贝的内核堆栈的状况,设定自己的eip和esp的位置,如果位置不对,出栈的时候,执行到iret时,会和堆栈不一致。


    系统调用内核处理函数sys_fork,sys_clone,sys_vfork

    区别

    linux-3.18.6/kernel/fork.c

    #ifdef __ARCH_WANT_SYS_FORK
    SYSCALL_DEFINE0(fork)
    {
    #ifdef CONFIG_MMU
        return do_fork(SIGCHLD, 0, 0, NULL, NULL);
    #else
        /* can not support in nommu mode */
        return -EINVAL;
    #endif
    }
    #endif
    
    #ifdef __ARCH_WANT_SYS_VFORK
    SYSCALL_DEFINE0(vfork)
    {
        return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, 0,
                0, NULL, NULL);
    }
    #endif
    

    几种带不同参数的clone:

    #ifdef __ARCH_WANT_SYS_CLONE
    #ifdef CONFIG_CLONE_BACKWARDS
    SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
             int __user *, parent_tidptr,
             int, tls_val,
             int __user *, child_tidptr)
    #elif defined(CONFIG_CLONE_BACKWARDS2)
    SYSCALL_DEFINE5(clone, unsigned long, newsp, unsigned long, clone_flags,
             int __user *, parent_tidptr,
             int __user *, child_tidptr,
             int, tls_val)
    #elif defined(CONFIG_CLONE_BACKWARDS3)
    SYSCALL_DEFINE6(clone, unsigned long, clone_flags, unsigned long, newsp,
            int, stack_size,
            int __user *, parent_tidptr,
            int __user *, child_tidptr,
            int, tls_val)
    #else
    SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
             int __user *, parent_tidptr,
             int __user *, child_tidptr,
             int, tls_val)
    #endif
    {
        return do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr);
    }
    #endif
    

    可以看到在用户态不管调用这三个系统调用中的哪一个,最后都调用了do_fork。
    do_fork:

    /*
     *  Ok, this is the main fork-routine.//fork的主要的处理例程 
     *
     * It copies the process, and if successful kick-starts
     * it and waits for it to finish using the VM if required.
     */
    long do_fork(unsigned long clone_flags,
              unsigned long stack_start,
              unsigned long stack_size,
              int __user *parent_tidptr,
              int __user *child_tidptr)
    {
        struct task_struct *p;
        int trace = 0;
        long nr;
    
        /*
         * Determine whether and which event to report to ptracer.  When
         * called from kernel_thread or CLONE_UNTRACED is explicitly
         * requested, no event is reported; otherwise, report if the event
         * for the type of forking is enabled.
         */
        if (!(clone_flags & CLONE_UNTRACED)) {
            if (clone_flags & CLONE_VFORK)
                trace = PTRACE_EVENT_VFORK;
            else if ((clone_flags & CSIGNAL) != SIGCHLD)
                trace = PTRACE_EVENT_CLONE;
            else
                trace = PTRACE_EVENT_FORK;
    
            if (likely(!ptrace_event_enabled(current, trace)))
                trace = 0;
        }
    
        p = copy_process(clone_flags, stack_start, stack_size,
                 child_tidptr, NULL, trace);            //创建一个进程内容的主要代码 
        /*
         * Do this prior waking up the new thread - the thread pointer
         * might get invalid after that point, if the thread exits quickly.
         */
        if (!IS_ERR(p)) {
            struct completion vfork;
            struct pid *pid;
    
            trace_sched_process_fork(current, p);
    
            pid = get_task_pid(p, PIDTYPE_PID);
            nr = pid_vnr(pid);
    
            if (clone_flags & CLONE_PARENT_SETTID)
                put_user(nr, parent_tidptr);
    
            if (clone_flags & CLONE_VFORK) {
                p->vfork_done = &vfork;
                init_completion(&vfork);
                get_task_struct(p);
            }
    
            wake_up_new_task(p);
    
            /* forking complete and child started to run, tell ptracer */
            if (unlikely(trace))
                ptrace_event_pid(trace, pid);
    
            if (clone_flags & CLONE_VFORK) {
                if (!wait_for_vfork_done(p, &vfork))
                    ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid);
            }
    
            put_pid(pid);
        } else {
            nr = PTR_ERR(p);
        }
        return nr;
    }
    
    static struct task_struct *copy_process(unsigned long clone_flags,
                        unsigned long stack_start,
                        unsigned long stack_size,
                        int __user *child_tidptr,
                        struct pid *pid,
                        int trace)
    {
        ......
        出错处理
        ......
        p = dup_task_struct(current);//复制task_struct,p指向了子进程的进程描述符
        ......
        对子进程的初始化,修改
        ......
        /* copy all the process information */
        shm_init_task(p);
        retval = copy_semundo(clone_flags, p);
        if (retval)
            goto bad_fork_cleanup_audit;
        retval = copy_files(clone_flags, p);//初始化file 
        if (retval)
            goto bad_fork_cleanup_semundo;
        retval = copy_fs(clone_flags, p);//初始化文件系统 
        if (retval)
            goto bad_fork_cleanup_files;
        retval = copy_sighand(clone_flags, p);
        if (retval)
            goto bad_fork_cleanup_fs;
        retval = copy_signal(clone_flags, p);
        if (retval)
            goto bad_fork_cleanup_sighand;
        retval = copy_mm(clone_flags, p);//初始化内存 
        if (retval)
            goto bad_fork_cleanup_signal;
        retval = copy_namespaces(clone_flags, p);
        if (retval)
            goto bad_fork_cleanup_mm;
        retval = copy_io(clone_flags, p);//初始化io 
        if (retval)
            goto bad_fork_cleanup_namespaces;
        retval = copy_thread(clone_flags, stack_start, stack_size, p);//拷贝内核堆栈数据和指定新进程的第一条指令地址 
        if (retval)
            goto bad_fork_cleanup_io;
    
        if (pid != &init_struct_pid) {
            retval = -ENOMEM;
            pid = alloc_pid(p->nsproxy->pid_ns_for_children);
            if (!pid)
                goto bad_fork_cleanup_io;
        }
    
        p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
        ......
    }
    
    static struct task_struct *dup_task_struct(struct task_struct *orig)
    {
        struct task_struct *tsk;
        struct thread_info *ti;
        int node = tsk_fork_get_node(orig);
        int err;
    
        tsk = alloc_task_struct_node(node);//alloc一个结点 
        if (!tsk)
            return NULL;
    
        ti = alloc_thread_info_node(tsk, node);//分配内核堆栈空间的效果 
        if (!ti)
            goto free_tsk;
    
        err = arch_dup_task_struct(tsk, orig);//执行了复制
        if (err)
            goto free_ti;
    
        tsk->stack = ti;
    #ifdef CONFIG_SECCOMP
        /*
         * We must handle setting up seccomp filters once we're under
         * the sighand lock in case orig has changed between now and
         * then. Until then, filter must be NULL to avoid messing up
         * the usage counts on the error path calling free_task.
         */
        tsk->seccomp.filter = NULL;
    #endif
    
        setup_thread_stack(tsk, orig);//复制task_thread_info信息 
        clear_user_return_notifier(tsk);
        clear_tsk_need_resched(tsk);
        set_task_stack_end_magic(tsk);
    
    #ifdef CONFIG_CC_STACKPROTECTOR
        tsk->stack_canary = get_random_int();
    #endif
    
        /*
         * One for us, one for whoever does the "release_task()" (usually
         * parent)
         */
        atomic_set(&tsk->usage, 2);
    #ifdef CONFIG_BLK_DEV_IO_TRACE
        tsk->btrace_seq = 0;
    #endif
        tsk->splice_pipe = NULL;
        tsk->task_frag.page = NULL;
    
        account_kernel_stack(ti, 1);
    
        return tsk;
    
    free_ti:
        free_thread_info(ti);
    free_tsk:
        free_task_struct(tsk);
        return NULL;
    }
    
    int __weak arch_dup_task_struct(struct task_struct *dst,
                               struct task_struct *src)
    {
        *dst = *src;
        return 0;
    }
    
    /*
     * Allocate pages if THREAD_SIZE is >= PAGE_SIZE, otherwise use a
     * kmemcache based allocator.
     */
    # if THREAD_SIZE >= PAGE_SIZE
    static struct thread_info *alloc_thread_info_node(struct task_struct *tsk,
                              int node)
    {
        //创建了两个页,一个存放thread_info,另一部分从高地址指向低地址的内核堆栈 
        struct page *page = alloc_kmem_pages_node(node, THREADINFO_GFP,
                              THREAD_SIZE_ORDER);
    
        return page ? page_address(page) : NULL;
    }
    

    创建的新进程是从哪里开始执行的

    int copy_thread(unsigned long clone_flags, unsigned long sp,
        unsigned long arg, struct task_struct *p)
    {
        struct pt_regs *childregs = task_pt_regs(p);//pt_regs
        struct task_struct *tsk;
        int err;
    
        p->thread.sp = (unsigned long) childregs;
        p->thread.sp0 = (unsigned long) (childregs+1);
        memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps));
    
        if (unlikely(p->flags & PF_KTHREAD)) {
            /* kernel thread */
            memset(childregs, 0, sizeof(struct pt_regs));
            p->thread.ip = (unsigned long) ret_from_kernel_thread;
            task_user_gs(p) = __KERNEL_STACK_CANARY;
            childregs->ds = __USER_DS;
            childregs->es = __USER_DS;
            childregs->fs = __KERNEL_PERCPU;
            childregs->bx = sp; /* function */
            childregs->bp = arg;
            childregs->orig_ax = -1;
            childregs->cs = __KERNEL_CS | get_kernel_rpl();
            childregs->flags = X86_EFLAGS_IF | X86_EFLAGS_FIXED;
            p->thread.io_bitmap_ptr = NULL;
            return 0;
        }
        *childregs = *current_pt_regs();
        childregs->ax = 0;
        if (sp)
            childregs->sp = sp;
    
        p->thread.ip = (unsigned long) ret_from_fork;
        task_user_gs(p) = get_user_gs(current_pt_regs());
    
        p->thread.io_bitmap_ptr = NULL;
        tsk = current;
        err = -ENOMEM;
    
        if (unlikely(test_tsk_thread_flag(tsk, TIF_IO_BITMAP))) {
            p->thread.io_bitmap_ptr = kmemdup(tsk->thread.io_bitmap_ptr,
                            IO_BITMAP_BYTES, GFP_KERNEL);
            if (!p->thread.io_bitmap_ptr) {
                p->thread.io_bitmap_max = 0;
                return -ENOMEM;
            }
            set_tsk_thread_flag(p, TIF_IO_BITMAP);
        }
    
        err = 0;
    
        /*
         * Set a new TLS for the child thread?
         */
        if (clone_flags & CLONE_SETTLS)
            err = do_set_thread_area(p, -1,
                (struct user_desc __user *)childregs->si, 0);
    
        if (err && p->thread.io_bitmap_ptr) {
            kfree(p->thread.io_bitmap_ptr);
            p->thread.io_bitmap_max = 0;
        }
        return err;
    }
    

    int指令和SAVE_ALL压到内核堆栈的内容
    复制内核堆栈只复制下边这些

    struct pt_regs {
        unsigned long bx;
        unsigned long cx;
        unsigned long dx;
        unsigned long si;
        unsigned long di;
        unsigned long bp;
        unsigned long ax;//传递的系统调用号 
        unsigned long ds;
        unsigned long es;
        unsigned long fs;
        unsigned long gs;
        unsigned long orig_ax;
        unsigned long ip;
        unsigned long cs;
        unsigned long flags;
        unsigned long sp;
        unsigned long ss;
    };
    
    ENTRY(ret_from_fork)
        CFI_STARTPROC
        pushl_cfi %eax
        call schedule_tail
        GET_THREAD_INFO(%ebp)
        popl_cfi %eax
        pushl_cfi $0x0202       # Reset kernel eflags
        popfl_cfi
        jmp syscall_exit      #会跳转到system_call里边的syscall_exit,继续向下进行
        CFI_ENDPROC
    END(ret_from_fork)
    
    ENTRY(system_call)
        RING0_INT_FRAME         # can't unwind into user space anyway
        ASM_CLAC
        pushl_cfi %eax          # save orig_eax
        SAVE_ALL
        GET_THREAD_INFO(%ebp)
                        # system call tracing in operation / emulation
        testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
        jnz syscall_trace_entry
        cmpl $(NR_syscalls), %eax
        jae syscall_badsys
    syscall_call:
        call *sys_call_table(,%eax,4)
    syscall_after_call:
        movl %eax,PT_EAX(%esp)      # store the return value
    syscall_exit:
        LOCKDEP_SYS_EXIT
        DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt
                        # setting need_resched or sigpending
                        # between sampling and the iret
        TRACE_IRQS_OFF
        movl TI_flags(%ebp), %ecx
        testl $_TIF_ALLWORK_MASK, %ecx  # current->work
        jne syscall_exit_work
    
    restore_all:
        TRACE_IRQS_IRET
    restore_all_notrace:
    

    参考这个"关于kernel_thread的补充说明"分析的很透彻


    使用gdb跟踪创建新进程的过程

    在MenuOS里增加一个fork命令,用带有fork的test_fork.c把test.c给覆盖掉



    编译之后,可以看到列表中多了fork命令。执行fork命令之后,它会创建一个子进程。父进程和子进程都输出了信息。



    使用gdb跟踪:

    设置几个断点:


    继续执行,执行到了do_fork


    相关文章

      网友评论

          本文标题:6进程的描述和进程的创建

          本文链接:https://www.haomeiwen.com/subject/vakvottx.html