day05

进程管理
一、进程的基本概念
    1、进程与程序
        程序是存储在磁盘上的可执行文件,程序被加载到内存中开始运行称为进程,一个程序可以同时加载成多个进程,进程就是处于活动状态下的程序
    2、进程的分类
        进程根据功能不同一般分为三种类型:交互进程、批处理进程、守护进程
            交互进程:由一个shell终端启动的进程,在运行过程中需要与用户进行交互操作,可以运行于前台,也可以在后台运行
            批处理进程:该进程是一个进程的集合,负责按顺序执行预定于好的操作
            守护进程:一般处于活跃状态,运行在后台,一般都是由系统在开机时通过启动系统脚本自动创建
    3、查看进程
        简单形式:ps        当前用户有控制终端的进程简单信息
        列表心思:ps -auxw  以最大列宽显示所有进程的详细信息
            USER            进程的属主
            PID             进程的编号
            %CPU            CUP的使用率
            %MEM            内存的使用率(内存泄漏)
            VSZ             虚拟内存使用的字节数
            RSS             物理内存使用的字节数
            TTY             终端设备号  ‘?’表示无终端控制
            STAT        ****进程的状态
                            /*                             要背                                     */            
                                O   就绪态              等待被调度  
                                R   运行态              Linux没有O,就绪态用R表示
                                S   可被唤醒的休眠态     例如系统中断、获得资源、收到信号都可以唤醒进入R    
                                D   不可被唤醒睡眠态     只能被系统唤醒
                                T   暂停态              收到信号SIGSTOP转入T,收到SIGCONT转入R
                                Z   僵尸态(僵死态)
                                X   死亡态
                            /*                                                                     */
                                <   高优先级
                                N   低优先级
                                s   进程组中的领导者
                                l   多线程的进程  
                                +   处于后台的进程组
            START TIME  进程运行总时间  
            COMMAND     进程的启动命令
    4、父进程、子进程、孤儿进程、僵尸进程
        一个进程可以被另一个进程创建,创建者称为被创建者的父进程,被创建者称为创建者的子进程,当子进程被父进程创建启动后在操作系统的调度下同时运行
        当子进程先于父进程结束时,子进程会向父进程发送SIGCHLD信号,此时父进程应该区回收子进程的相关资源,如果父进程没有回收子进程,那么子进程就变成了僵尸进程
        父进程先于子进程结束,子进程就变成孤儿进程,所有的孤儿进程都会被系统指定的一个进程领养接管,该进程就变成了孤儿进程的父进程
    5、进程标识符(进程ID号)
        每个进程都有一个以非负整数表示的唯一标识,即是进程ID(进程PID)
        进程ID在任意时刻中都是唯一的,但是可以重用,进程一旦结束它的进程ID会被系统回收,过一段时间后可以继续分配给新的进程(延时重用)
        pid_t getpid(void);
        功能:获取调用者的进程ID
        pid_t getppid(void);
        功能:获取调用者父进程的进程ID

二、进程的创建
        pid_t fork(void);
        功能:创建一个子进程
        返回值:成功时一次调用两次返回,子进程一定返回‘0’,父进程返回的是子进程的PID
            当进程的数量超过了系统的限制可能会创建失败返回一次-1
        注意:
            1、子进程创建成功后,会与父进程同样从fork处开始继续往下执行,为了让父子进程分别执行不同的功能,可通过返回值的不同让他们进入不同的分支语句
            2、****
                通过fork创建的子进程会拷贝父进程的数据段、bss段、堆、栈、I/O流的缓冲区等数据,与父进程共享代码段的数据,子进程会继承父进程的信号处理方式
               ****
            3、fork结束后,不确定是父进程先返回还是子进程先返回,可以通过休眠函数让另一个进程先执行usleep
            4、通过fork创建的子进程可以共享父进程的文件描述符
        练习:通过fork创建4个子进程,再为这四个子进程各自创建两个子进程

        pid_t vfork(void);
        功能:以加载可执行程序的方式创建并运行子进程
        返回值:成功时一次调用两次返回,子进程一定返回‘0’,父进程返回的是子进程的PID
        注意:
            1、子进程一定先返回,此时子进程不算完全创建成功,需要加载一个可执行程序来替换当前子进程的所有资源,替换完成后子进程才算完全创建成功,此时父进程才会返回

        使用exec系列函数让子进程加载可执行程序
        int execl(const  char  *path,  const  char *arg, ...
                       /* (char  *) NULL */);
            path:要加载的可执行文件的路径
            arg:命令行参数,...表示多个     最后一个一定要以NULL结尾
        int execlp(const  char  *file, const char *arg, ...
                       /* (char  *) NULL */);
            file:要加载的可执行文件的名字,会根据PATH环境变量中记录的路径查找并加载该文件

        注意:如果想要指定一个路径,需要在配置文件中增加PATH的值

        int execle(const char  *path,  const  char *arg, ...
                        /*, (char *) NULL, char *const envp[] */);
            path:要加载的可执行文件的路径
            arg:命令行参数,...表示多个     最后一个一定要以NULL结尾
            envp:环境变量表
        int execv(const char  *path,  char  *const argv[]);

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

        注意:
            1、exec系列函数成功不返回,失败才返回-1
            2、以vfrok、exec创建出来的子进程不会继承父进程的信号处理方式,但是能继承父进程的信号屏蔽集

        考点:对比fork和vfork的区别?
            fork:
                1、是将父进程的整个地址包括地址中的内容拷贝给子进程
                2、共享数据段,bss,栈,堆 ,I/O流缓冲区,继承父进程的信号处理方式
            vfork:
                1、和父进程共享父进程的地址,不是拷贝
                2、不共享数据段、bss等内容,编号继承父进程的信号处理方式,但能继承父进程的信号屏蔽集
        int system(const char *command);
        功能:创建子进程加载command程序来执行系统命令

