zoukankan      html  css  js  c++  java
  • 《Linux/UNIX系统编程手册》第28章 详述进程创建和程序执行

    关键词:acct()、clone()、fork()/vfork()等等。

    内核对进程记账会使系统在每个进程结束后记录一条账单信息。

    Linux通过fork()、vfork()、clone()创建进程,其中clone()提供了更为精细的控制。

    接着比较了fork()、vfork()、clone()、fork()+exec()、vfork()+exec()创建进程熟读和虚拟内存消耗总量。

    exec()执行新程序替换当前进程,然后详细比较了exec()和fork()两者对创建进程属性的影响。

    1. 进程记账

    内核对进程记账的信息包括终止状态以及进程消耗的CPU时间。

    特权进程可利用acct()来打开和关闭进程记账功能。

    #define _BSD_SOURCE
    #include <unistd.h>
    int acct(const char *acctfile);
        Returns 0 on success, or –1 on error

    2. 系统调用clone()

    类似于fork()和vfork(),clone()也能创建一个新进程,clone()对进程创建步骤控制更为精准。

    #define _GNU_SOURCE
    #include <sched.h>
    int clone(int (*func) (void *), void *child_stack, int flags, void *func_arg, ...
    /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );
        Returns process ID of child on success, or –1 on error

    clone()创建的新锦成几近于父进程的翻版,但是子进程继续运行时不以调用处为起点,转而去调用参数func所指定的函数。

     调用者必须分配一块大小适中的内存空间供子进程的栈使用,同时将这块内存的指针置于参数child_stack中。child_stack应当指向所分配内存块的高端。

    flags低字节中存放着子进程的中职信号,剩余字节存放掩码,用于控制clone()操作。

     剩余参数ptkd、tls、ctid与线程的实现相关。

    #define _GNU_SOURCE
    #include <signal.h>
    #include <sys/wait.h>
    #include <fcntl.h>
    #include <sched.h>
    #include "tlpi_hdr.h"
    
    #ifndef CHILD_SIG
    #define CHILD_SIG SIGUSR1       /* Signal to be generated on termination
                                       of cloned child */
    #endif
    
    static int                      /* Startup function for cloned child */
    childFunc(void *arg)
    {
        if (close(*((int *) arg)) == -1)
            errExit("close");
    
        return 0;                           /* Child terminates now */
    }
    
    int
    main(int argc, char *argv[])
    {
        const int STACK_SIZE = 65536;       /* Stack size for cloned child */
        char *stack;                        /* Start of stack buffer */
        char *stackTop;                     /* End of stack buffer */
        int s, fd, flags;
    
        fd = open("/dev/null", O_RDWR);     /* Child will close this fd */
        if (fd == -1)
            errExit("open");
    
        /* If argc > 1, child shares file descriptor table with parent */
    
        flags = (argc > 1) ? CLONE_FILES : 0;-----------------------------------使用CLONE_FILES之后,父子进程共享同一个文件描述符表。
    
        /* Allocate stack for child */
    
        stack = malloc(STACK_SIZE);
        if (stack == NULL)
            errExit("malloc");
        stackTop = stack + STACK_SIZE;      /* Assume stack grows downward */---分配一个栈供子进程使用,并将高地址作为栈开始。
    
        /* Ignore CHILD_SIG, in case it is a signal whose default is to
           terminate the process; but don't ignore SIGCHLD (which is ignored
           by default), since that would prevent the creation of a zombie. */
    
        if (CHILD_SIG != 0 && CHILD_SIG != SIGCHLD)
            if (signal(CHILD_SIG, SIG_IGN) == SIG_ERR)          errExit("signal");
    
        /* Create child; child commences execution in childFunc() */
    
        if (clone(childFunc, stackTop, flags | CHILD_SIG, (void *) &fd) == -1)--创建子进程。
            errExit("clone");
    
        /* Parent falls through to here. Wait for child; __WCLONE is
           needed for child notifying with signal other than SIGCHLD. */
    
        if (waitpid(-1, NULL, (CHILD_SIG != SIGCHLD) ? __WCLONE : 0) == -1)------等待子进程终止。
            errExit("waitpid");
        printf("child has terminated
    ");
    
        /* Did close() of file descriptor in child affect parent? */
    
        s = write(fd, "x", 1);---------------------------------------------------调用write()检查文件描述符是否仍处于打开状态。
        if (s == -1 && errno == EBADF)
            printf("file descriptor %d has been closed
    ", fd);
        else if (s == -1)
            printf("write() on file descriptor %d failed "
                    "unexpectedly (%s)
    ", fd, strerror(errno));
        else
            printf("write() on file descriptor %d succeeded
    ", fd);
    
        exit(EXIT_SUCCESS);
    }

     Linux Threads线程实现使用clone()来创建线程:CLONE_VM|CLONE_FILES|CLONE_FS|CLONE_SIGHAND。

    3. 进程的创建速度

    x86 32系统,内核2.6.27测试数据:

    第1行进程所占内存越大,fork()所需时间越长。额外时间花在了为子进程复制页表、将数据段、堆栈页表标记为只读的工作上。

    第2行尽管进程的虚拟内存总量在增加,但所使用的时间保持不变。因为vfork()并未复制页表或页,调用进程的虚拟内存总大小并未造成影响。

    第3行clone()使用CLONE_VM|CLONE_VFORK|CLONE_FS|CLONE_SIGHAND|CLONE_FILES,前两个模拟vfork(),剩余则要求父子进程共享文件系统属性、信号处置表、打开文件描述符表。和vfork()之间的差值表示了这些额外工作的开销。

    第4行和第2行的差值,说明执行新程序的开销。

    第5行和第3行的差值,说明执行新程序的开销。

    4. exec()和fork()对进程属性的影响

    fork()、vfork()、clone()使用do_fork()系统调用,和exec()有较大差异。

    下表详细描述了exec()和fork()对进程属性的影响:

     

     

     

  • 相关阅读:
    springMVC web项目转springboot web项目的杂谈
    testNG的DataProvider返回Iterator<Object[]>的妙用
    java+testng利用json格式的txt做数据源的数据驱动示例
    搭建rest-assured接口自动化框架遇到的坑
    Linux SAR命令详解
    springboot集成jsp需添加的包依赖
    springboot集成jsp,页面跳转问题记录
    Spring Boot 使用JSP时,启动热部署配置
    性能测试大牛推荐的必读书单
    ARP详解(转)
  • 原文地址:https://www.cnblogs.com/arnoldlu/p/14085229.html
Copyright © 2011-2022 走看看