精华内容
下载资源
问答
  • 创建进程的大致过程
    千次阅读
    2019-04-15 22:58:41

    写时拷贝

    传统的fork系统调用直接把所有的资源复制给新创建的进程,但是这种实现过于简单,效率低下,因为并不支持拷贝数据的共享。

    更糟的是如果新进程打算立即执行一个新的映像那么所有的拷贝都将前功尽弃。

    Linux下面的fork采用的是写时拷贝的方法,也就是说让父进程与子进程拥有同一份拷贝,之后如果发生了写入数据的情况。再也根本不会被写入的情况下,它就不会复制了。例如直接调用了exec()

    vfork函数其实在系统调用的时候步骤和fork一致但是,它不会拷贝父进程的页表项,子进程作为父进程的一个单独的线程执行。子进程运行在自己的内存空间内,父进程会在它执行的时候阻塞,知道子进程退出或执行exec

    创建进程的详细步骤

    fork函数是通过clone系统来完成创建进程的。这个调用来标识一下父进程和子进程之间共享的资源。

    1. 调用dup_task_struct()来创建一个内核栈,thread_info,task_struct等结构,一开始的时候其实父进程和子进程里面的资源和里都是一样的,此时子进程与父进程的进程描述符完全一致
    2. check 检查当前进程是否已经超过了系统的最大文件描述符
    3. 清除子描述符里面一些内容,使它与父进程分开。但是进程描述符不需要去改变就是直接复制就好了,因为主要起的是描述作用,而不是记录的作用
    4. 设置标志位,TASK_UNINTERRUPTIBLE 来保证该进程不会被投入使用
    5. copy-flags()用来更新struct_task的一些flags成员,
    6. get_pid()给进程一个pid号
    7. 根据传递给clone的参数标志,copy_process函数拷贝或共享打开文件,文件系统信息等、信号处理函数、进程地址空间和命名空间
    8. 让父进程和子进程平分剩余的时间片
    9. 最后copy_process会返回一个指向子进程的指针

    线程在Linux内部的实现

    其实大体上与进程差不多也就有一下几点区别

    同一程序内共享内存地址空间运行一组线程,这些线程可以共享打开的文件和其他资源,支持并发程序设计技术(多处理器)

    线程父子共享内存空间,文件系统资源,文件描述符信号处理程序等。

    内核线程

    • 内核线程没有独立的地址空间,只在内核运行,并且只能从来不切换到用户空间去。
    • 可以被调度和抢占
    • 只能被其他的内核线程创建

    进程终结

    1. 设置task_struct成员为PF_EXITING
    2. 使用del_tim_sync删除该进程的内核定时器
    3. 放弃内存使用的mm_struct,如果没有其他进程使用就彻底的放弃它(没有人在和它共享资源)
    4. 在IPC队列中删除掉这个进程的存在
    5. 递减文件描述符,文件系统数据、进程空间名字和信号处理函数的引用计数
    6. 将任务退出代码发给父进程存放
    7. 向父进程发送信号,将子进程的父进程重新设置为线程组中的其他线程或者调用init,把进程设置为TASK_ZOMBIE
    8. 切换到其他进程

     

    更多相关内容
  • 本文主要介绍Linux内核创建一个新进程过程,需要的朋友可以参考下。
  • 主要为大家深入浅出的介绍了Linux内核创建进程的全过程,感兴趣的小伙伴们可以参考一下
  • 进程线程创建过程

    2020-12-09 19:31:05
    一、进程创建过程 所有进程都通过 PspCreateProcess 函数创建,包括 System 进程。它被三个函数调用,分别是NtCreateProcessEx、PsCreateSystemProcess 和 PspInitPhase0 。 NtCreateProcessEx 是 CreateProcess 的...

    一、进程创建过程

    所有进程都通过 PspCreateProcess 函数创建,包括 System 进程。它被三个函数调用,分别是NtCreateProcessEx、PsCreateSystemProcess 和 PspInitPhase0 。

    NtCreateProcessEx 是 CreateProcess 的内核服务;

    PspInitPhase0 函数是系统初始化早期调用的,它创建了 System 进程,System 进程的句柄保存在全局变量 PspInitialSystemProcessHandle 中,EPROCESS 保存在 PsInitialSystemProcess 中;

    PsCreateSystemProcess 是用来创建系统进程的,它创建的进程都是 PsInitialSystemProcess 的子进程。

    PspCreateProcess 的大致流程在136-140页分析了。这里简单介绍一下,完整代码放在文末。

    • 创建进程对象
    • 初始化进程对象
    • 初始化内存区对象 SectionObject
    • 初始化调试端口
    • 初始化异常端口
    • 创建进程地址空间
    • 初始化进程基本优先级,CPU亲和性,页目录基址,超空间页帧号
    • 初始化进程安全属性(从父进程复制令牌)
    • 设置优先级类别
    • 初始化句柄表
    • 初始化进程地址空间
    • 创建进程ID
    • 审计此次进程创建行为
    • 作业相关操作
    • 创建或复制PEB
    • 新进程对象插入 PsActiveProcessHead 链表
    • 新进程对象插入当前进程句柄表
    • 计算新进程的基本优先级和时限重置值
    • 设置进程访问权限,当前进程句柄可访问,允许进程终止
    • 设置进程创建时间

    PspCreateProcess 创建了进程,此时进程中的代码还没运行起来,进程还是死的,因为此时还没有线程,接下来介绍线程的创建过程。

    二、线程创建过程

    NtCreateThread 和 PsCreateSystemThread 函数会调用 PspCreateThread ,分别用于创建用户线程和系统线程。

    下面简单概括 PspCreateThread 创建线程的工作,可能有遗漏或错误。

    • 获取当前CPU模式
    • 获取进程对象
    • 创建线程对象并初始化为0
    • 设置父进程
    • 创建CID
    • 初始化线程结构的部分属性
    • 初始化定时器链表
    • 如果是用户线程,创建并初始化TEB,用 ThreadConatext 初始化R0 R3的入口点
    • 如果是系统线程,用 StartRoutine 初始化R0入口点
    • 不管是用户线程还是内核线程,都调用 KeInitThread 初始化 Header, WaitBlock, ServiceTable, APC,定时器,内核栈等属性
    • 进程的活动线程计数加1
    • 新线程加入进程的线程链表
    • 调用 KeStartThread 初始化剩余的域,主要是和调度相关的优先级、时限、CPU亲和性等
    • 如果是该进程的第一个线程,则触发该进程的创建通知
    • 工作集相关的操作
    • 线程对象的引用计数加2,一个针对当前的创建操作,一个针对要返回的线程句柄
    • 如果 CreateSuspended 为真,指示新线程立即被挂起
    • 根据指定的期望访问权限,调用 SeCreateAccessStateEx 创建一个访问状态结构
    • 把新线程对象插入到当前进程的句柄表
    • 设置输出参数 ThreadHandle
    • 设置输出参数 ClientId
    • 设置线程创建时间
    • 设置线程访问权限
    • 新线程加入就绪链表,等待调度;或者此时进程不在内存中,设置新线程状态为转移
    • 引用计数减1

    PspCreateThread 函数返回后,新线程随时可以被调度执行。

    三、创建进程的全貌

    进程创建后才创建线程,但是 PspCreateProcess 函数中根本没有创建线程的动作,也没有打开进程可执行映像文件的代码。在WRK中,我们看不到完整的进程创建过程,这是不行的!下面从 CreateProcess 函数调用开始,分析进程创建的全过程。

    实际上,0环的东西已经分析过了,我们只需要分析3环 CreateProcessW 进0环之前干了啥就行。

    CreateProcessW 本身没干啥,而是调用了 CreateProcessInternalW 函数,源码见文末。

    CreateProcessInternalW 函数在 base\win32\client\process.c,总共 2582 行C代码,我看不懂,主要是不知道怎么把文件名转化成 SectionHandle 的,代码实在太多了,而且有很多看不懂的操作。

    BOOL
    WINAPI
    CreateProcessInternalW(
        HANDLE hUserToken,
        LPCWSTR lpApplicationName,
        LPWSTR lpCommandLine,
        LPSECURITY_ATTRIBUTES lpProcessAttributes,
        LPSECURITY_ATTRIBUTES lpThreadAttributes,
        BOOL bInheritHandles,
        DWORD dwCreationFlags,
        LPVOID lpEnvironment,
        LPCWSTR lpCurrentDirectory,
        LPSTARTUPINFOW lpStartupInfo,
        LPPROCESS_INFORMATION lpProcessInformation,
        PHANDLE hRestrictedUserToken
        )
    

    CreateProcessInternalW 打开指定的可执行文件,并创建一个内存区对象,注意,内存区对象并没有被映射到内存中(由于目标进程尚未创建,不可能完成内存映射),但它确实是打开了。

    接下来就是调用 ntdll 的 NtCreateProcessEx,通过系统调用,CPU模式变成内核模式,进入0环 KiSystemService / KiFastCallEntry 分发函数,然后调用执行体的 NtCreateProcessEx 函数。

    接下来的工作我们先前已经分析过了,这里再次过一遍。NtCreateProcessEx 函数执行前面介绍的进程创建逻辑,包括创建并初始化 EPROCESS 对象,创建初始的进程地址空间,创建和初始化句柄表,设置 EPROCESS KPROCESS 的各种属性,如进程优先级,安全属性,创建时间等。到这里,执行体层的进程对象已经建立,进程地址空间已经初始化,PEB也已初始化。

    接下来是创建线程,首先需要构造一个栈,和一个上下文环境。栈的大小通过映像文件获得,创建线程通过 ntdll 的 NtCreateThread 调用执行体的 NtCreateThread 完成,具体工作就是先前介绍过的,包括创建并初始化 ETHREAD,生成线程ID,建立TEB和设置线程安全性。

    进程的第一个线程启动函数是 kernel32 的 BaseProcessStart ,这里创建的线程并不会立即执行,而是要等进程完全初始化后才执行。

    到此为止,从内核角度来看,进程对象和第一个线程对象已经建立起来了,但是对子系统而言,进程创建才刚刚开始。kernel32 给windows子系统发送一个消息,消息中包括进程和线程的句柄、进程创建者的ID等必要信息。windows子系统 csrss.exe 接收到此消息,执行以下操作:

    • 保留一份句柄
    • 设定新进程的优先级类别
    • 在子系统中分配一个内部进程块
    • 设置新进程的异常端口,从而子系统可以接收到该进程中发生的异常
    • 对于被调试进程,设置它的调试端口,从而子系统可以接收到该进程的调试事件
    • 分配并初始化一个内部线程块,并插入到进程的线程列表中
    • 窗口会话中的进程计数加1
    • 设置进程的停机级别位默认级别
    • 将新进程插入到子系统的进程列表中
    • 分配并初始化一块内存供子系统的内核模式部分使用(W32PROCESS结构)
    • 显示应用程序启动光标

    到此为止,进程环境已经建好,其线程将要使用的资源也分配好了,windows子系统已经知道并登记了此进程和线程。所以,初始线程被恢复执行,余下部分的初始化工作是在初始线程在新进程环境中完成的。在内核中,新线程启动的例程是 KiThreadStartup 函数,这是在 PspCreateThread 函数中调用 KeInitThread 时,KeInitThread 函数又调用 KiInitializeContextThread 函数来设置的。

    cPublicProc _KiThreadStartup ,1
    xor ebx,ebx ; clear registers
    xor esi,esi ;
    xor edi,edi ;
    xor ebp,ebp ;
    LowerIrql APC_LEVEL ; KeLowerIrql(APC_LEVEL)
    pop eax ; (eax)->SystemRoutine
    call eax ; SystemRoutine(StartRoutine, StartContext)
    pop ecx ; (ecx) = UserContextFlag
    or ecx, ecx
    jz short kits10 ; No user context, go bugcheck
    mov ebp,esp ; (bp) -> TrapFrame holding UserContext
    jmp _KiServiceExit2
    kits10: stdCall _KeBugCheck, <NO_USER_MODE_CONTEXT>
    stdENDP _KiThreadStartup
    

    KiThreadStartup 函数首先将 IRQL 降低到 APC_LEVEL,然后调用系统初始的线程函数 PspUserThreadStartup (PspCreateThread 函数在调用KeInitThread 时指定的,如果是创建系统线程,这里就是 PspSystemThreadStartup 函数)。线程启动函数被作为一个参数传递给 PspUserThreadStartup ,此处应是 kernel32 的 BaseProcessStart。

    PspUserThreadStartup 函数设置异步函数调用APC机制,基本流程如下:

    • 获得当前线程和进程对象。

    • 是否由于创建过程中出错而需要终止本线程。

    • 如果需要,通知调试器。

    • 如果这是进程中的第一个线程,则判断系统是否支持应用程序预取的特性,如果
      是,则通知缓存管理器预取可执行映像文件中的页面(见2 106 行的CcPfBeginAppLaunch
      调用)。所谓应用程序预取,是指将该进程上一次启动的前10 s 内引用到的页面直接读
      入到内存中。

    • 然后,PspUserThreadStartup 把一个用户模式APC 插入到线程的用户APC 队列中,
      此APC 例程是在全局变量PspSystemDll 中指定的,指向ntdll.dll 的LdrInitializeThunk 函数。

    • 接下来填充系统范围的一个Cookie 值。

    PspUserThreadStartup 返回后,KiThreadStartup 函数返回到用户模式,此时,PspUserThreadStartup 插入的APC 被交付,于是 LdrInitializeThunk 函数被调用,这是映像加载器(image loader)的初始化函数,完成加载器,堆管理器等初始化工作,然后加载必要的dll,并调用它们的入口函数。最后,当 LdrInitializeThunk 返回到用户模式 APC 分发器时,该线程开始在用户模式下执行,调用应用程序指定的线程启动函数,此启动函数的地址已经在APC交付时被压到用户栈中。

    至此,进程创建完毕,开始执行用户空间中的代码。

    本文涉及的函数源码

    PspCreateProcess

    NTSTATUS
    PspCreateProcess(
        OUT PHANDLE ProcessHandle,
        IN ACCESS_MASK DesiredAccess,
        IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
        IN HANDLE ParentProcess OPTIONAL,
        IN ULONG Flags,
        IN HANDLE SectionHandle OPTIONAL,
        IN HANDLE DebugPort OPTIONAL,
        IN HANDLE ExceptionPort OPTIONAL,
        IN ULONG JobMemberLevel
        )
    
    /*++
    
    Routine Description:
    
        This routine creates and initializes a process object.  It implements the
        foundation for NtCreateProcess and for system initialization process
        creation.
        这个函数创建并初始化一个进程对象。NtCreateProcess 会调用它;系统进程初始化也会调用它。
    
    Arguments:
    
        ProcessHandle - Returns the handle for the new process.
        输出参数,返回新进程句柄
    
        DesiredAccess - Supplies the desired access modes to the new process.
        期望对新进程的访问权限
    
        ObjectAttributes - Supplies the object attributes of the new process.
        新进程的对象属性
    
        ParentProcess - Supplies a handle to the process' parent process.  If this
                        parameter is not specified, then the process has no parent
                        and is created using the system address space.
        指定父进程句柄。如果未指定,说明没有父进程,那就是用系统地址空间创建进程。
    
        Flags         - Process creation flags
        进程创建标志
    
        SectionHandle - Supplies a handle to a section object to be used to create
                        the process' address space.  If this parameter is not
                        specified, then the address space is simply a clone of the
                        parent process' address space.
        提供一个内存区对象句柄用来创建进程地址空间。如果此参数未指定,则简单地复制父进程
        的地址空间。
    
        DebugPort - Supplies a handle to a port object that will be used as the
                    process' debug port.
        调试端口句柄
    
        ExceptionPort - Supplies a handle to a port object that will be used as the
                        process' exception port.
        异常端口句柄
    
        JobMemberLevel - Level for a create process in a jobset
        工作集等级
    --*/
    
    {
    
        NTSTATUS Status;
        PEPROCESS Process;
        PEPROCESS CurrentProcess;
        PEPROCESS Parent;
        PETHREAD CurrentThread;
        KAFFINITY Affinity;
        KPRIORITY BasePriority;
        PVOID SectionObject;
        PVOID ExceptionPortObject;
        PVOID DebugPortObject;
        ULONG WorkingSetMinimum, WorkingSetMaximum;
        HANDLE LocalProcessHandle;
        KPROCESSOR_MODE PreviousMode;
        INITIAL_PEB InitialPeb;
        BOOLEAN CreatePeb;
        ULONG_PTR DirectoryTableBase[2];
        BOOLEAN AccessCheck;
        BOOLEAN MemoryAllocated;
        PSECURITY_DESCRIPTOR SecurityDescriptor;
        SECURITY_SUBJECT_CONTEXT SubjectContext;
        NTSTATUS accesst;
        NTSTATUS SavedStatus;
        ULONG ImageFileNameSize;
        HANDLE_TABLE_ENTRY CidEntry;
        PEJOB Job;
        PPEB Peb;
        AUX_ACCESS_DATA AuxData;
        PACCESS_STATE AccessState;
        ACCESS_STATE LocalAccessState;
        BOOLEAN UseLargePages;
        SCHAR QuantumReset;
    #if defined(_WIN64)
        INITIAL_PEB32 InitialPeb32;
    #endif
    
        PAGED_CODE();
    
        // 获取当前线程、CPU模式、当前进程
        CurrentThread = PsGetCurrentThread ();
        PreviousMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
        CurrentProcess = PsGetCurrentProcessByThread (CurrentThread);
    
        CreatePeb = FALSE;
        UseLargePages = FALSE;
        DirectoryTableBase[0] = 0;
        DirectoryTableBase[1] = 0;
        Peb = NULL;
        
        //
        // Reject bogus create parameters for future expansion
        // 如果 Flags 里的保留位被置1,就是非法参数
        if (Flags&~PROCESS_CREATE_FLAGS_LEGAL_MASK) {
            return STATUS_INVALID_PARAMETER;
        }
    
        //
        // Parent
        // 检查有无指定父进程
        // 
    
        if (ARGUMENT_PRESENT (ParentProcess)) {
            // 如果指定了父进程句柄,就获取它的EPROCESS
            Status = ObReferenceObjectByHandle (ParentProcess,
                                                PROCESS_CREATE_PROCESS,
                                                PsProcessType,
                                                PreviousMode,
                                                &Parent,
                                                NULL);
            if (!NT_SUCCESS (Status)) {
                return Status;
            }
    
            // 工作集相关
            if (JobMemberLevel != 0 && Parent->Job == NULL) {
                ObDereferenceObject (Parent);
                return STATUS_INVALID_PARAMETER;
            }
    
            // 继承父进程的CPU亲和性
            Affinity = Parent->Pcb.Affinity;
            // 用全局变量初始化工作集最大最小值
            WorkingSetMinimum = PsMinimumWorkingSet;
            WorkingSetMaximum = PsMaximumWorkingSet;
    
    
        } else {
            // 没有父进程
            Parent = NULL;
            Affinity = KeActiveProcessors;
            WorkingSetMinimum = PsMinimumWorkingSet;
            WorkingSetMaximum = PsMaximumWorkingSet;
        }
    
        //
        // Create the process object
        // 创建进程对象 EPROCESS
        //
        Status = ObCreateObject (PreviousMode,
                                 PsProcessType,
                                 ObjectAttributes,
                                 PreviousMode,
                                 NULL,
                                 sizeof (EPROCESS),
                                 0,
                                 0,
                                 &Process);
    
        if (!NT_SUCCESS (Status)) {
            goto exit_and_deref_parent;
        }
    
        //
        // The process object is created set to NULL. Errors
        // That occur after this step cause the process delete
        // routine to be entered.
        // EPROCESS 数据清零。此后发生的错误会导致调用进程删除函数
        //
        // Teardown actions that occur in the process delete routine
        // do not need to be performed inline.
        //
    
        RtlZeroMemory (Process, sizeof(EPROCESS));
        ExInitializeRundownProtection (&Process->RundownProtect);
        PspInitializeProcessLock (Process);
        InitializeListHead (&Process->ThreadListHead);
    
    #if defined(_WIN64)
    
        if (Flags & PROCESS_CREATE_FLAGS_OVERRIDE_ADDRESS_SPACE) {
            PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_OVERRIDE_ADDRESS_SPACE);
        }
    
    #endif
    
        PspInheritQuota (Process, Parent);
        ObInheritDeviceMap (Process, Parent);
        if (Parent != NULL) {
            Process->DefaultHardErrorProcessing = Parent->DefaultHardErrorProcessing;
            Process->InheritedFromUniqueProcessId = Parent->UniqueProcessId;
    
        } else {
            Process->DefaultHardErrorProcessing = PROCESS_HARDERROR_DEFAULT;
            Process->InheritedFromUniqueProcessId = NULL;
        }
    
        //
        // Section
        //
    
        if (ARGUMENT_PRESENT (SectionHandle)) {
            // 如果指定了内存区对象句柄参数 SectionHandle,就获取内存区对象
            Status = ObReferenceObjectByHandle (SectionHandle,
                                                SECTION_MAP_EXECUTE,
                                                MmSectionObjectType,
                                                PreviousMode,
                                                &SectionObject,
                                                NULL);
            if (!NT_SUCCESS (Status)) {
                goto exit_and_deref;
            }
    
        } else {
            // SectionHandle 参数为NULL,要看父进程是不是 System 进程
    
            SectionObject = NULL;
    
            
            if (Parent != PsInitialSystemProcess) {
                // 如果父进程不是 System 进程 ,那么内存区对象继承自父进程
                //
                // Fetch the section pointer from the parent process
                // as we will be cloning. Since the section pointer
                // is removed at last thread exit we need to protect against
                // process exit here to be safe.
                //
    
                if (ExAcquireRundownProtection (&Parent->RundownProtect)) {
                    SectionObject = Parent->SectionObject;
                    if (SectionObject != NULL) {
                        ObReferenceObject (SectionObject);
                    }
    
                    ExReleaseRundownProtection (&Parent->RundownProtect);
                }
    
                if (SectionObject == NULL) {
                    Status = STATUS_PROCESS_IS_TERMINATING;
                    goto exit_and_deref;
                }
            }
    
            // 如果父进程是 System 进程,那么 SectionObject 就是 NULL
        }
    
        // 内存区对象 SectionObject 初始化完成(如果是NULL则表示父进程是System进程)
        Process->SectionObject = SectionObject;
    
        //
        // DebugPort
        // 调试端口初始化
    
        if (ARGUMENT_PRESENT (DebugPort)) {
            Status = ObReferenceObjectByHandle (DebugPort,
                                                DEBUG_PROCESS_ASSIGN,
                                                DbgkDebugObjectType,
                                                PreviousMode,
                                                &DebugPortObject,
                                                NULL);
    
            if (!NT_SUCCESS (Status)) {
                goto exit_and_deref;
            }
    
            Process->DebugPort = DebugPortObject;
            if (Flags&PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT) {
                PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_NO_DEBUG_INHERIT);
            }
    
        } else {
            if (Parent != NULL) {
                DbgkCopyProcessDebugPort (Process, Parent);
            }
        }
    
        //
        // ExceptionPort
        // 异常端口初始化
    
        if (ARGUMENT_PRESENT (ExceptionPort)) {
            Status = ObReferenceObjectByHandle (ExceptionPort,
                                                0,
                                                LpcPortObjectType,
                                                PreviousMode,
                                                &ExceptionPortObject,
                                                NULL);
    
            if (!NT_SUCCESS (Status)) {
                goto exit_and_deref;
            }
    
            Process->ExceptionPort = ExceptionPortObject;
        }
    
        Process->ExitStatus = STATUS_PENDING;
    
        //
        // Clone parent's object table.
        // If no parent (booting) then use the current object table created in
        // ObInitSystem.
        //
    
        // 创建进程地址空间
    
        if (Parent != NULL) {
            // 如果有父进程
            //
            // Calculate address space
            //
            //      If Parent == PspInitialSystem
            //
    
            // 创建一个全新的地址空间,这个函数有三个版本,wrk选 procx86.c 的
            if (!MmCreateProcessAddressSpace (WorkingSetMinimum,
                                              Process,
                                              &DirectoryTableBase[0])) {
    
                Status = STATUS_INSUFFICIENT_RESOURCES;
                goto exit_and_deref;
            }
    
        } else {
            // 如果没有父进程
    
            // 复制当前进程的句柄表
            Process->ObjectTable = CurrentProcess->ObjectTable;
    
            //
            // Initialize the Working Set Mutex and address creation mutex
            // for this "hand built" process.
            // Normally, the call to MmInitializeAddressSpace initializes the
            // working set mutex, however, in this case, we have already initialized
            // the address space and we are now creating a second process using
            // the address space of the idle thread.
            //
    
            // 使用空闲线程的地址空间
            Status = MmInitializeHandBuiltProcess (Process, &DirectoryTableBase[0]);
            if (!NT_SUCCESS (Status)) {
                goto exit_and_deref;
            }
        }
    
        // 进程地址空间初始化完成
        PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_HAS_ADDRESS_SPACE);
    
        Process->Vm.MaximumWorkingSetSize = WorkingSetMaximum;
    
        // 初始化进程基础优先级,CPU亲和性,页目录基址,超空间的页帧号
        KeInitializeProcess (&Process->Pcb,
                             NORMAL_BASE_PRIORITY,
                             Affinity,
                             &DirectoryTableBase[0],
                             (BOOLEAN)(Process->DefaultHardErrorProcessing & PROCESS_HARDERROR_ALIGNMENT_BIT));
    
        //
        //  Initialize the security fields of the process
        //  The parent may be null exactly once (during system init).
        //  Thereafter, a parent is always required so that we have a
        //  security context to duplicate for the new process.
        //
    
        // 函数初始化新进程的安全属性,主要是从父进程复制一个令牌
        Status = PspInitializeProcessSecurity (Parent, Process);
        if (!NT_SUCCESS (Status)) {
            goto exit_and_deref;
        }
    
        // 设置新进程优先级类别
        Process->PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
    
        if (Parent != NULL) {
            // 拷贝父进程的优先级类别
            if (Parent->PriorityClass == PROCESS_PRIORITY_CLASS_IDLE ||
                Parent->PriorityClass == PROCESS_PRIORITY_CLASS_BELOW_NORMAL) {
                Process->PriorityClass = Parent->PriorityClass;
            }
    
            //
            // if address space creation worked, then when going through
            // delete, we will attach. Of course, attaching means that the kprocess
            // must be initialized, so we delay the object stuff till here.
            // 如果地址空间已创建,
    
            // 初始化进程句柄表。如果指定父进程,则拷贝所有设置了继承属性的句柄,句柄计数加一
            Status = ObInitProcess ((Flags&PROCESS_CREATE_FLAGS_INHERIT_HANDLES) ? Parent : NULL,
                                    Process);
    
            if (!NT_SUCCESS (Status)) {
                goto exit_and_deref;
            }
    
        } else {
            Status = MmInitializeHandBuiltProcess2 (Process);
            if (!NT_SUCCESS (Status)) {
                goto exit_and_deref;
            }
        }
    
        Status = STATUS_SUCCESS;
        SavedStatus = STATUS_SUCCESS;
    
        //
        // Initialize the process address space
        // The address space has four possibilities
        // 初始化进程地址空间,有4种可能性
        //
        //      1 - Boot Process. Address space is initialized during
        //          MmInit. Parent is not specified.
        //          引导进程,这种情况不在这里初始化地址空间,而是在 MmInitSystem 函数初始化
        //
        //      2 - System Process. Address space is a virgin address
        //          space that only maps system space. Process is same
        //          as PspInitialSystemProcess.
        //          System 进程。地址空间未使用,映射到系统空间。
        //
        //      3 - User Process (Cloned Address Space). Address space
        //          is cloned from the specified process.
        //          用户进程(克隆父进程地址空间)
        //
        //      4 - User Process (New Image Address Space). Address space
        //          is initialized so that it maps the specified section.
        //          用户进程(新地址空间镜像)
        //
    
        if (SectionHandle != NULL) {
    
            //
            // User Process (New Image Address Space). Don't specify Process to
            // clone, just SectionObject.
            //
            // Passing in the 4th parameter as below lets the EPROCESS struct contain its image file name, provided that
            // appropriate audit settings are enabled.  Memory is allocated inside of MmInitializeProcessAddressSpace
            // and pointed to by ImageFileName, so that must be freed in the process deletion routine (PspDeleteProcess())
            //
            // 新进程指定了内存区对象,调用 MmInitializeProcessAddressSpace 函数初始化进程地址空间
            //
    
            Status = MmInitializeProcessAddressSpace (Process,
                                                      NULL,
                                                      SectionObject,
                                                      &Flags,
                                                      &(Process->SeAuditProcessCreationInfo.ImageFileName));
    
            if (!NT_SUCCESS (Status)) {
                goto exit_and_deref;
            }
    
            //
            // In order to support relocating executables, the proper status
            // (STATUS_IMAGE_NOT_AT_BASE) must be returned, so save it here.
            //
    
            SavedStatus = Status;
            CreatePeb = TRUE;
            UseLargePages = ((Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES) != 0 ? TRUE : FALSE);
    
        } else if (Parent != NULL) {
            if (Parent != PsInitialSystemProcess) {
                Process->SectionBaseAddress = Parent->SectionBaseAddress;
    
                //
                // User Process ( Cloned Address Space ).  Don't specify section to
                // map, just Process to clone.
                //
                // 指定了父进程,父进程不是 System 进程,调用 MmInitializeProcessAddressSpace 
                // 根据父进程初始化地址空间
                //
    
                Status = MmInitializeProcessAddressSpace (Process,
                                                          Parent,
                                                          NULL,
                                                          &Flags,
                                                          NULL);
    
                if (!NT_SUCCESS (Status)) {
                    goto exit_and_deref;
                }
    
                CreatePeb = TRUE;
                UseLargePages = ((Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES) != 0 ? TRUE : FALSE);
                
                //
                // A cloned process isn't started from an image file, so we give it the name
                // of the process of which it is a clone, provided the original has a name.
                //
    
                if (Parent->SeAuditProcessCreationInfo.ImageFileName != NULL) {
                    ImageFileNameSize = sizeof(OBJECT_NAME_INFORMATION) +
                                        Parent->SeAuditProcessCreationInfo.ImageFileName->Name.MaximumLength;
    
                    Process->SeAuditProcessCreationInfo.ImageFileName =
                        ExAllocatePoolWithTag (PagedPool,
                                               ImageFileNameSize,
                                               'aPeS');
    
                    if (Process->SeAuditProcessCreationInfo.ImageFileName != NULL) {
                        RtlCopyMemory (Process->SeAuditProcessCreationInfo.ImageFileName,
                                       Parent->SeAuditProcessCreationInfo.ImageFileName,
                                       ImageFileNameSize);
    
                        //
                        // The UNICODE_STRING in the process is self contained, so calculate the
                        // offset for the buffer.
                        //
    
                        Process->SeAuditProcessCreationInfo.ImageFileName->Name.Buffer =
                            (PUSHORT)(((PUCHAR) Process->SeAuditProcessCreationInfo.ImageFileName) +
                            sizeof(UNICODE_STRING));
    
                    } else {
                        Status = STATUS_INSUFFICIENT_RESOURCES;
                        goto exit_and_deref;
                    }
                }
    
            } else {
    
                //
                // System Process.  Don't specify Process to clone or section to map
                //
                // 没有指定内存区对象,但父进程是System进程
                //
    
                Flags &= ~PROCESS_CREATE_FLAGS_ALL_LARGE_PAGE_FLAGS;
                Status = MmInitializeProcessAddressSpace (Process,
                                                          NULL,
                                                          NULL,
                                                          &Flags,
                                                          NULL);
    
                if (!NT_SUCCESS (Status)) {
                    goto exit_and_deref;
                }
    
                //
                // In case the image file name of this system process is ever queried, we give
                // a zero length UNICODE_STRING.
                //
    
                Process->SeAuditProcessCreationInfo.ImageFileName =
                    ExAllocatePoolWithTag (PagedPool,
                                           sizeof(OBJECT_NAME_INFORMATION),
                                           'aPeS');
    
                if (Process->SeAuditProcessCreationInfo.ImageFileName != NULL) {
                    RtlZeroMemory (Process->SeAuditProcessCreationInfo.ImageFileName,
                                   sizeof(OBJECT_NAME_INFORMATION));
                } else {
                    Status = STATUS_INSUFFICIENT_RESOURCES;
                    goto exit_and_deref;
                }
            }
        }
    
        //
        // Create the process ID
        //
        // 创建进程ID,方法是调用 ExCreateHandle 在System 进程句柄表中存一个句柄
        // 句柄值就是PID
        //
    
        CidEntry.Object = Process;
        CidEntry.GrantedAccess = 0;
        Process->UniqueProcessId = ExCreateHandle (PspCidTable, &CidEntry);
        if (Process->UniqueProcessId == NULL) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto exit_and_deref;
        }
    
        ExSetHandleTableOwner (Process->ObjectTable, Process->UniqueProcessId);
    
        //
        // Audit the process creation.
        // 审计此次进程创建行为
    
        if (SeDetailedAuditingWithToken (NULL)) {
            SeAuditProcessCreation (Process);
        }
    
        //
        // See if the parent has a job. If so reference the job
        // and add the process in.
        //
    
        // 作业相关,本书不分析
        if (Parent) {
            Job = Parent->Job;
            if (Job != NULL && !(Job->LimitFlags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)) {
                if (Flags&PROCESS_CREATE_FLAGS_BREAKAWAY) {
                    if (!(Job->LimitFlags & JOB_OBJECT_LIMIT_BREAKAWAY_OK)) {
                        Status = STATUS_ACCESS_DENIED;
    
                    } else {
                        Status = STATUS_SUCCESS;
                    }
    
                } else {
                    Status = PspGetJobFromSet (Job, JobMemberLevel, &Process->Job);
                    if (NT_SUCCESS (Status)) {
                        PACCESS_TOKEN Token, NewToken;
                        Job = Process->Job;
                        Status = PspAddProcessToJob (Job, Process);
    
                        //
                        // Duplicate a new process token if one is specified for the job
                        //
    
                        Token = Job->Token;
                        if (Token != NULL) {
                            Status = SeSubProcessToken (Token,
                                                        &NewToken,
                                                        FALSE,
                                                        Job->SessionId);
    
                            if (!NT_SUCCESS (Status)) {
                                goto exit_and_deref;
                            }
    
                            SeAssignPrimaryToken (Process, NewToken);    
                            ObDereferenceObject (NewToken);                    
                        }
                    }
                }
    
                if (!NT_SUCCESS (Status)) {
                    goto exit_and_deref;
                }
            }
        }
    
        // 对于通过映像内存区对象创建的进程,创建一个PEB
        // 对于进程拷贝(fork)的情景,则使用继承的PEB
        if (Parent && CreatePeb) {
    
            //
            // For processes created w/ a section,
            // a new "virgin" PEB is created. Otherwise,
            // for forked processes, uses inherited PEB
            // with an updated mutant.
            //
    
            RtlZeroMemory (&InitialPeb, FIELD_OFFSET(INITIAL_PEB, Mutant));
    
            InitialPeb.Mutant = (HANDLE)(-1);
            InitialPeb.ImageUsesLargePages = (BOOLEAN) UseLargePages;
                
            if (SectionHandle != NULL) {
                Status = MmCreatePeb (Process, &InitialPeb, &Process->Peb);
                if (!NT_SUCCESS (Status)) {
                    Process->Peb = NULL;
                    goto exit_and_deref;
                }
    
                Peb =  Process->Peb;
    
            } else {
                SIZE_T BytesCopied;
    
                InitialPeb.InheritedAddressSpace = TRUE;
                Process->Peb = Parent->Peb;
                MmCopyVirtualMemory (CurrentProcess,
                                     &InitialPeb,
                                     Process,
                                     Process->Peb,
                                     sizeof (INITIAL_PEB),
                                     KernelMode,
                                     &BytesCopied);
    
    #if defined(_WIN64)
                if (Process->Wow64Process != NULL) {
                    
                    RtlZeroMemory (&InitialPeb32, FIELD_OFFSET(INITIAL_PEB32, Mutant));
                    InitialPeb32.Mutant = -1;
                    InitialPeb32.InheritedAddressSpace = TRUE;
                    InitialPeb32.ImageUsesLargePages = (BOOLEAN) UseLargePages;
    
                    MmCopyVirtualMemory (CurrentProcess,
                                         &InitialPeb32,
                                         Process,
                                         Process->Wow64Process->Wow64,
                                         sizeof (INITIAL_PEB32),
                                         KernelMode,
                                         &BytesCopied);
                }
    #endif
    
            }
        }
    
        Peb = Process->Peb;
    
        //
        // Add the process to the global list of processes.
        // 新进程对象插入到全局活动进程链表 PsActiveProcessHead
    
        PspLockProcessList (CurrentThread);
        InsertTailList (&PsActiveProcessHead, &Process->ActiveProcessLinks);
        PspUnlockProcessList (CurrentThread);
    
        AccessState = NULL;
        if (!PsUseImpersonationToken) {
            AccessState = &LocalAccessState;
            Status = SeCreateAccessStateEx (NULL,
                                            (Parent == NULL || Parent != PsInitialSystemProcess)?
                                               PsGetCurrentProcessByThread (CurrentThread) :
                                               PsInitialSystemProcess,
                                            AccessState,
                                            &AuxData,
                                            DesiredAccess,
                                            &PsProcessType->TypeInfo.GenericMapping);
            if (!NT_SUCCESS (Status)) {
                goto exit_and_deref;
            }
        }
    
        //
        // Insert the object. Once we do this is reachable from the outside world via
        // open by name. Open by ID is still disabled. Since its reachable
        // somebody might create a thread in the process and cause
        // rundown.
        //
    
        // 新进程对象插入到当前进程句柄表中
        Status = ObInsertObject (Process,
                                 AccessState,
                                 DesiredAccess,
                                 1,     // bias the refcnt by one for future process manipulations
                                 NULL,
                                 &LocalProcessHandle);
    
        if (AccessState != NULL) {
            SeDeleteAccessState (AccessState);
        }
    
        if (!NT_SUCCESS (Status)) {
            goto exit_and_deref_parent;
        }
    
        //
        // Compute the base priority and quantum reset values for the process and
        // set the memory priority.
        //
    
        ASSERT(IsListEmpty(&Process->ThreadListHead) == TRUE);
    
        // 计算新进程的基本优先级和时限重置值
        BasePriority = PspComputeQuantumAndPriority(Process,
                                                    PsProcessPriorityBackground,
                                                    &QuantumReset);
    
        Process->Pcb.BasePriority = (SCHAR)BasePriority;
        Process->Pcb.QuantumReset = QuantumReset;
    
        //
        // As soon as a handle to the process is accessible, allow the process to
        // be deleted.
        //
    
        // 设置进程访问权限,当前进程句柄可访问,允许进程终止
        Process->GrantedAccess = PROCESS_TERMINATE;
        if (Parent && Parent != PsInitialSystemProcess) {
            Status = ObGetObjectSecurity (Process,
                                          &SecurityDescriptor,
                                          &MemoryAllocated);
    
            if (!NT_SUCCESS (Status)) {
                ObCloseHandle (LocalProcessHandle, PreviousMode);
                goto exit_and_deref;
            }
    
            //
            // Compute the subject security context
            //
    
            SubjectContext.ProcessAuditId = Process;
            SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process);
            SubjectContext.ClientToken = NULL;
            AccessCheck = SeAccessCheck (SecurityDescriptor,
                                         &SubjectContext,
                                         FALSE,
                                         MAXIMUM_ALLOWED,
                                         0,
                                         NULL,
                                         &PsProcessType->TypeInfo.GenericMapping,
                                         PreviousMode,
                                         &Process->GrantedAccess,
                                         &accesst);
    
            PsDereferencePrimaryTokenEx (Process, SubjectContext.PrimaryToken);
            ObReleaseObjectSecurity (SecurityDescriptor,
                                     MemoryAllocated);
    
            if (!AccessCheck) {
                Process->GrantedAccess = 0;
            }
    
            //
            // It does not make any sense to create a process that can not
            // do anything to itself.
            // Note: Changes to this set of bits should be reflected in psquery.c
            // code, in PspSetPrimaryToken.
            //
    
            Process->GrantedAccess |= (PROCESS_VM_OPERATION |
                                       PROCESS_VM_READ |
                                       PROCESS_VM_WRITE |
                                       PROCESS_QUERY_INFORMATION |
                                       PROCESS_TERMINATE |
                                       PROCESS_CREATE_THREAD |
                                       PROCESS_DUP_HANDLE |
                                       PROCESS_CREATE_PROCESS |
                                       PROCESS_SET_INFORMATION |
                                       STANDARD_RIGHTS_ALL |
                                       PROCESS_SET_QUOTA);
    
        } else {
            Process->GrantedAccess = PROCESS_ALL_ACCESS;
        }
    
        // 设置进程创建时间
        KeQuerySystemTime (&Process->CreateTime);
        try {
            if (Peb != NULL && CurrentThread->Tcb.Teb != NULL) {
                ((PTEB)(CurrentThread->Tcb.Teb))->NtTib.ArbitraryUserPointer = Peb;
            }
    
            *ProcessHandle = LocalProcessHandle;
    
        } except (EXCEPTION_EXECUTE_HANDLER) {
            NOTHING;
        }
    
        if (SavedStatus != STATUS_SUCCESS) {
            Status = SavedStatus;
        }
    
    exit_and_deref:
        ObDereferenceObject (Process);
    
    exit_and_deref_parent:
        if (Parent != NULL) {
            ObDereferenceObject (Parent);
        }
    
        return Status;
    }
    

    PspCreateThread

    NTSTATUS
    PspCreateThread(
        OUT PHANDLE ThreadHandle,
        IN ACCESS_MASK DesiredAccess,
        IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
        IN HANDLE ProcessHandle,
        IN PEPROCESS ProcessPointer,
        OUT PCLIENT_ID ClientId OPTIONAL,
        IN PCONTEXT ThreadContext OPTIONAL,
        IN PINITIAL_TEB InitialTeb OPTIONAL,
        IN BOOLEAN CreateSuspended,
        IN PKSTART_ROUTINE StartRoutine OPTIONAL,
        IN PVOID StartContext
        )
    
    /*++
    
    Routine Description:
    
        This routine creates and initializes a thread object. It implements the
        foundation for NtCreateThread and for PsCreateSystemThread.
    
    Arguments:
    
        ThreadHandle - Returns the handle for the new thread.
        输出参数,返回新线程句柄
    
        DesiredAccess - Supplies the desired access modes to the new thread.
        期望的新线程访问权限
    
        ObjectAttributes - Supplies the object attributes of the new thread.
        指定新线程对象的属性
    
        ProcessHandle - Supplies a handle to the process that the thread is being
                        created within.
        新线程将运行在此进程的环境中
    
        ProcessPointer
        仅当创建系统线程时,此参数指向 System进程 PsInitialSystemProcess
        其他情况下都是NULL
    
        ClientId - Returns the CLIENT_ID of the new thread.
        返回新线程的CID
    
        ThreadContext - Supplies a pointer to a context frame that represents the
                    initial user-mode context for a user-mode thread. The absence
                    of this parameter indicates that a system thread is being
                    created.
        用户线程执行环境;如果是NULL,则表示创建系统线程
    
        InitialTeb - Supplies the contents of certain fields for the new threads
                     TEB. This parameter is only examined if both a trap and
                     exception frame were specified.
        用户线程初始化TEB结构
    
        CreateSuspended - Supplies a value that controls whether or not a user-mode
                          thread is created in a suspended state.
        是否挂起这个用户线程
    
        StartRoutine - Supplies the address of the system thread start routine.
        系统线程启动函数
    
        StartContext - Supplies context for a system thread start routine.
        系统线程启动函数执行环境
    
    --*/
    
    {
    
        HANDLE_TABLE_ENTRY CidEntry;
        NTSTATUS Status;
        PETHREAD Thread;
        PETHREAD CurrentThread;
        PEPROCESS Process;
        PTEB Teb;
        KPROCESSOR_MODE PreviousMode;
        HANDLE LocalThreadHandle;
        BOOLEAN AccessCheck;
        BOOLEAN MemoryAllocated;
        PSECURITY_DESCRIPTOR SecurityDescriptor;
        SECURITY_SUBJECT_CONTEXT SubjectContext;
        NTSTATUS accesst;
        LARGE_INTEGER CreateTime;
        ULONG OldActiveThreads;
        PEJOB Job;
        AUX_ACCESS_DATA AuxData;
        PACCESS_STATE AccessState;
        ACCESS_STATE LocalAccessState;
    
        PAGED_CODE();
    
        // 当前线程
        CurrentThread = PsGetCurrentThread ();
    
        // 当前CPU模式
        if (StartRoutine != NULL) {
            // 如果指定了 StartRoutine ,就是创建系统线程
            PreviousMode = KernelMode;
        } else {
            // 如果未指定 StartRoutine,就通过当前线程判断
            PreviousMode = KeGetPreviousModeByThread (&CurrentThread->Tcb);
        }
    
        Teb = NULL;
    
        Thread = NULL;
        Process = NULL;
    
        
        // 获取进程对象,存储到局部变量 Process
    
        if (ProcessHandle != NULL) {
            // 如果指定了 ProcessHandle,就通过 ProcessHandle 获取进程对象
    
            //
            // Process object reference count is biased by one for each thread.
            // This accounts for the pointer given to the kernel that remains
            // in effect until the thread terminates (and becomes signaled)
            //
    
            Status = ObReferenceObjectByHandle (ProcessHandle,
                                                PROCESS_CREATE_THREAD,
                                                PsProcessType,
                                                PreviousMode,
                                                &Process,
                                                NULL);
        } else {
            if (StartRoutine != NULL) {
                // 如果 ProcessHandle 是 NULL,且指定了 StartRoutine ,说明是System进程要创建系统线程
                ObReferenceObject (ProcessPointer);
                Process = ProcessPointer;
                Status = STATUS_SUCCESS;
            } else {
                // 否则,返回错误
                Status = STATUS_INVALID_HANDLE;
            }
        }
    
        if (!NT_SUCCESS (Status)) {
            return Status;
        }
    
        //
        // If the previous mode is user and the target process is the system
        // process, then the operation cannot be performed.
        //
    
        // 如果是用户模式,父进程却是System进程,就返回错误
        if ((PreviousMode != KernelMode) && (Process == PsInitialSystemProcess)) {
            ObDereferenceObject (Process);
            return STATUS_INVALID_HANDLE;
        }
    
        // 创建 ETHREAD 并清零
        Status = ObCreateObject (PreviousMode,
                                 PsThreadType,
                                 ObjectAttributes,
                                 PreviousMode,
                                 NULL,
                                 sizeof(ETHREAD),
                                 0,
                                 0,
                                 &Thread);
    
        if (!NT_SUCCESS (Status)) {
            ObDereferenceObject (Process);
            return Status;
        }
    
        RtlZeroMemory (Thread, sizeof (ETHREAD));
    
        //
        // Initialize rundown protection for cross thread TEB refs etc.
        //
    
        // 初始化 RundownProtect
        ExInitializeRundownProtection (&Thread->RundownProtect);
    
        //
        // Assign this thread to the process so that from now on
        // we don't have to dereference in error paths.
        //
    
        // 设置新线程的父进程
        Thread->ThreadsProcess = Process;
    
        // 创建CID
        Thread->Cid.UniqueProcess = Process->UniqueProcessId;    
        CidEntry.Object = Thread;
        CidEntry.GrantedAccess = 0;
        Thread->Cid.UniqueThread = ExCreateHandle (PspCidTable, &CidEntry);
    
        if (Thread->Cid.UniqueThread == NULL) {
            ObDereferenceObject (Thread);
            return (STATUS_INSUFFICIENT_RESOURCES);
        }
    
        //
        // Initialize Mm
        //
    
        Thread->ReadClusterSize = MmReadClusterSize;
    
        //
        // Initialize LPC
        //
    
        KeInitializeSemaphore (&Thread->LpcReplySemaphore, 0L, 1L);
        InitializeListHead (&Thread->LpcReplyChain);
    
        //
        // Initialize Io
        //
    
        InitializeListHead (&Thread->IrpList);
    
        //
        // Initialize Registry
        //
    
        InitializeListHead (&Thread->PostBlockList);
    
        //
        // Initialize the thread lock
        //
    
        PspInitializeThreadLock (Thread);
    
        // 初始化定时器链表
        KeInitializeSpinLock (&Thread->ActiveTimerListLock);
        InitializeListHead (&Thread->ActiveTimerListHead);
    
        // 获得进程的 RundownProtect 锁,防止创建过程中进程被停掉(rundown)
        // 直到线程被插入到进程的线程链表中,才能解锁
        if (!ExAcquireRundownProtection (&Process->RundownProtect)) {
            ObDereferenceObject (Thread);
            return STATUS_PROCESS_IS_TERMINATING;
        }
    
        if (ARGUMENT_PRESENT (ThreadContext)) {
    
            //
            // User-mode thread. Create TEB etc
            // 如果 ThreadContext 不空,说明是创建用户线程
            // 于是创建TEB,并用 InitialTeb 初始化
    
            Status = MmCreateTeb (Process, InitialTeb, &Thread->Cid, &Teb);
            if (!NT_SUCCESS (Status)) {
                ExReleaseRundownProtection (&Process->RundownProtect);
                ObDereferenceObject (Thread);
                return Status;
            }
    
    
            try {
                //
                // Initialize kernel thread object for user mode thread.
                //
    
                // 用 ThreadContext 的 Eip 初始化 StartAddress
                Thread->StartAddress = (PVOID)CONTEXT_TO_PROGRAM_COUNTER(ThreadContext);
    
    #if defined(_AMD64_)
    
                Thread->Win32StartAddress = (PVOID)ThreadContext->Rdx;
    
    #elif defined(_X86_)
    
                // 用 ThreadContext 的 Eax 初始化 Win32StartAddress
                Thread->Win32StartAddress = (PVOID)ThreadContext->Eax;
    
    #else
    
    #error "no target architecture"
    
    #endif
    
            } except (EXCEPTION_EXECUTE_HANDLER) {
    
                Status = GetExceptionCode();
            }
    
            // 根据进程对象的信息初始化线程的一些属性
            // 包括 Header, WaitBlock, ServiceTable, APC,定时器,内核栈等
            if (NT_SUCCESS (Status)) {
                Status = KeInitThread (&Thread->Tcb,
                                       NULL,
                                       PspUserThreadStartup,
                                       (PKSTART_ROUTINE)NULL,
                                       Thread->StartAddress,
                                       ThreadContext,
                                       Teb,
                                       &Process->Pcb);
           }
    
    
        } else {
    
            Teb = NULL;
            //
            // Set the system thread bit thats kept for all time
            // 设置系统线程标志位
            //
            PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_SYSTEM);
    
            //
            // Initialize kernel thread object for kernel mode thread.
            //
    
            // 设置系统线程启动地址 StartRoutine
            Thread->StartAddress = (PKSTART_ROUTINE) StartRoutine;
    
            // 初始化线程
            Status = KeInitThread (&Thread->Tcb,
                                   NULL,
                                   PspSystemThreadStartup,
                                   StartRoutine,
                                   StartContext,
                                   NULL,
                                   NULL,
                                   &Process->Pcb);
        }
    
    
        if (!NT_SUCCESS (Status)) {
            if (Teb != NULL) {
                MmDeleteTeb(Process, Teb);
            }
            ExReleaseRundownProtection (&Process->RundownProtect);
            ObDereferenceObject (Thread);
            return Status;
        }
    
        // 锁住进程,确保不是在退出或终止过程中
        PspLockProcessExclusive (Process, CurrentThread);
        //
        // Process is exiting or has had delete process called
        // We check the calling threads termination status so we
        // abort any thread creates while ExitProcess is being called --
        // but the call is blocked only if the new thread would be created
        // in the terminating thread's process.
        //
        if ((Process->Flags&PS_PROCESS_FLAGS_PROCESS_DELETE) != 0 ||
            (((CurrentThread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_TERMINATED) != 0) &&
            (ThreadContext != NULL) &&
            (THREAD_TO_PROCESS(CurrentThread) == Process))) {
    
            PspUnlockProcessExclusive (Process, CurrentThread);
    
            KeUninitThread (&Thread->Tcb);
    
            if (Teb != NULL) {
                MmDeleteTeb(Process, Teb);
            }
            ExReleaseRundownProtection (&Process->RundownProtect);
            
            ObDereferenceObject(Thread);
    
            return STATUS_PROCESS_IS_TERMINATING;
        }
    
        // 进程的活动线程计数加一
        OldActiveThreads = Process->ActiveThreads++;
        // 新线程加入进程的线程链表
        InsertTailList (&Process->ThreadListHead, &Thread->ThreadListEntry);
        // 初始化剩余的域,尤其是和调度相关的,比如优先级、时限设置、CPU亲和性等
        KeStartThread (&Thread->Tcb);
    
        // 此时线程可以被调度执行了
    
        PspUnlockProcessExclusive (Process, CurrentThread);
    
        ExReleaseRundownProtection (&Process->RundownProtect);
    
        //
        // Failures that occur after this point cause the thread to
        // go through PspExitThread
        //
    
        // 如果是该进程的第一个线程,触发该进程的创建通知
        if (OldActiveThreads == 0) {
            PERFINFO_PROCESS_CREATE (Process);
    
            if (PspCreateProcessNotifyRoutineCount != 0) {
                ULONG i;
                PEX_CALLBACK_ROUTINE_BLOCK CallBack;
                PCREATE_PROCESS_NOTIFY_ROUTINE Rtn;
    
                for (i=0; i<PSP_MAX_CREATE_PROCESS_NOTIFY; i++) {
                    CallBack = ExReferenceCallBackBlock (&PspCreateProcessNotifyRoutine[i]);
                    if (CallBack != NULL) {
                        Rtn = (PCREATE_PROCESS_NOTIFY_ROUTINE) ExGetCallBackBlockRoutine (CallBack);
                        Rtn (Process->InheritedFromUniqueProcessId,
                             Process->UniqueProcessId,
                             TRUE);
                        ExDereferenceCallBackBlock (&PspCreateProcessNotifyRoutine[i],
                                                    CallBack);
                    }
                }
            }
        }
    
        //
        // If the process has a job with a completion port,
        // AND if the process is really considered to be in the Job, AND
        // the process has not reported, report in
        //
        // This should really be done in add process to job, but can't
        // in this path because the process's ID isn't assigned until this point
        // in time
        //
        Job = Process->Job;
        if (Job != NULL && Job->CompletionPort &&
            !(Process->JobStatus & (PS_JOB_STATUS_NOT_REALLY_ACTIVE|PS_JOB_STATUS_NEW_PROCESS_REPORTED))) {
    
            PS_SET_BITS (&Process->JobStatus, PS_JOB_STATUS_NEW_PROCESS_REPORTED);
    
            KeEnterCriticalRegionThread (&CurrentThread->Tcb);
            ExAcquireResourceSharedLite (&Job->JobLock, TRUE);
            if (Job->CompletionPort != NULL) {
                IoSetIoCompletion (Job->CompletionPort,
                                   Job->CompletionKey,
                                   (PVOID)Process->UniqueProcessId,
                                   STATUS_SUCCESS,
                                   JOB_OBJECT_MSG_NEW_PROCESS,
                                   FALSE);
            }
            ExReleaseResourceLite (&Job->JobLock);
            KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
        }
    
        PERFINFO_THREAD_CREATE(Thread, InitialTeb);
    
        //
        // Notify registered callout routines of thread creation.
        //
    
        if (PspCreateThreadNotifyRoutineCount != 0) {
            ULONG i;
            PEX_CALLBACK_ROUTINE_BLOCK CallBack;
            PCREATE_THREAD_NOTIFY_ROUTINE Rtn;
    
            for (i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) {
                CallBack = ExReferenceCallBackBlock (&PspCreateThreadNotifyRoutine[i]);
                if (CallBack != NULL) {
                    Rtn = (PCREATE_THREAD_NOTIFY_ROUTINE) ExGetCallBackBlockRoutine (CallBack);
                    Rtn (Thread->Cid.UniqueProcess,
                         Thread->Cid.UniqueThread,
                         TRUE);
                    ExDereferenceCallBackBlock (&PspCreateThreadNotifyRoutine[i],
                                                CallBack);
                }
            }
        }
    
    
        //
        // Reference count of thread is biased once for itself and once for the handle if we create it.
        //
    
        // 线程对象的引用计数加2,一个针对当前的创建操作,一个针对要返回的线程句柄
        ObReferenceObjectEx (Thread, 2);
    
        // 如果 CreateSuspended ,指示新线程立即被挂起
        if (CreateSuspended) {
            try {
                KeSuspendThread (&Thread->Tcb);
            } except ((GetExceptionCode () == STATUS_SUSPEND_COUNT_EXCEEDED)?
                         EXCEPTION_EXECUTE_HANDLER :
                         EXCEPTION_CONTINUE_SEARCH) {
            }
            //
            // If deletion was started after we suspended then wake up the thread
            //
            if (Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_TERMINATED) {
                KeForceResumeThread (&Thread->Tcb);
            }
        }
    
        // 根据指定的期望访问权限,调用 SeCreateAccessStateEx 创建一个访问状态结构 
        AccessState = NULL;
        if (!PsUseImpersonationToken) {
            AccessState = &LocalAccessState;
            Status = SeCreateAccessStateEx (NULL,
                                            ARGUMENT_PRESENT (ThreadContext)?PsGetCurrentProcessByThread (CurrentThread) : Process,
                                            AccessState,
                                            &AuxData,
                                            DesiredAccess,
                                            &PsThreadType->TypeInfo.GenericMapping);
    
            if (!NT_SUCCESS (Status)) {
                PS_SET_BITS (&Thread->CrossThreadFlags,
                             PS_CROSS_THREAD_FLAGS_DEADTHREAD);
    
                if (CreateSuspended) {
                    (VOID) KeResumeThread (&Thread->Tcb);
                }
                KeReadyThread (&Thread->Tcb);
                ObDereferenceObjectEx (Thread, 2);
    
                return Status;
            }
        }
    
        // 把新线程对象插入到当前进程的句柄表
        Status = ObInsertObject (Thread,
                                 AccessState,
                                 DesiredAccess,
                                 0,
                                 NULL,
                                 &LocalThreadHandle);
    
        if (AccessState != NULL) {
            SeDeleteAccessState (AccessState);
        }
    
        if (!NT_SUCCESS (Status)) {
    
            //
            // The insert failed. Terminate the thread.
            //
    
            //
            // This trick is used so that Dbgk doesn't report
            // events for dead threads
            //
    
            PS_SET_BITS (&Thread->CrossThreadFlags,
                         PS_CROSS_THREAD_FLAGS_DEADTHREAD);
    
            if (CreateSuspended) {
                KeResumeThread (&Thread->Tcb);
            }
    
        } else {
            
            try {
                // 设置输出参数 ThreadHandle
                *ThreadHandle = LocalThreadHandle;
    
                // 设置输出参数 ClientId 
                if (ARGUMENT_PRESENT (ClientId)) {
                    *ClientId = Thread->Cid;
                }
            } except(EXCEPTION_EXECUTE_HANDLER) {
    
                PS_SET_BITS (&Thread->CrossThreadFlags,
                             PS_CROSS_THREAD_FLAGS_DEADTHREAD);
    
                if (CreateSuspended) {
                    (VOID) KeResumeThread (&Thread->Tcb);
                }
                KeReadyThread (&Thread->Tcb);
                ObDereferenceObject (Thread);
                ObCloseHandle (LocalThreadHandle, PreviousMode);
                return GetExceptionCode();
            }
        }
    
        // 设置线程创建时间
        KeQuerySystemTime(&CreateTime);
        ASSERT ((CreateTime.HighPart & 0xf0000000) == 0);
        PS_SET_THREAD_CREATE_TIME(Thread, CreateTime);
    
        // 设置线程访问权限
        if ((Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_DEADTHREAD) == 0) {
            Status = ObGetObjectSecurity (Thread,
                                          &SecurityDescriptor,
                                          &MemoryAllocated);
            if (!NT_SUCCESS (Status)) {
                //
                // This trick us used so that Dbgk doesn't report
                // events for dead threads
                //
                PS_SET_BITS (&Thread->CrossThreadFlags,
                             PS_CROSS_THREAD_FLAGS_DEADTHREAD);
    
                if (CreateSuspended) {
                    KeResumeThread(&Thread->Tcb);
                }
                KeReadyThread (&Thread->Tcb);
                ObDereferenceObject (Thread);
                ObCloseHandle (LocalThreadHandle, PreviousMode);
                return Status;
            }
    
            //
            // Compute the subject security context
            //
    
            SubjectContext.ProcessAuditId = Process;
            SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process);
            SubjectContext.ClientToken = NULL;
    
            AccessCheck = SeAccessCheck (SecurityDescriptor,
                                         &SubjectContext,
                                         FALSE,
                                         MAXIMUM_ALLOWED,
                                         0,
                                         NULL,
                                         &PsThreadType->TypeInfo.GenericMapping,
                                         PreviousMode,
                                         &Thread->GrantedAccess,
                                         &accesst);
    
            PsDereferencePrimaryTokenEx (Process, SubjectContext.PrimaryToken);
    
            ObReleaseObjectSecurity (SecurityDescriptor,
                                     MemoryAllocated);
    
            if (!AccessCheck) {
                Thread->GrantedAccess = 0;
            }
    
            Thread->GrantedAccess |= (THREAD_TERMINATE | THREAD_SET_INFORMATION | THREAD_QUERY_INFORMATION);
    
        } else {
            Thread->GrantedAccess = THREAD_ALL_ACCESS;
        }
    
        // 新线程加入就绪链表,等待调度;或者此时进程不在内存中,设置新线程状态为转移
        KeReadyThread (&Thread->Tcb);
    
        // 引用计数减一
        ObDereferenceObject (Thread);
    
        return Status;
    }
    

    CreateProcessW

    BOOL
    WINAPI
    CreateProcessW(
        LPCWSTR lpApplicationName,
        LPWSTR lpCommandLine,
        LPSECURITY_ATTRIBUTES lpProcessAttributes,
        LPSECURITY_ATTRIBUTES lpThreadAttributes,
        BOOL bInheritHandles,
        DWORD dwCreationFlags,
        LPVOID lpEnvironment,
        LPCWSTR lpCurrentDirectory,
        LPSTARTUPINFOW lpStartupInfo,
        LPPROCESS_INFORMATION lpProcessInformation
        )
    {
        return CreateProcessInternalW(
                   NULL, // Create new process with the token on the creator process
                   lpApplicationName,
                   lpCommandLine,
                   lpProcessAttributes,
                   lpThreadAttributes,
                   bInheritHandles,
                   dwCreationFlags,
                   lpEnvironment,
                   lpCurrentDirectory,
                   lpStartupInfo,
                   lpProcessInformation,
                   NULL  // Do not return the restricted token
                   );
    }
    

    结束

    展开全文
  • 追踪Linux系统创建进程过程,在实验楼虚拟中使用gdb调试工具进行调试

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

    实验环境:https://www.shiyanlou.com/courses/195

    实验过程

    本次实验主要追踪Linux系统创建进程的过程,在实验楼虚拟中使用gdb调试工具进行调试,下面是实验截图,主要的流程分析通过代码进行:
    do_fork断点
    这里写图片描述
    copy_process断点
    这里写图片描述
    dup_task_struct断点
    这里写图片描述

    进程创建分析

    Linux中创建进程一共有三个函数:

    1. fork,创建子进程
    2. vfork,与fork类似,但是父子进程共享地址空间,而且子进程先于父进程运行。
    3. clone,主要用于创建线程
      这里值得注意的是,Linux中得线程是通过模拟进程实现的,较新的内核使用的线程库一般都是NPTL。

    下面通过fork系统调用的实现,观察Linux系统中进程的创建步骤。

    进程创建的大概过程

    通过之前的学习,我们知道fork是通过触发0x80中断,陷入内核,来使用内核提供的提供调用。:

    SYSCALL_DEFINE0(fork)
    {
    return do_fork(SIGCHLD, 0, 0, NULL, NULL);
    }
    #endif
    
    SYSCALL_DEFINE0(vfork)
    {
    return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, 0,
    0, NULL, NULL);
    }
    
    SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
     int __user *, parent_tidptr,
     int __user *, child_tidptr,
     int, tls_val)
    {
    return do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr);
    }
    

      通过上面的精简代码精简,我们可以看出,fork、vfork和clone这三个函数最终都是通过do_fork函数实现的。

    我们追踪do_fork的代码:

    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;
    
    // ...
    
    // 复制进程描述符,返回创建的task_struct的指针
    p = copy_process(clone_flags, stack_start, stack_size,
             child_tidptr, NULL, trace);
    
    if (!IS_ERR(p)) {
        struct completion vfork;
        struct pid *pid;
    
        trace_sched_process_fork(current, p);
    
        // 取出task结构体内的pid
        pid = get_task_pid(p, PIDTYPE_PID);
        nr = pid_vnr(pid);
    
        if (clone_flags & CLONE_PARENT_SETTID)
            put_user(nr, parent_tidptr);
    
        // 如果使用的是vfork,那么必须采用某种完成机制,确保父进程后运行
        if (clone_flags & CLONE_VFORK) {
            p->vfork_done = &vfork;
            init_completion(&vfork);
            get_task_struct(p);
        }
    
        // 将子进程添加到调度器的队列,使得子进程有机会获得CPU
        wake_up_new_task(p);
    
        // ...
    
        // 如果设置了 CLONE_VFORK 则将父进程插入等待队列,并挂起父进程直到子进程释放自己的内存空间
        // 保证子进程优先于父进程运行
        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;
    }
    

    我们通过上面的代码,可以看出,do_fork大概做了这么几件事情:

    1. 调用copy_process,将当前进程复制一份出来为子进程,并且为子进程设置相应地上下文信息。
    2. 初始化vfork的完成处理信息(如果是vfork调用)
    3. 调用wake_ up_ new_task,将子进程放入调度器的队列中,此时的子进程就可以被调度进程选中,得以运行。
    4. 如果是vfork调用,需要阻塞父进程,知道子进程执行exec。
      上面的过程对vfork稍微做了处理,因为vfork必须保证子进程优先运行,执行exec,替换自己的地址空间。

    抛开vfork,进程创建的大部分过程都在copy_process函数中,下面我们详细观察这个函数。

    进程创建的关键-copy_process

    copy_process的代码非常复杂,这里我精简了大部分,只留下最重要的一些:

    /*
    创建进程描述符以及子进程所需要的其他所有数据结构
    为子进程准备运行环境
    */
    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)
    {
    int retval;
    struct task_struct *p;
    
    // 分配一个新的task_struct,此时的p与当前进程的task,仅仅是stack地址不同
    p = dup_task_struct(current);
    
    // 检查该用户的进程数是否超过限制
    if (atomic_read(&p->real_cred->user->processes) >=
            task_rlimit(p, RLIMIT_NPROC)) {
        // 检查该用户是否具有相关权限,不一定是root
        if (p->real_cred->user != INIT_USER &&
            !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN))
            goto bad_fork_free;
    }
    
    retval = -EAGAIN;
    // 检查进程数量是否超过 max_threads,后者取决于内存的大小
    if (nr_threads >= max_threads)
        goto bad_fork_cleanup_count;
    
    // 初始化自旋锁
    
    // 初始化挂起信号
    
    // 初始化定时器
    
    // 完成对新进程调度程序数据结构的初始化,并把新进程的状态设置为TASK_RUNNING
    retval = sched_fork(clone_flags, p);
    // .....
    
    // 复制所有的进程信息
    // copy_xyz
    
    // 初始化子进程的内核栈
    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号
        pid = alloc_pid(p->nsproxy->pid_ns_for_children);
        if (!pid)
            goto bad_fork_cleanup_io;
    }
    
    /* ok, now we should be set up.. */
    // 设置子进程的pid
    p->pid = pid_nr(pid);
    // 如果是创建线程
    if (clone_flags & CLONE_THREAD) {
        p->exit_signal = -1;
        // 线程组的leader设置为当前线程的leader
        p->group_leader = current->group_leader;
        // tgid是当前线程组的id,也就是main进程的pid
        p->tgid = current->tgid;
    } else {
        if (clone_flags & CLONE_PARENT)
            p->exit_signal = current->group_leader->exit_signal;
        else
            p->exit_signal = (clone_flags & CSIGNAL);
        // 创建的是进程,自己是一个单独的线程组
        p->group_leader = p;
        // tgid和pid相同
        p->tgid = p->pid;
    }
    
    if (clone_flags & (CLONE_PARENT|CLONE_THREAD)) {
        // 如果是创建线程,那么同一线程组内的所有线程、进程共享parent
        p->real_parent = current->real_parent;
        p->parent_exec_id = current->parent_exec_id;
    } else {
        // 如果是创建进程,当前进程就是子进程的parent
        p->real_parent = current;
        p->parent_exec_id = current->self_exec_id;
    }
    
    // 将pid加入PIDTYPE_PID这个散列表
    attach_pid(p, PIDTYPE_PID);
    // 递增 nr_threads的值
    nr_threads++;
    
    // 返回被创建的task结构体指针
    return p;
    }
    

    看完这份精简代码,我们总结出copy_process的大体流程:

    1. 检查各种标志位(已经省略)
    2. 调用dup_ task_ struct复制一份task_struct结构体,作为子进程的进程描述符。
    3. 检查进程的数量限制。
    4. 初始化定时器、信号和自旋锁。
    5. 初始化与调度有关的数据结构,调用了sched_ fork,这里将子进程的state设置为TASK_RUNNING。
    6. 复制所有的进程信息,包括fs、信号处理函数、信号、内存空间(包括写时复制)等。
    7. 调用copy_thread,这又是关键的一步,这里设置了子进程的堆栈信息。
    8. 为子进程分配一个pid
    9. 设置子进程与其他进程的关系,以及pid、tgid等。这里主要是对线程做一些区分。
      进一步追踪dup_task_struct

    简化后的代码如下:

    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;
    
    // 分配一个task_struct结点
    tsk = alloc_task_struct_node(node);
    if (!tsk)
        return NULL;
    
    // 分配一个thread_info结点,其实内部分配了一个union,包含进程的内核栈
    // 此时ti的值为栈底,在x86下为union的高地址处。
    ti = alloc_thread_info_node(tsk, node);
    if (!ti)
        goto free_tsk;
    
    err = arch_dup_task_struct(tsk, orig);
    if (err)
        goto free_ti;
    
    // 将栈底的值赋给新结点的stack
    tsk->stack = ti;
    
    // ...
    
    // 返回新申请的结点
    return tsk;
    }
    dup_task_struct的代码要结合一个联合体的定义来分析。
    
    union thread_union 
    {
        struct thread_info thread_info;
        unsigned long stack[THREAD_SIZE/sizeof(long)];
    };
    

      这个联合体的定义非常关键。我们知道x86体系结构的栈空间,按照从高到低的方式增长。而C中的结构体,是按从低到高的方式使用。
      这样我们可以声明一个联合体,低地址用作thread_info,高地址用作栈底。这样做还有一个好处,就是thread_info中存放着一个task_struct的指针,这样我们根据栈底地址就可以通过thread_info快速定位到进程对应的task_struct指针。

    上面的dup_task_struct中:

    1. 先调用alloc_task_struct_node分配一个task_struct结构体。
    2. 调用alloc_thread_info_node,分配了一个union,注意,这里不仅仅分配了一个thread_info结构体,还分配了一个stack数组。返回值为ti,实际上就是栈底。
    3. tsk->stack = ti;这句话,就是将栈底的地址赋给task的stack变量。
      所以,最后为子进程分配了内核栈空间。
      执行完dup_task_struct之后,子进程和父进程的task结构体,除了stack指针之外,完全相同!

    进一步追踪copy_thread函数

    上面的copy_process中,我们提到copy_thread函数为子进程准备了上下文堆栈信息。代码如下:

    // 初始化子进程的内核栈
    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);
    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();
    // 子进程的eax置为0,所以fork的子进程返回值为0
    childregs->ax = 0;
    if (sp)
        childregs->sp = sp;
    
    // 子进程从ret_from_fork开始执行
    p->thread.ip = (unsigned long) ret_from_fork;
    task_user_gs(p) = get_user_gs(current_pt_regs());
    
    return err;
    }
    

    我们看到,copy_thread的流程如下:

    1. 获取子进程寄存器信息的存放位置
    2. 对子进程的thread.sp赋值,将来子进程运行,这就是子进程的esp寄存器的值。
    3. 如果是创建内核线程,那么它的运行位置是ret_from_kernel_thread,将这段代码的地址赋给thread.ip,之后准备其他寄存器信息,退出
    4. 将父进程的寄存器信息复制给子进程。
    5. 将子进程的eax寄存器值设置为0,所以fork调用在子进程中的返回值为0.
    6. 子进程从ret_from_fork开始执行,所以它的地址赋给thread.ip,也就是将来的eip寄存器。
      从上面的流程中,我们看出,子进程复制了父进程的上下文信息,仅仅对某些地方做了改动,运行逻辑和父进程完全一致。

    另外,我们得出结论,子进程从ret_from_fork处开始执行。

    新进程的执行

    上文已经得知,新进程从ret_from_fork处开始执行,子进程的运行是由这几处保证的:

    1. dup_task_struct中为其分配了新的堆栈
    2. copy_process中调用了sched_fork,将其置为TASK_RUNNING
    3. copy_thread中将父进程的寄存器上下文复制给子进程,这是非常关键的一步,这里保证了父子进程的堆栈信息是一致的。
    4. 将ret_from_fork的地址设置为eip寄存器的值,这是子进程的第一条指令。
      创建进程时的函数调用堆栈

    在gdb中使用bt命令即可。见截图:

    对进程创建的理解

    Linux中所有的进程创建都是基于复制的方式,然后对子进程做一些特殊的处理。
    而Linux中得线程,又是一种特殊的进程。

    展开全文
  • Zygote进程创建过程(Android 8.1)

    万次阅读 2019-08-13 16:10:29
    本篇文章的内容中,我将要给大家介绍Android开发中最长见到的一个底层进程——...它的启动过程从init进程启动开始,到Zygote进程真正运行起来,并且进行相关初始化完成为止。 我们来详细分析下Zygote进程的启动过程...

    本篇文章的内容中,我将要给大家介绍Android开发中最长见到的一个底层进程——Zygote进程,它是我们开发的所有APP进程的父进程。可以说,Android中所有的Java进程都是由Zygote进程fork出来的。

    那面Zygote进程又是如何启动的呢?它的启动过程从init进程启动开始,到Zygote进程真正运行起来,并且进行相关初始化完成为止。

    我们来详细分析下Zygote进程的启动过程。

    说明:我们的源码是基于Android 8.1系统进行分析的。

     

    init进程的启动

    init进程是Android系统启动的第一个进程。我们来看init进程是如何启动的?(源码位置:system/core/init/init.cpp)

    Linux内核启动的是从start_kernel函数开始的,start_kernel是所有Linux平台进入系统内核初始化后的入口函数,它主要完成一系列内核初始化相关工作,并且调用第一个用户进程——init进程。init启动之后,也就代表系统已经顺利地启动了Linux内核。

     

    我们来看init进程的入口函数——init.cpp的main()方法:

    int main(int argc, char** argv) {
    
        if (!strcmp(basename(argv[0]), "ueventd")) {//1、如果文件名是"ueventd",则执行守护进程ueventd的主函数ueventd_main()
    
            return ueventd_main(argc, argv);
    
        }
    
            if (!strcmp(basename(argv[0]), "watchdogd")) {//2、如果文件名是"watchdogd",则执行看门狗守护进程的主函数
    
            return watchdogd_main(argc, argv);
    
        }
    
        ……
    
        add_environment("PATH", _PATH_DEFPATH);//3、设置环境变量
    
        bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
    
        if (is_first_stage) {
    
            // Clear the umask.
    
            umask(0);
    
            //4、创建一些基本的目录,包括/dev、/porc、/sysfc等。同时把一些文件系统,如tmpfs、devpt、proc、sysfs等mount到项目的目录。
    
            mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
    
            mkdir("/dev/pts", 0755);
    
            mkdir("/dev/socket", 0755);
    
            mount("devpts", "/dev/pts", "devpts", 0, NULL);
    
            #define MAKE_STR(x) __STRING(x)
    
            mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
    
            // Don't expose the raw commandline to unprivileged processes.
    
            chmod("/proc/cmdline", 0440);
    
            gid_t groups[] = { AID_READPROC };
    
            setgroups(arraysize(groups), groups);
    
            mount("sysfs", "/sys", "sysfs", 0, NULL);
    
            mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
    
            mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
    
            mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
    
            mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
    
            //5、SELinux初始化逻辑
    
            // Set up SELinux, loading the SELinux policy.
    
            selinux_initialize(true);
    
            // We're in the kernel domain, so re-exec init to transition to the init domain now
    
            // that the SELinux policy has been loaded.
    
            if (selinux_android_restorecon("/init", 0) == -1) {
    
                PLOG(ERROR) << "restorecon failed";
    
                security_failure();
    
            }
    
            ……
    
        }
    
        ……
    
        close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));//6、在/dev目录下创建一个空文件".booting"表示初始化正在进行。初始化结束后,这个文件会被删除。
    
        // Propagate the kernel variables to internal variables
    
        // used by init as well as the current required properties.
    
        export_kernel_boot_props();//7、主要是调用property_init()函数来初始化Android的属性系统。
    
        ……
    
        epoll_fd = epoll_create1(EPOLL_CLOEXEC);//8、调用epoll_create1创建epoll句柄,如果创建失败,则退出。
    
        if (epoll_fd == -1) {
    
            PLOG(ERROR) << "epoll_create1 failed";
    
            exit(1);
    
        }
    
        signal_handler_init();//9、调用signal_handler_init()函数,主要是装载进程信号处理器。当子进程被kill之后,会在父进程接受一个信号。防止称为僵尸进程的子进程占用程序表的空间。
    
        ……
    
        //10、解析init.rc文件
    
        std::string bootscript = GetProperty("ro.boot.init_rc", "");
    
        if (bootscript.empty()) {
    
            parser.ParseConfig("/init.rc");
    
            parser.set_is_system_etc_init_loaded(
    
                parser.ParseConfig("/system/etc/init"));
    
            parser.set_is_vendor_etc_init_loaded(
    
                parser.ParseConfig("/vendor/etc/init"));
    
            parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
    
        } else {
    
            parser.ParseConfig(bootscript);
    
            parser.set_is_system_etc_init_loaded(true);
    
            parser.set_is_vendor_etc_init_loaded(true);
    
            parser.set_is_odm_etc_init_loaded(true);
    
        }
    
        ……
        
    }

     

    执行过程如上,主要步骤我们通过注释可以清楚的了解到。

     

    init进程启动过程中,会执行init.rc脚本文件(位置: /system/core/rootdir/init.rc)。它通过解析init.rc脚本来构建出系统的初始形态,解析init.rc会把一条条命令映射到内存中,然后依次启动。init.rc里面的顺序大致顺序如下:on early-init -> init -> late-init -> boot。

    其中,init.rc会执行Zygote相关的启动脚本。所以说,Zygote进程是Linux系统的init进程通过解析配置脚本来进行启动的。脚本位置:/system/core/rootdir/init.zygote64.rc (64位)。

     

    Zygote进程

    通常,子进程被fork出来后,会继续执行系统调用exec,exec将用一个新的可执行文件的内容替换当前进程的代码段、数据段、堆和栈段。Fork加exec 是Linux启动应用的标准做法,init进程也是这样来启动的各种服务的。

    Zygote创建应用程序时却只使用了fork,没有调用exec。Zygote初始化时会创建创建虚拟机,同时把需要的系统类库和资源文件加载到内存里面。Zygote fork出子进程后,这个子进程也继承了能正常工作的虚拟机和各类系统资源,接下来子进程只需要装载APK文件的字节码文件就可以运行了。这样应用程序的启动时间就会大大缩短。

    init.rc脚本

    我们来看init.rc脚本是如何启动Zygote进程的:

    import /init.${ro.zygote}.rc //这里通过ro.zygote属性来控制启动不同版本的Zyogte进程。例如,ro.zygote=zygote64_32(zygote32/zygote32_64/zygote64
    
    )
    
    ……
    
    on late-init
    
        # Now we can start zygote for devices with file based encryption
    
        trigger zygote-start
    
        ……
    
    # It is recommended to put unnecessary data/ initialization from post-fs-data
    
    # to start-zygote in device's init.rc to unblock zygote start.
    
    on zygote-start && property:ro.crypto.state=unencrypted
    
        # A/B update verifier that marks a successful boot.
    
        exec_start update_verifier_nonencrypted
    
        start netd
    
        start zygote
    
        start zygote_secondary
    
    
    
    on zygote-start && property:ro.crypto.state=unsupported
    
        # A/B update verifier that marks a successful boot.
    
        exec_start update_verifier_nonencrypted
    
        start netd
    
        start zygote
    
        start zygote_secondary
    
    
    
    on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
    
        # A/B update verifier that marks a successful boot.
    
        exec_start update_verifier_nonencrypted
    
        start netd
    
        start zygote
    
        start zygote_secondary
    
    ……

     

    我们可以看到,Zygote进程是通过init.rc脚本,在init进程中启动的(以service的方式启动),详细过程后面会讲到。

    另外,在init.rc中,我们可以看到anr文件的权限设置:

    mkdir /data/anr 0775 system system

    这里可以看到其实anr文件的目录其实对我们正常应用的APP是开放了rx权限(读、执行权限),但是非系统应用在Android 5.0之后就读取不到anr日志了,这个原因是因为在Android5.0之后,Android默认开启了SELinux权限控制。

    ro.zygote变量

    我们看到,脚本文件使用了另一个.rc文件,文件名称使用了ro.zygote变量,它的值有四种:

    • zygote32

    • zygote32_64

    • zygote64

    • zygote64_32

    分别对应了4个.rc文件(在init.rc同目录下):

    • init.zygote32

    • init.zygote64

    • init.zygote32_64

    • init.zygote64_32

    为什么会有4个Zygote脚本文件呢?

    这其实代表了Andorid系统支持4种运行模式:

    1. 纯32位模式:属性ro.zygote的值为zygote32

    2. 混32位模式(即32位为主,64位为辅)模式:属性ro.zygote的值为zygote32_64

    3. 纯64位模式:属性ro.zygote的值为zygote64

    4. 混64位模式(即 64位为主,32位为辅)模式:属性ro.zygote值为zygote64_32

    Zygote进程的脚本文件

    Zygote对应的配置脚本文件,描述了init该如何启动Zygote进程。

    我们以64位CPU架构的脚本文件init.zygote64.rc作为示例:

    service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    
        class main
    
        priority -20
    
        user root
    
        group root readproc
    
        socket zygote stream 660 root system
    
        onrestart write /sys/android_power/request_state wake
    
        onrestart write /sys/power/state on
    
        onrestart restart audioserver
    
        onrestart restart cameraserver
    
        onrestart restart media
    
        onrestart restart netd
    
        onrestart restart wificond
    
        writepid /dev/cpuset/foreground/tasks

    它使用的是Android init配置脚本的语法,我们重点关注第一行内容:

    • service zygote:它告诉init进程,我们现在要配置一个名为zygote的服务(这里的服务是init内部的概念)。

    • /system/bin/app_process64:指明zygote服务对应的二进制文件的路径。init进程会fork一个子进程来运行指定的程序。对应zygote而言,这个程序就是/system/bin/app_process。

    • -Xzygote /system/bin --zygote --start-system-server:传递给app_process的启动参数。

    当init进程真的启动zygote服务的时候,就会走到service_start()函数(init.cpp)。

    下面我们来看Zygote真正的启动代码。

    Zygote进程启动过程

    Zygote进程的启动,涉及到了init.cpp、service.cpp、app_main.cpp以及脚本文件init.rc、init.zygote64.rc等文件。详细过程如图:

     

    启动过程中的相关类及类的功能如下:

     

    init.cpp

    • 初始化相关配置。

    • 通过handle_control_message()监听消息,执行Zygote服务。

    • 调用service.cpp的Restart()方法。

     

    service.cpp

    • 调用Start()方法。

    • fork Zygote进程。

     

    app_main.cpp

    • Zygote进程的入口函数main()

    • 创建AppRuntime对象

    • 调用AppRuntime对象的start方法

     

    AndroidRuntime.cpp

    • AppRuntime的start方法实际指向AndroidRuntime的start方法

    • 创建了一个JniInvocation的实例,并且调用它的成员函数init来初始化JNI环境

    • 执行startVm,创建虚拟机及其对应的JNI接口

    • 执行startReg,注册JNI函数

    • 执行env.CallStaticVoidMethod调用到Java层,初始化Zygote进程

     

    Zygoteinit.java

    • 执行main方法

    • 调用ZygoteServier的registerServerSocket(socketName)方法注册Zygote的socket监听接口,用来启动应用程序的消息

    • 调用preload()方法装载系统资源,包括系统预加载类、Framework资源和openGL的资源。这样当程序被fork处理后,应用的进程内已经包含了这些系统资源,大大节省了应用的启动时间。

    • 调用forkSystemServer()方法启动SystemServer进程

    • 调动runSelectLoop方法来监听和处理启动应用的请求

    app_main.cpp的main函数

    我们来看重点来看下app_main.cpp的main函数:

    int main(int argc, char* const argv[])
    
    {
    
        if (!LOG_NDEBUG) {
    
          String8 argv_String;
    
          for (int i = 0; i < argc; ++i) {
    
            argv_String.append("\"");
    
            argv_String.append(argv[i]);
    
            argv_String.append("\" ");
    
          }
    
          ALOGV("app_process main with argv: %s", argv_String.string());
    
        }
    
    
    
        AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));//1、创建AppRuntime对象
    
        // Process command line arguments
    
        // ignore argv[0]
    
        argc--;
    
        argv++;
    
            ……
    
        bool known_command = false;
    
        //****************2、从init.rc传入的参数 为 -Xzygote /system/bin --zygote --start-system-server
    
        int i;
    
        for (i = 0; i < argc; i++) {
    
            if (known_command == true) {
    
              runtime.addOption(strdup(argv[i]));
    
              // The static analyzer gets upset that we don't ever free the above
    
              // string. Since the allocation is from main, leaking it doesn't seem
    
              // problematic. NOLINTNEXTLINE
    
              ALOGV("app_process main add known option '%s'", argv[i]);
    
              known_command = false;
    
              continue;
    
            }
    
    
    
            for (int j = 0;
    
                 j < static_cast<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0]));
    
                 ++j) {
    
              if (strcmp(argv[i], spaced_commands[j]) == 0) {
    
                known_command = true;
    
                ALOGV("app_process main found known command '%s'", argv[i]);
    
              }
    
            }
    
    
    
            if (argv[i][0] != '-') {
    
                break;
    
            }
    
            if (argv[i][1] == '-' && argv[i][2] == 0) {
    
                ++i; // Skip --.
    
                break;
    
            }
    
    
    
    
    
            runtime.addOption(strdup(argv[i]));
    
            // The static analyzer gets upset that we don't ever free the above
    
            // string. Since the allocation is from main, leaking it doesn't seem
    
            // problematic. NOLINTNEXTLINE
    
            ALOGV("app_process main add option '%s'", argv[i]);
    
        }
    
    
    
        // Parse runtime arguments.  Stop at first unrecognized option.
    
        bool zygote = false;
    
        bool startSystemServer = false;
    
        bool application = false;
    
        String8 niceName;
    
        String8 className;
    
    
    
        //****************3、将上面的内容赋给相应的变量
    
        ++i;  // Skip unused "parent dir" argument.
    
        while (i < argc) {
    
            const char* arg = argv[i++];
    
            if (strcmp(arg, "--zygote") == 0) {
    
                zygote = true;
    
                niceName = ZYGOTE_NICE_NAME;//niceName将被设置为app_process的进程名,32位机为“zygote”,64位机为“zygote64”。
    
            } else if (strcmp(arg, "--start-system-server") == 0) {
    
                startSystemServer = true;
    
            } else if (strcmp(arg, "--application") == 0) {
    
                application = true;
    
            } else if (strncmp(arg, "--nice-name=", 12) == 0) {
    
                niceName.setTo(arg + 12);
    
            } else if (strncmp(arg, "--", 2) != 0) {
    
                className.setTo(arg);
    
                break;
    
            } else {
    
                --i;
    
                break;
    
            }
    
        }
    
    
    
        //*************4、准备启动ZygoteInit类或者RuntimeInit类,这里根据类名是否存在为空,来区别是非zyoget模式和zyoget模式.
    
        Vector<String8> args;
    
        if (!className.isEmpty()) {
    
            // We're not in zygote mode, the only argument we need to pass
    
            // to RuntimeInit is the application argument.
    
            //
    
            // The Remainder of args get passed to startup class main(). Make
    
            // copies of them before we overwrite them with the process name.
    
            args.add(application ? String8("application") : String8("tool"));
    
            runtime.setClassNameAndArgs(className, argc - i, argv + i);
    
    
    
    
    
            if (!LOG_NDEBUG) {
    
              String8 restOfArgs;
    
              char* const* argv_new = argv + i;
    
              int argc_new = argc - i;
    
              for (int k = 0; k < argc_new; ++k) {
    
                restOfArgs.append("\"");
    
                restOfArgs.append(argv_new[k]);
    
                restOfArgs.append("\" ");
    
              }
    
              ALOGV("Class name = %s, args = %s", className.string(), restOfArgs.string());
    
            }
    
        } else {
    
            // We're in zygote mode.
    
            maybeCreateDalvikCache();
    
    
    
    
    
            if (startSystemServer) {
    
                args.add(String8("start-system-server"));
    
            }
    
    
    
    
    
            char prop[PROP_VALUE_MAX];
    
            if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
    
                LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
    
                    ABI_LIST_PROPERTY);
    
                return 11;
    
            }
    
    
    
    
    
            String8 abiFlag("--abi-list=");
    
            abiFlag.append(prop);
    
            args.add(abiFlag);
    
    
    
    
    
            // In zygote mode, pass all remaining arguments to the zygote
    
            // main() method.
    
            for (; i < argc; ++i) {
    
                args.add(String8(argv[i]));
    
            }
    
        }
    
    
    
        //5、进程的名称修改
    
        if (!niceName.isEmpty()) {
    
            runtime.setArgv0(niceName.string(), true /* setProcName */);
    
        }
    
    
    
        //6、启动Java类
    
        if (zygote) {
    
            runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    
        } else if (className) {
    
            runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    
        } else {
    
            fprintf(stderr, "Error: no class name or --zygote supplied.\n");
    
            app_usage();
    
            LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
    
        }
    
    }

    主要过程如下:

    1、创建AppRuntime对象。

     

    2、处理从init.rc传入的参数 为 -Xzygote /system/bin --zygote --start-system-server,传递给AppRuntime对象。

    • -Xzygote是传递给虚拟机的参数

    • /system/bin 是 parent dir(程序运行目录)

    • --zygote表示以zygote模式启动

     

    3、将上面的内容赋给相应的变量

    以-Xzygote /system/bin --zygote --start-system-server为例,结果如下:

    • 变量 parentDir 等于/system/bin

    • 变量 niceName 等于 zyoget

    • 变量 startSystemServer 等于 true

    • 变量 zygote 等于 true

     

    4、准备启动ZygoteInit类或者RuntimeInit类,这里根据类名是否存在为空,来区别是非zyoget模式和zyoget模式.

     

    5、如果niceName不为空,则将本进程的名称修改为参数** --nice-name** 指定的字符串。缺省的情况下,niceName 的值为"zygote"或者"zygote64"。其中set_process_name函数用来改变进程的mi9ngcheng。setArgv0函数是用来替换启动参数串中的"app_process"为参数。

     

    6、启动Java类,如果启动参数带有 "--zygote"。则执行ZygoteInit。

    分三种情况:

    • zygote 模式,即启动ZygoteInit

    • 非zygote 模式,即启动RuntimeInit

    • 以上两种均不是

    Java层的ZygoteInit的main()方法

    我们再来分析下上述过程中的ZygoteInit的main()方法:

    public static void main(String argv[]) {
    
        ZygoteServer zygoteServer = new ZygoteServer();//1、创建ZygoteServer对象
    
    
    
        // Mark zygote start. This ensures that thread creation will throw
    
        // an error.
    
        ZygoteHooks.startZygoteNoThreadCreation();
    
    
    
        // Zygote goes into its own process group.
    
        try {
    
            Os.setpgid(0, 0);
    
        } catch (ErrnoException ex) {
    
            throw new RuntimeException("Failed to setpgid(0,0)", ex);
    
        }
    
        final Runnable caller;
    
        try {
    
            // Report Zygote start time to tron unless it is a runtime restart
    
            if (!"1".equals(SystemProperties.get("sys.boot_completed"))) {
    
                MetricsLogger.histogram(null, "boot_zygote_init",
    
                        (int) SystemClock.elapsedRealtime());
    
            }
    
    
    
            String bootTimeTag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing";
    
            TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,
    
                    Trace.TRACE_TAG_DALVIK);
    
            bootTimingsTraceLog.traceBegin("ZygoteInit");
    
            RuntimeInit.enableDdms();
    
            //***********2、解析参数
    
            boolean startSystemServer = false;
    
            String socketName = "zygote";
    
            String abiList = null;
    
            boolean enableLazyPreload = false;
    
            for (int i = 1; i < argv.length; i++) {
    
                if ("start-system-server".equals(argv[i])) {
    
                    startSystemServer = true;
    
                } else if ("--enable-lazy-preload".equals(argv[i])) {
    
                    enableLazyPreload = true;
    
                } else if (argv[i].startsWith(ABI_LIST_ARG)) {
    
                    abiList = argv[i].substring(ABI_LIST_ARG.length());
    
                } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
    
                    socketName = argv[i].substring(SOCKET_NAME_ARG.length());
    
                } else {
    
                    throw new RuntimeException("Unknown command line argument: " + argv[i]);
    
                }
    
            }
    
    
    
            if (abiList == null) {
    
                throw new RuntimeException("No ABI list supplied.");
    
            }
    
            //3、调用registerZygoteSocket(socketName)方法注册Zygote的socket监听接口,用来启动应用程序的消息
    
            zygoteServer.registerServerSocket(socketName);
    
            // In some configurations, we avoid preloading resources and classes eagerly.
    
            // In such cases, we will preload things prior to our first fork.
    
            if (!enableLazyPreload) {
    
                bootTimingsTraceLog.traceBegin("ZygotePreload");
    
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
    
                    SystemClock.uptimeMillis());
    
                preload(bootTimingsTraceLog);//4、调用preload()方法装载系统资源,包括系统预加载类、Framework资源和openGL的资源。这样当程序被fork处理后,应用的进程内已经包含了这些系统资源,大大节省了应用的启动时间。
    
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
    
                    SystemClock.uptimeMillis());
    
                bootTimingsTraceLog.traceEnd(); // ZygotePreload
    
            } else {
    
                Zygote.resetNicePriority();
    
            }
    
    
    
            // Do an initial gc to clean up after startup
    
            bootTimingsTraceLog.traceBegin("PostZygoteInitGC");
    
            gcAndFinalize();
    
            bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC
    
    
    
            bootTimingsTraceLog.traceEnd(); // ZygoteInit
    
            // Disable tracing so that forked processes do not inherit stale tracing tags from
    
            // Zygote.
    
            Trace.setTracingEnabled(false, 0);
    
    
    
            // Zygote process unmounts root storage spaces.
    
            Zygote.nativeUnmountStorageOnInit();
    
    
    
            // Set seccomp policy
    
            Seccomp.setPolicy();
    
    
    
            ZygoteHooks.stopZygoteNoThreadCreation();
    
    
    
            if (startSystemServer) {
    
                Runnable r = forkSystemServer(abiList, socketName, zygoteServer);//5、启动SystemServer进程
    
    
    
                // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
    
                // child (system_server) process.
    
                if (r != null) {
    
                    r.run();
    
                    return;
    
                }
    
            }
    
    
    
            Log.i(TAG, "Accepting command socket connections");
    
    
    
            // The select loop returns early in the child process after a fork and
    
            // loops forever in the zygote.
    
            caller = zygoteServer.runSelectLoop(abiList);//6、调动runSelectLoop方法来监听和处理启动应用的请求
    
        } catch (Throwable ex) {
    
            Log.e(TAG, "System zygote died with exception", ex);
    
            throw ex;
    
        } finally {
    
            zygoteServer.closeServerSocket();
    
        }
    
    
    
    
    
        // We're in the child process and have exited the select loop. Proceed to execute the
    
        // command.
    
        if (caller != null) {
    
            caller.run();
    
        }
    
    }

    1、创建ZygoteServer对象。

     

    2、解析参数。

     

    3、调用registerZygoteSocket(socketName)方法注册Zygote的socket监听接口,用来启动应用程序的消息。

    为zygote命令注册一个socket连接的服务端socket。init进程会根据这条选项来创建一个"AF_UNIX"socket,并把它的句柄放到环境变量"ANDROID_SOCKET_zygote"中。同理我们也可以这样得到句柄,得到句柄后,new了一个FileDescriptor对象,并通过调用setInt$()方法来设置其值。最后new了LocalServerSocket对象,来创建本地的服务socket,并将其值保存在全局变量sServerSocket中。

     

    4、调用preload()方法预加载资源。

    为了加快应用程序的启动,Android把系统公用的Java类和一部分Framework的资源保存在zygote中了,这样就可以保证zygote进程fork子进程的是共享的。

    • preloadClasses():预加载Java类

    • preloadResources():预加资源

    • preloadOpenGL():预加载OpenGL资源

    • preloadSharedLibraries():预计加载共享库

    • preloadTextResources():预加载文本资源

    • WebViewFactory.prepareWebViewInZygote():初始化WebView

     

    5、启动SystemServer进程。

    • 为fork准备参数parsedArgs

    • 调用Zygote.forkSystemServer()方法来创建system_server

    • 调用handleSystemServerProcess()方法执行system_server的剩余工作

     

    6、调动runSelectLoop方法来监听和处理启动应用的请求。

    ZygoteInit类的main()方法调用runSelectLoop()方法来监听和处理启动应用的请求。

    执行zygote进程的循环。当来一个新的连接请求时,则建立接受并建立连接,并在连接中读取请求的命令。

     

    至此,Init进程以及Zygote进程的启动过程就分析完成了。

     

     

    展开全文
  • 关于Windows创建进程过程

    千次阅读 2017-10-09 00:17:05
    转载自:http://www.cnblogs.com/HsinTsao/p/6534317.html从操作系统的角度来说创建进程步骤: 1.申请进程块 2.为进程分配内存资源 3.初始化进程块 4.将进程块链入就绪队列课本上的知识。。。 从CreateProcess的...
  • 进程创建过程

    2015-04-24 16:39:22
    进程创建过程无疑是最重要的操作系统处理过程之一,很多书和教材上说的最多的还是一些原理的部分,忽略了很多细节。比如,子进程复制父进程所拥有的资源,或者子进程和父进程共享相同的物理页面,拥有自己的地址...
  • Linux系统进程复习总结  1....进程就是内核对某个运行状态的数据集合的一种总结, 是进行系统资源分配和调度的基本单元 ...Linux内核在创建进程时的函数调用主要是通过上面三个函数执行的, 其中co
  • 但凡是硬件,都需要有操作系统去管理,只要有操作系统,就有进程的概念,就需要有创建进程的方式,一些操作系统只为一个应用程序设计,比如扫地机器人,一旦启动,所有的进程都已经存在。 而对于通用系统(跑很多...
  • 本篇文章通过fork函数的调用,来说明在Linux系统中创建一个新进程需要经历的过程
  • 通过调试,查看创建进程过程,如图三所示 图一 Fork() 图二 设置断点 图三 dup_task_struct调试中的代码 简析Linux内核创建一个新的进程过程 进程控制块(Processing ...
  • Linux创建进程的流程

    2020-07-03 23:38:03
    Linux系统中提供系统调用fork、vfork、clone的接口用于创建多进程,内核本身...而它们最终都会调用_do_fork()来创建进程或者内核线程。它们的区别是在于flag的不同,大致的流程如下图所示: _do_fork简要流程 ...
  • 进程创建与管理.docx

    2019-06-09 16:38:42
    设计一个有 N个进程并发执行的进程调度模拟程序。 进程调度算法:采用最高优先级优先的调度算法(即把处理机分配给优先级最高的进程)和先来先服务(若优先级相同)算法。
  • 有了之前的对进程和线程对象的学习的铺垫后,我们现在可以开始学习windows下的进程创建过程了,我将尝试着从源代码的层次来分析在windows下创建一个进程都要涉及到哪些步骤,都要涉及到哪些数据结构。   1. 相关...
  • Linux内核创建进程的全过程

    千次阅读 2016-05-26 20:48:57
    进程描述 进程描述符(task_struct) 用来描述进程的数据结构,可以理解为进程的属性。比如进程的状态、进程的标识(PID)等,都被封装在了进程描述符这个数据结构中,该数据结构被定义为task_struct 进程...
  • 此次实验就是了解内核创建一个新进程大致过程。为了简单,使用fork再用户态创建一个进程。代码如下:#include#include#includeintmain(intargc,char*argv[]){intpid;/*forkanotherprocess*/pid=fork();if(pid{/*...
  • 进程创建

    千次阅读 2016-10-07 10:08:11
    进程创建也就有两种方式:一是由操作系统创建,二是由父进程创建。 在系统启动时,操作系统会创建一些进程,他们承担着管理和分配系统资源的任务,这些进程通常被称为系统进程。 系统允许一个进程创建进程...
  • 什么是进程 进程就好比一个仓库,仅仅提供程序所需的资源...对于每个进程的4G空间,可大致分为4个区域 空指针赋值区 0x00000000 ——0x0000FFFF 64K的空闲空间,没有物理内存与之对应,所以通过空指针进行读写会出现异
  • 进程进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,每一个进程都是一个实体有属于自己的地址控件,进程也是一个执行的程序(也就是说一个运行中的程序...
  • 进程调度的基本过程

    2022-03-28 08:38:41
    进程是操作系统中的非常重要的软件资源,把一个可执行程序跑起来,系统中就会创建一个对应的进程 如果这个程序执行结束了,系统就会随之销毁对应的进程 进程就可以看成是一个程序的执行的"过程" 注意区分 程序 / 可...
  • 进程是怎么描述的?这是一个提纲挈领性的东西,它可以把内存管理,文件...记录进程的各种属性,描述进程的动态变化过程,而PCB是系统感知进程存在的唯一标志为了管理进程,内核必须对每个进程进行清晰的描述,进程描...
  • 进程创建而产生处于就绪状态。 进程因调用而运行,运行中的进程可能发生等待事件,比如访问的设备资源未满足,所以进程处于等待状态。 等待状态的进程因等待结束,进程就会处于就绪态。 另外,运行中的进程可能被...
  • 为了对任务管理(或者说进程管理)有个大体的认识,本章主要探究一下Linux内核中,任务创建相关内容。 任务创建 学过C语言的都知道,在当前进程中执行fork(),能够创建一个新进程。那么Linux内核中,第一个任务0...
  • Linux系统如何创建一个新进程

    万次阅读 2016-04-02 00:14:36
    对于Linux系统是如何创建一个新进程的,关于fork函数又是怎样具体实现系统调用的,我们通过这次实验来做一个初步的了解。 在做实验前我们要先了解下fork函数具体完成了哪些功能,fork函数是创建一个子进程,但是子...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 97,468
精华内容 38,987
关键字:

创建进程的大致过程