• Linux — 进程管理


    进程创建

    进程通过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 !=0 ){
            return child_pid;
        }else{
        execvp(program, arg_list);
        abort();
        }
    }
    

    概览图:

    编译知识

    静态库

    这里额外补充一些编译相关的内容。一个源码文件要变成可执行的程序,需要经过编译、链接。

    这里编译出的.o文件,就是ELF的第一种类型,可重定位文件,格式如下:

    • .text:存放编译好的二进制可执行代码

    • .data:已经初始化好的全局变量

    • .rodata:只读数据,例如字符创常量、const常量

    • .bss:未初始化全局变量,运行时会置0

    • .symtab:符号表,记录的是函数和变量

    • .strtab:字符串表、字符串常量和变量名

    在程序中我们提到过函数栈,局部变量是放在里的,在运行期随时分配、随时释放。这里讨论的是文件,还没到执行阶段。

    如要将函数作为库文件被重用,不能以.o的形式存在,而是要形成库文件,最简单的类型是静态链接库.a文件。

    ar cr libstaticprocess.a process.o
    # 形成二进制执行文件staticcreateprocess
    gcc -o staticcreateprocess createprocess.o -L. -lstaticprocess
    

    上述第二条命令链接时,createprocess.o调用了create_process函数,但不知道位置,通过将process.o合并进来,就可以确定位置了。这里形成了ELF的第二种格式:

    .o相似,文件被分为多个section,通过节头表来描述。这里除了段描述之外,最重要的是p_vaddr——加载到内存的虚拟地址。

    静态库特点:

    • 一旦链接,代码和变量的section都合并在一起。当程序运行后,就不依赖这个库是否存在

    • 但如果相同的代码段被多个程序使用时,在内存里就存在多份

    • 当静态库更新了,需重新编译二进制执行文件以应用更新

    动态库

    当动态链接库被链接到一个程序文件时,最后的程序文件并不包括动态链接库中的代码,而仅仅包括对动态链接库的引用,并且不保存动态链接库的全路径,仅保存其名称

    gcc -shared -fPIC -o libdynamicprocess.so process.o
    
    gcc -o dynamiccreateprocess createprocess.o -L. -ldynamicprocess
    

    执行此程序时,先寻找动态链接库,再进行加载。默认,系统在/lib/usr/lib 路径下寻找,找不到则报错。可以通过设定LD_LIBRARY_PATH环境变量,指定动态链接库的路劲。

    动态链接库,就是ELF的第三种类型,共享对象文件

    • 多个.interp的Segment,里面是ld-linux.so动态连接器,执行链接动作

    • 多个.plt.got.plt,分别是 过程链接表(PLT)全局偏移量表(GOT)

    接下来,我们来看下程序运行时,是如何将so文件动态链接到进程空间的?

    • dynamiccreateprocess程序调用libdynamicprocess.so里的create_process函数时,是运行时才去找的,编译压根不知道在哪,因此在PLT里建立一个PLT[x]——在二进制程序里,不直接调用函数,而是调用PLT[x]里的代理代码,它会在运行时找到真正的create_process函数

    • 去哪找?去GOT里——它会为create_process函数创建GOT[y],对应运行时 create_process函数在内存中真正的地址

    • GOT又是咋知道的?对于create_process函数,GOT在开始时会创建GOT[y],但开始这里没有真正的地址,因为不知道...,它又回调PLT,告诉他:你里面的代理代码来找我要create_process的真实地址,我不知道,你自己想想。

    • PLT只能去调用PLT[0],PLT[0]去调用GOT[2],这里面保存ld-linux.so入口函数,它去找到加载到内存中的libdynamicprocess.so里的create_process函数地址,然后把这个地址放到GOT[y]中,以供下次PLT[x]调用。

    运行程序为进程

    ELF二进制文件是如何加载到内存里的?

    static struct linux_binfmt elf_format = {
            .module         = THIS_MODULE,
            .load_binary    = load_elf_binary,
            .load_shlib     = load_elf_library,
            .core_dump      = elf_core_dump,
            .min_coredump   = ELF_EXEC_PAGESIZE,
    };
    

    具体调用链:do_execve->do_execveat_common->exec_binprm->search_binary_handler。

    在系统调用时,exec最终调用的是load_elf_binary.

    进程树

    所有进程的祖宗进程,就是系统启动时的init进程。init进程会启动很多daemon进程,为系统运行提供服务。然后启动getty,让用户登录,登录后运行shell。

    • 1号进程是/sbin/init,所有用户态进程的祖先

    • 2号进程是内核线程kthreadd,所有内核态的祖先

    • ps -ef下,用户态不带中括号,内核态带中括号

  • 相关阅读:
    Linux 用户 quote 配置 说明
    Linux 用户和用户组 配置说明
    ORA-00600: [kck_rls_check must use (11,0,0,0,0) or lower] 故障解决
    Oracle Data Guard PING[ARC2]: Heartbeat failed to connect to standby ''. Error is 12514 故障分析
    Oracle 11g RAC OCR 与 db_unique_name 配置关系 说明
    Data Guard 奇葩的 ORA-16191: Primary log shipping client not logged on standby 问题
    Oracle 11gR2 RMAN Duplicate 触发PLS-00201: identifier 'DBMS_RCVCAT.GETDBID' must be declared 错误
    RMAN RAC 到 单实例 duplicate 自动分配通道 触发 ORA-19505 错误
    Secure CRT 自动记录日志 配置 小记
    VBox fdisk 不显示 添加的硬盘 解决方法
  • 原文地址:https://www.cnblogs.com/CocoML/p/14135367.html
Copyright © 2020-2023  润新知