【Linux基础】什么是进程?进程相关知识详解

进程基本概念:

    1、进程与程序

        程序就是存储在磁盘上的可执行文件,程序被加载到内存中开始运行叫进程。一个程序可以被多次加载生成多个进程,进程就是处于活动状态的计算机程序。

    2、进程的分类

        进程一般分为三种类型:交互进程、批处理进程、守护进程。

        守护进程一般都处于活跃状态,运行在后台,由于系统在开机时通过启动脚本自动创建的。

    3、查看进程

        简单形式:ps 显示当前用户有控制终端的进程信息。

        列表形式:ps auxw 显示进程详细信息

            a 所有用户的有控制终端的进程

            x 无终端控制的进程

            u 显示进程的详细信息

            w 以更大列宽显示

            USER 进程的属主

            PID 进程的编号

            %CPU CPU使用率

            %MEM 内核使用率

            VSZ 虚拟内存使用的字节数

            RSS 物理内存使用的字节数

            TTY 终端设备号,?表示无终端控制

            STAT 进程的状态

                O 就绪,等待被调度

                R 运行,Linux系统没有O,就绪也用R表示

                S 可被唤醒的睡眠,如系统中断、获得资源、收到信号都可以唤醒它转入运行状态。

                D 不可被唤醒的睡眠,只能被系统唤醒。

                T 暂停状态,收到SIGSTOP信号进程暂停状态,收到SIGCONT信号转入运行状态。

                W 等待内存分页(2.6内核以后被废弃)

                X 死亡状态

                Z 僵尸状态

                < 高优级

                N 低优先级

                l 多线程的进程

                s 会话首进程

                L 有内存被锁进内存分页

                + 在前台进程级中

            START 进程启动的时间

            TIME 进程运行的时间

            COMMAND 启动进程的命令

    4、父进程、子进程、孤儿进程、僵尸进程

        一个进程可以被另一个进程创建,创建者叫父进程,被创建者叫子进程,子进程被父进程启动后在操作系统的调用同时运行。

        当子进程先于父进程结束,子进程会向父进程发送SIGCHLD信号,此时父进程应该去回收子进程的相关资源,如果没有收回子进程就会进入僵尸状态。

        僵尸进程:该进程已经死亡,但它的父进程没有立即回收它的相关资源,该进程就进入僵尸状态。

        孤儿进程:父进程先于进程结束,子进程就变成了孤儿进程,孤儿进程会被孤儿院(init守护进程)领养,init就是孤儿进程的父进程。

    5、进程标识符

        每个进程都有一个以非负整数表示的唯一标识,即进程ID/PID。

        进程ID在任意时刻都是唯一的,但可以重用,进程一旦结束它的进程ID就会被回收,过一段时间后再重新分配给其它新创建的进程(延时重用)。

        pid_t getpid(void);

        功能:获取进程ID

        pid_t getppid(void);

        功能:获取父进程ID

        init的进程ID永远是1。

创建进程:

    int system(const char *command);

    功能:执行一个可以执行文件,这样就创建一个子进程。

    返回值:子进程结束后才返回。

    该函数的实现调用了fork和waitpid函数,其实是当进程创建一个子进程,子进程又加载了command可执行文件。

    pid_t fork(void);

    返回值:一次调用两次返回,子进程返回0,父进程返回子进程ID,当进程的数量超过系统的限制会创建进程失败,返回-1。

    可以根据返回值的不同让父子进程进入不同的分支,执行不同的代码。

    

    该函数调用后父子进程各自独立运行,谁先返回不确定,但可以通过睡眠确定让哪个进程先执行。

    通过fork创建的子进程会拷贝父进程的(数据段、bss段、堆、栈、I/O流缓冲区)等数据同,与父进程共享代码段,子进程会继承父进程的信号处理方式。

    通过fork创建的子进程可以共享父进程的文件描述符。

    pid_t vfork(void);

    功能:以加载可以执行文件方式创建子进程。

    返回值:子进程返回0,父进程返回子进程的PID。

    子进程先返回,此时子进程并没有创建成功,需要加载一个可以执行文件来替换当前子进程的所有资源,替换完成子进程才算创建成功,此时父进程才返回。

    使用exec系列函数加载可执行文件:

    int execl(const char *path, const char *arg, ...);

    path:可执行文件的路径,

    arg:命令行参数,一般每个是可执行文件的名字,至少一个,NULL结尾。

    int execlp(const char *file, const char *arg, ...);

    file:可执行文件名字,会根据PATH环境变量的查找可执行文件;

    arg:命令行参数

    int execle(const char *path, const char *arg, ..., char * const envp[]);

    path:可执行文件的路径

    arg:命令行参数

    envp:环境变量表,父进程可以在加载子进程时把环境变量传递给子进程,这样父子进程共用一个环境变量表。

    int execv(const char *path, char *const argv[]);

    path:可执行文件的路径

    argv:指针数组,由传递给子进程的字符串组成,以NULL结尾

    int execvp(const char *file, char *const argv[]);

    file:可执行文件名字,会根据PATH环境变量的查找可执行文件。

    argv:指针数组

    int execvpe(const char *file, char *const argv[],char *const envp[]);

    file:可执行文件名字

    argv:指针数组

    envp:环境变量表

    

    exec系列函数正常是不会返回会,当加载子可执行文件失败时返回-1。

    以excl系列函数创建出的子进程不会继承父进程的信号处理函数,但能继承父进程的信号屏蔽。