三、进程的正常退出
    1、执行了main函数的return n语句,该返回值可以被父进程接收到
    2、进程调用了exit函数,也相当于正常退出,该函数属于C标准库提供的函数
        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);
            功能:向内核注册绑定一个进程结束前要执行的函数
                arg:会在调用function的时候传递给该函数
        进程正常退出前要完成的步骤:
            1、要事先调用atexit、on_exit函数进行注册,如果注册了多个函数,结束时这些函数的执行顺序与注册顺序相反(栈)
            2、冲刷并关闭所有打开状态下的标准IO流
            3、调用_exit、_Exit函数          
    3、调用_exit、_Exit函数
        void _exit(int status);
        功能:结束进程,由系统提供
        void _Exit(int status);
        功能:结束进程,由标准库提供
            1、它们的参数status会被父进程获取到
            2、结束前会关闭所有打开状态下的系统IO的文件描述符
            3、向父进程发出结束信号SIGCHLD
            4、该函数不会返回
    4、进程的最后一个线程执行了return语句
    5、进程的最后一个线程执行了pthread_exit函数

四、进程的异常终止
    1、进程调用了abort函数,产生了SIGABRT信号
    2、进程接收到了其他进程的某些信号,导致终止
    3、进程自己的错误操作导致终止,例如:段错误、除零等
    4、进程的最后一个线程接收到了“取消”请求操作,并相应了请求

    注意:以上的异常终止方式都会让父进程无法获取结束进程的结束状态码,所以叫异常终止
    注意:无论进程是如何结束的,他们的资源都会被回收,所有的文件描述符、文件指针都会被关闭

五、子进程的回收
    对于任何结束方式,都希望父进程能够获取到,通过wait、waitpid函数可以直到子进程是如何结束的以及结束状态码
   
    pid_t wait(int *status);
    功能:等待任意子进程的结束,并获取其结束状态码
        status:输出型参数
    返回值:子进程的ID
        1、如果所有子进程都还在正常运行,则阻塞等待
        2、如果有一个子进程结束,立即返回该进程的结束状态码和pid
        3、如果没有子进程运行,立即返回-1
    WIFEXITED(status)
        判断子进程是否是正常退出,如果是返回真
    WEXITSTATUS(status)
        如果子进程正常退出,可以获取正确的结束状态码,只获取低八位
    WIFSIGNALED(status)
        判断子进程是否异常退出,如果是返回真
    WTERMSIG(status)
        如果子进程是异常退出的,可以获取杀死该子进程的信号id
   
    pid_t waitpid(pid_t pid, int *status,  int options);
    功能:指定回收某个或者某些子进程的状态
        pid:
            <-1    等待组id为abs(pid)的进程组中的任意子进程结束
            -1     等待所有子进程,与wait一样
            0      等待调用者同组的任意子进程结束
            >0     等待该进程结束
        status:处理与wait等同
        options:等待模式
            0       正常的阻塞模式=wait
            WNOHANG 非阻塞模式  如果没有子进程结束,立即返回0
             WUNTRACED  如果等待的子进程中有处于暂停态的,立即返回该子进程的状态码
             WCONTINUED 如果等待的子进程中有从暂停态转入运行态,立即返回该子进程的状态码
                    用于判断和获取status的宏函数:
             WIFSTOPPED 判断状态码,如果子进程是转入暂停态,返回真
             WSTOPSIG   当子进程是暂停态返回时,获取导致暂停态的信号id
             WIFCONTNUED  判断状态码,如果子进程是暂停态转入运行态,返回真
    注意:
        1、如果没有子进程结束 wait会阻塞等待,如果在父进程的业务逻辑代码出wait,会对父进程的业务产生阻塞影响,因为子进程结束时会给父进程发送SIGCHLD信号,可以通过在父进程中注册该信号处理函数,在函数内执行wait,就不影响父进程的业务执行,但是信号最好选择可靠信号
        2、waitpid可以选择不阻塞等待,因此不需要像wait一样在信号处理函数中执行,可以直接在父进程的业务逻辑中调用不受影响
posted @   歪爱慕外  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示