精华内容
下载资源
问答
  • 创建进程的过程
    千次阅读
    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进程创建过程

    千次阅读 2020-12-03 17:20:34
    那么Linux的进程是如何创建的呢? 这里一副图,我们先看个大概: 1、首先,我们得知道,我们的进程准备要干什么,这就需要写程序。如图右上角的.h .c文件,可以通过gcc编译成.o(可重定位目标文件,它是ELF...

    进程

    进程的产生极大地提高的cpu的利用率,进程是cpu的执行单元。cpu可以通过进程切换提高使用率。那么Linux的进程是如何创建的呢?
    这里一副图,我们先看个大概:
    在这里插入图片描述
    1、首先,我们得知道,我们的进程准备要干什么,这就需要写程序。如图右上角的.h .c文件,可以通过gcc编译成.o(可重定位目标文件,它是ELF(Relocatable File)文件的一种格式),里面包含了.text:编译好的二进制可执行代码,.data:已经初始化好的全局变量等,具体如下:

    .text:放编译好的二进制可执行代码
    .data:已经初始化好的全局变量
    .rodata:只读数据,例如字符串常量、const 的变量
    .bss:未初始化全局变量,运行时会置 0
    .symtab:符号表,记录的则是函数和变量
    .strtab:字符串表、字符串常量和变量名
    

    这与我们java代码编译是同理的。

    2、编译好文件之后,因为每个文件是松散的,互相都不知道各自有哪些可以调用的函数,属于两耳不闻窗外事的情况,无法协作完成一项任务。此时就需要进行链接。链接可分为静态链接和动态链接

    动态链接会创建一个动态链接库.so,它可以使得多个进程共享这些方法,类似于全局变量,静态链接只能被一个进程使用,类似于局部变量

    链接的过程在JVM加载class文件时也是同理的

    3、链接完之后,就会生成可执行文件,这个可执行文件也是ELF类型,是另外一种格式。这个我们暂时不用深究,知道就行,这个就是加载进程需要的程序了(后续会用到)。

    4、我们要知道,Linux创建进程不是从0直接创建的,而是通过一个父进程调用fork()复制一个相同的进程的形式来创建子进程的。
    这就类似于设计模式里面的原型模式了,从0到有比较费事,还是cv来得快

    fork()函数的介绍如下:

    pid_t fork( void);
    (pid_t 是一个宏定义,其实质是int 被定义在#includesys/types.h>中)
    返回值: 若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1
    

    使用fork函数的实例:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <unistd.h>
    extern int create_process (char* program, char** arg_list);
    int create_process (char* program, char** arg_list)
    {
    	 pid_t child_pid;
    	 child_pid = fork ();
    	 if(child_pid == -1) {
    	 	//出错了
    	 }
    	 if (child_pid != 0)
    	 	// 父进程
    	 	return child_pid;
    	 else {
    	 	// 新创建的子进程,可以执行自己的逻辑
    	 	execvp (program, arg_list);
    	 	abort ();
    	 }
     }
    

    5、创建完子进程,则需要自己调用程序代码了。这就像我们执行了 ./test.sh param可执行脚本后,操作系统要做的逻辑。

    • 接收执行的参数,如上面的param。此时还在用户态, 类似方法的传参
    • 开始进入内核态,执行系统调用,加载程序,分别调用了sys_execve、do_execve、load_elf_binary。注意第三步load_elf_binary,它的操作就是载入我们前面链接生成的elf文件,载入elf文件,进程就知道他需要干什么了。

    看到这里,你不妨回过头去看看第一张图上的流程,对照一下。

    拓展

    Linux操作系统一开始有一个0号进程(idle进程),它是操作系统启动时的第一个进程。没有它就没有后面的其他进程。在源码里面,它是通过set_task_stack_end_magic(&init_task),这里面有一个参数 init_task,定义是 struct task_struct init_task = INIT_TASK(init_task)创建的。

    有了0号进程,才fork出了1号进程(用户态进程的父进程),2号进程(内核态进程的父进程)。

    下面我们选一台机子,ps -ef看一下:
    在这里插入图片描述
    我们看到1号进程和2号进程的父进程是0号进程,下面几个进程父进程都是2号进程,都是内核进程。在cmd一列也可以看到,带中括号的是内核进程,不带中括号的是用户态进程

    展开全文
  • 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进程的启动过程就分析完成了。

     

     

    展开全文
  • Win32进程创建过程

    千次阅读 2020-04-09 22:30:15
    文章目录什么是进程进程内存空间的地址划分进程创建 什么是进程 运行在计算机上的程序,为当前的程序提供资源,举一个例子,进程相当于一个房子,房子里的东西由进程提供,房子里走动的人就是线程,进程仅仅提供...

    什么是进程

    运行在计算机上的程序,为当前的程序提供资源,举一个例子,进程相当于一个房子,房子里的东西由进程提供,房子里走动的人就是线程,进程仅仅提供资源,关于资源怎么用与进程无关。

    进程内存空间的地址划分

    分区x86 32位Windows
    空指针赋值区0x00000000~0x0000FFFF
    用户模式区0x00010000~0x7FFEFFFF
    64KB禁入区0x7FFF0000~0x7FFFFFFF
    内核0x80000000~0xFFFFFFFF

    虽然每个进程都有属于自己的4GB虚拟空间,但是只有前面的低2G是属于进程自己的,后面的高2G是所有进程公用的,在OD里我们附加一个进程,然后Alt+m就可以看一下它的内存分配情况了。
    在这里插入图片描述

    每一个进程就像一辆汽车,由许多个零件组成,这里的零件就是模块,我们可以按Alt+e查看这些组成进程的模块:
    在这里插入图片描述

    进程的创建

    当一个exe文件躺在我们的硬盘里的时候,它还不是一个进程,这里强调两点:

    1、每个进程都不是自己创建的,是由别的进程创建的。CreateProcess()
    
    2、进程的创建过程
    	2.1映射EXE文件
    	2.2创建内核对象eprocess
    	2.3映射系统dll(ntdll.dll)
    	2.4创建线程内核对象ethread
    	2.5系统启动线程
    		2.5.1映射DLL(ntdll.dll)
    		2.5.2线程开始执行
    

    每一个进程其实是由这个explorer.exe这个进程调用CreateProcess()函数来创建的:
    在这里插入图片描述
    那么这个CreateProcess()函数是如何把一个exe变成进程的呢?这就是上面的第二条:

    2、进程的创建过程
    	2.1  映射EXE文件
    	2.2  创建内核对象eprocess
    	2.3  映射系统dll(ntdll.dll)
    	2.4  创建线程内核对象ethread
    	2.5  系统启动线程
    		2.5.1  映射DLL(ntdll.dll)
    		2.5.2  线程开始执行
    

    我们用图来表示的的创建过程,
    在这里插入图片描述
    这里还没有跑完,还差最后一步,启动线程,在启动线程之前,还需要映射dll,一个PE文件一般是由一个exe程序加许多个dll组成的:

    这是笔者这里一个软件需要的dll
    在这里插入图片描述
    每个dll启动时也会带起别的dll,有一定的关联性,这就是为什么我们在OD里会看到那么多的dll,其实很多不是程序自带需的dll,而是需要的那个dll带起来的另一个dll,到这里程序才跑起来:
    在这里插入图片描述

    展开全文
  • 进程创建过程

    千次阅读 2019-02-25 16:07:26
    进程创建时,调用do_fork函数来创建进程,那么和调度相关的操作主要有两个,一个是sched_fork,这是对一个进程进行调度的初始化,另外一个就是wake_up_new_task,这个是把刚刚创建的子进程唤醒加入到调度器中...
  • 但凡是硬件,都需要有操作系统去管理,只要有操作系统,就有进程的概念,就需要有创建进程的方式,一些操作系统只为一个应用程序设计,比如扫地机器人,一旦启动,所有的进程都已经存在。 而对于通用系统(跑很多...
  • Windows操作系统下创建进程过程

    千次阅读 2013-11-20 16:26:13
    进程(Process)是具有一定独立功能的程序关于某个数据集合上...它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而被撤消,反映了一个程序在一定的数据集上运行的全部动态过程。  线程
  • 创建进程,等待进程,进程终止
  • 操作系统之进程创建

    万次阅读 2019-06-05 21:31:16
    操作系统基于某种原因决定创建一个新进程时,会按如下步骤操作: 为新进程分配一个唯一的进程标识符。此时,主进程表中会添加一个新表项,每个进程一个表项。 为进程分配空间。这包括进程映像中的所有元素。因此,...
  • 关于Windows创建进程过程

    千次阅读 2017-11-21 19:38:45
    当我们通过explorer.exe运行一个程序时,explorer.exe会调用CreateProcess函数请求系统为这个程序创建进程。当然,其它程序也可以调用CreateProcess函数创建进程。 系统在为进程分配内部资源,建立独立的地址空间...
  • 操作系统-15-进程创建

    千次阅读 2021-04-14 11:50:35
    从这一节起,我们将详细讲解进程的一生。进程如人生,进程的一生同样包含三个阶段,创建,运行和终结,本节是进程...1,操作系统创建进程:初始化 作为计算机的Boss,最初的进程是由操作系统创建的,操作系统在初始化的
  • 进程创建过程

    千次阅读 2019-05-18 17:47:47
    所有的进程都是由别的进程创建出来的,用鼠标双击运行一个程序的时候,实际上是由...进程创建过程: 映射EXE文件 创建内核对象EPROCESS(一个进程对应一个EPROCESS) 映射系统DLL(ntdll.dll) 创建线程内核对象ET...
  • 进程的创建,创建进程的两种方式

    万次阅读 2018-07-11 15:13:37
    一:进程创建 1,系统的初始化 2,一个进程在运行过程中开启了子进程 3,用户的交互式请求,而创建一个新进程(如双击qq) 4,一个批处理作业的初始化(只在大型机的批处理系统中应用) 关于进程创建,UNIX...
  • Linux下创建进程的三种方式

    千次阅读 2019-02-21 14:19:57
    在linux源码中这三个调用的执行过程是执行fork(),vfork(),clone()时,通过一个系统调用表映射到sys_fork(),sys_vfork(),sys_clone(),再在这三个函数中去调用do_fork()去做具体的创建进程工作。  fork   fork...
  • 进程创建的几种方式

    千次阅读 2021-06-13 11:28:15
    进程创建的几种方式 1.fork fork用于创建进程,系统调用函数:pid_t fork(void) 返回值是当前进程的pid 调用fork的进程我们称为父进程(父进程的返回值是0) fork原理: 当一个进程调用fork,操作系统会为该进程创建...
  • 在Linux系统下用fork()函数创建进程,lockf()函数对进程加锁,实现进程的创建撤销与控制。 四、源代码 1.进程的创建 #include<stdio.h> main() { int p1,p2; while((p1=fork())==-1); if(p1==0) putchar('b');...
  • 此方法会将在当前进程空间里,用新的程序覆盖掉当前程序,并执行新的程序,它们依然在同一个进程里,只是进程的内容发生了变化。 main11.go package main import ( "fmt" "syscall" ) func mai
  • linux进程创造 - 创建进程API及过程

    千次阅读 2016-11-20 00:56:20
    1. 创建进程函数API1.1 创建进程fork()fork的翻译为“叉子,分叉”,其实在unix编程中,我们来创建进程的时候是深有体会的,感觉创建一个进程就像是走到了一个岔路口,父进程和子进程在叉路口分道扬镳,所以我想这...
  • Linux学习--进程创建

    千次阅读 2016-07-28 21:19:21
    Linux学习--进程创建:fork和vfork
  • 例如子进程从fork返回后,调用exec*函数(进程替换马上详谈) fork调用失败的原因 众所周知,创建进程成本很高(时间+空间),系统中有太多进程时,资源不足 用户创建的进程数超出了限制,为了防止某些用户恶意创建。...
  • fork的应用及同时创建多个进程的分析1 进程及进程的创建1 进程2 利用fork创建进程2 利用fork同时创建多个进程3 创建多个进程的代码分析2级标题3级标题四级标题五级标题六级标题 1 进程及进程的创建 在linux编程中,...
  • 〖Python语法进阶篇③〗- 进程创建与常用方法

    千次阅读 多人点赞 2022-04-08 02:10:29
    该章节我们来学习一下在 Python 中去创建并使用多进程的方法,通过学习该章节,我们将可以通过创建多个进程来帮助我们提高脚本执行的效率。可以认为缩短脚本执行的时间,就是提高执行我们脚本的效率。接下来让我们都...
  • Ubuntu下进程创建fork

    千次阅读 2020-12-08 15:10:53
    二、如何创建进程1.使用fork函数创建进程2.代码实例总结 前言 进程在计算机中是不可或缺的一部分,是系统资源分配和调度的基本单位,进程是程序执行的实体。 一、进程是什么? 进程(Process)是计算机中的...
  • 多进程编程---创建进程 头文件:#include<unistd.h> 创建一个新进程 :pid_t fork(void) 如果出错返回-1 fork 调用一次,两次返回,原来的进程返回新进程的pid(父进程) 新进程中返回0(子进程) ...
  • 进程和线程的创建过程

    千次阅读 2014-04-13 04:31:55
    然而,通过Windows API 函数创建的进程也要接受Windows 子系统的管理,在这种情况下,仅仅内核部分的工作还不够,系统在创建进程过程中,还需要跟子系统打交道。另外,建立起独立的内存地址空间是Windows 创建进程...
  • linux进程创建过程与原理

    千次阅读 2012-07-01 15:28:25
    linux创建进程过程。 原文链接:http://blog.21ic.com/user1/8252/archives/2012/89761.html 系统允许一个进程创建进程,新进程即为子进程,子进程还可以创建新的子进程,形成进程树结构模型。整个linux...
  • Linux中fork()函数创建进程

    千次阅读 多人点赞 2019-05-09 22:05:09
    Linux系统中学习fork函数创建进程前言一.准备工作二.任务三.感想 前言    最近学习到操作系统原理中的进程同步的知识点时,为了加深对进程的了解,就实践了一下在Linux系统中fork()函数的使用。 一.准备工作 ...
  • 系统为它创建进程,并把进程控制块PCB的内容送到终端显示器上输出。 2、同时模拟内存空间为作业分配内存空间,并把结果用图形形象地表示出来,同样通过终端输出。 3、按进程的优先级的顺序撤消进程,同时通过终端...
  • Java对象创建过程

    万次阅读 2022-03-28 17:54:13
    java对象创建过程、对象的组成、对象头、实例数据、对齐填充、对象创建方式、new关键字、Class类的newInstance方法、Constructor类的newInstance方法、Object类的clone方法、反序列化、无父类的对象创建、有父类的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 790,260
精华内容 316,104
关键字:

创建进程的过程