进程的正常退出:

    1、在main函数中执行 return n,该返回值可以被父进程接收到,它与exit几乎等价。

    2、进程调用了exit函数,该函数是标准库函数:

        void exit(int status);

        功能:在任何时候调用此函数都可以结束进程。

        status:结束状态码(EXIT_SUCCESS/EXIT_FAILURE),与main函数中return的返回值效果一样,

        返回值:该函数不会返回

        进程退出前要完成:

            int atexit(void (*function)(void));

            功能:注册一个进程结束时执行的函数

            int on_exit(void (*function)(int , void *), void *arg);

            功能:注册一个进程结束时执行的函数

                int: return 的值或exit函数的参数

                arg:会在进行结束时自动传递给function函数。

            1、先调用事先通过at_exit/on_exit注册过的函数,如果都注册了执行顺序与注册顺序相反。

            2、冲刷并关闭所有打开状态的标准IO流。

            3、该函数的实现调用了_Exit/_exit。

    3、调用_exit/_Exit函数

        void _exit(int status);

        功能:结束进程,由系统提供

        void _Exit(int status);

        功能:结束进程,由标准库提供

        1、它们的参数会被父进程获取到。

        2、进程结束前会关闭所有处理打开状态的文件描述符。

        3、会向父进程发送SIGCHLD.

        4、该函数不会返回。

    4、进程的最后一个线程执行的返回语句。

    5、进程的最后一个结束调用pthread_exit函数。

进程的异常终止:

    1、进程调用了abort函数,产生了SIGABRT信号(自杀)。

    2、进程接收到了某些信号,可以是其它进程发送的,也可能自己的错误操作造成的(他杀,作死)。

    3、进程的最一个线程收到了"取消"操作,并作出了响应。

    这三种结束方式父进程无法猎取结束状态码,所以叫异常终止。

    注意:不管进程是如何结束的它们都会执行同一段代码,关闭所有打开的文件描述符,释放所有的内存。

子进程回收:

    对于任何结束方式,都希望父进程能够知道,通过wait、waitpid函数可以知道子进程是如何结束的以及结束状态码。

    pid_t wait(int *status);

    功能:等待子进程结束,并获取结束状态。

    返回值:子进程的ID

    1、如果所有子进程都在运行,则阻塞。

    2、如果有一个子进程结束,立即返回该进程结束状态和PID。

    3、如果没有子进程则返回-1。

    WIFEXITED(status) 判断进程是否是正常结束,如果是则返回真。

    WEXITSTATUS(status) 如果进程是正常结束的,可以获取到正确的结束状态码,只获取低8位。

    WIFSIGNALED(status) 判断进程是否是异常结束,如果是则返回值。

    WTERMSIG(status) 如果进程是异常结束的,可以获取到杀死进程的信号。

    注意:由于wait函数可能会阻塞,因此不适合在业务逻辑中调用些函数,可以为SIGCHLD信号注册一个处理函数,在处理函数中调用wait函数。

    pid_t waitpid(pid_t pid, int *status, int options);

    功能:指定收回某个或某些进程

    pid:

        <-1 等待abs(pid) 进程组中的进程结束。

        -1 等待任意子进程结束,功能与wiat等价。

        0  等待同组的任意进程结束。

        >0 等待该进程结束。

    status:结束状态,与wiat中的等价。

    options:

        WNOHANG 非阻塞模式,如果没有进程结束,立即返回0。

        WUNTRACED 如果有进程处理暂停状态,返回该进程的状态。

        WCONTINUED 如果有进程由暂停转为继续运行,返回该进程的状态。

        

    WIFSTOPPED(status) 判断进程是否处理暂停状态,是则返回真。

    WSTOPSIG(status) 获取到导致该进程暂停的信号。

    WIFCONTINUED(status) 判断该进程是否由暂停转为继续运行,是则返回真。

猜你喜欢

转载自blog.csdn.net/weixin_48994377/article/details/108547431