• Linux-进程基础


    知识点比较杂,需要注意逻辑联系

    1、程序与进程的区别

    程序是静态的,存放在磁盘上,是指令的集合。

    进程是程序运行的实例,一个程序运行产生一次产生一个进程。

    关于进程,每个进程都有自己的pid,都有自己的PCB,PCB(进程控制块)记录了进程使用到的资源。

    进程是资源分配的基本的单位,但是不是执行的基本单位。

    在Linux操作系统下,进程之间的关系是父子关系或者兄弟关系,所有的用户级的进程形成了一棵树。可以使用pstree查看。

    pstree命令以树状图显示进程间的关系(display a tree of processes)。ps命令可以显示当前正在运行的那些进程的信息,但是对于它们之间的关系却显示得不够清晰。在Linux系统中,系统调用fork可以创建子进程,通过子shell也可以创建子进程,Linux系统中进程之间的关系天生就是一棵树,树的根就是进程PID为1的init进程。

    常用参数

    格式:pstree

    以树状图显示进程,只显示进程的名字,且相同进程合并显示。

    格式:pstree -p

    以树状图显示进程,还显示进程PID。

    格式:pstree <pid>

    格式:pstree -p <pid>

    以树状图显示进程PID为<pid>的进程以及子孙进程,如果有-p参数则同时显示每个进程的PID。

    格式:pstree -a

    以树状图显示进程,相同名称的进程不合并显示,并且会显示命令行参数,如果有-p参数则同时显示每个进程的PID。

    因为pstree输出的信息可能比较多,所以最好与more/less配合使用。

     init是这棵树的树根,也是1好进程。是用户进程的第一个进程。环用户自定义变量不能被子进程继承,环境变量可以被子进程继承。

     


     

    2、创建新进程

    使用fork(2)创建新进程

    #include<unistd>

    pid_t fork(void);

    功能:

    参数:

    返回值:

      失败:在父进程-1被返回,子进程不被创建,errno被设置

      成功:在父进程里返回子进程的PID,子进程得到0的返回值

      注意:这里不是一个函数返回两个值,而是已经存在了两个进程,是两个fork的返回值

    可以使用ps -aux命令查看进程信息

     使用pstree查看进程树,Linux下的三个特殊进程:idle进程(PID=0),init进程(PID=1),和kthreadd(PID=2)

    idle进程由系统自动创建,运行在内核态
    idle进程其pid=0,其前身是系统创建的第一个进程,也是唯一一个没有通过fork或者kernel_thread产生的进程。完成加载系统后,演变为进程调度、交换。
    kthreadd进程由idle通过kernel_thread创建,并始终运行在内核空间,负责所有内核进程的调度和管理。
    它的任务就是管理和调度其他内核线程kernel_thread, 会循环执行一个kthread的函数,该函数的作用就是运行kthread_create_list全局链表中维护的kthread, 当我们调用kernel_thread创建的内核线程会被加入到此链表中,因此所有的内核线程都是直接或者间接的以kthreadd为父进程 。
    init进程由idle通过kernel_thread创建,在内核空间完成初始化后,加载init程序
    在这里我们就主要讲解下init进程,init进程由0进程创建,完成系统的初始化,是系统中所有其他用户进程的祖先进程
    Linux中的所有进程都是由init进程创建并运行的。首先Linux内核启动,然后在用户空间中启动init进程,再启动其他系统进程。在系统启动完成后,init将变成为守护进程监视系统其他进程。
    所以说init进程是Linux系统操作中不可缺少的程序之一,如果内核找不到init进程就会试着运行/bin/sh,如果运行失败,系统的启动也会失败。


    3、终止进程

    exit(3)和return的区别,return是函数的返回(可以使用echo $?查看最近一次返回结果),exit是进程的结束

    exit(3)

    #include<stdlib.h>

    void exit(int status)

    功能:

      终止一个正常进程,status&0377的值返回给父进程

    参数:

      status:状态码

    返回值:

      不返回

    进程退出之前,需要调用一些处理函数进行资源清理,这些函数需要进行注册

    atexit(3)

    atexit函数是一个特殊的函数,它是在正常程序退出时调用的函数,我们把他叫为登记函数 

    #include<stdlib.h>

    int atexit(void (*func)(void))

    功能

      C 库函数 int atexit(void (*func)(void)) 当程序正常终止时,调用指定的函数 func。您可以在任何地方注册你的终止函数,但它会在程序终止的时候被调用。

    参数

    • func - 在程序终止时被调用的函数。

    返回值

      如果函数成功注册,则该函数返回零,否则返回一个非零值。

    实例

      下面的实例演示了 atexit() 函数的用法。

    #include <stdio.h>
    #include <stdlib.h>
    
    void functionA ()
    {
       printf("这是函数A
    ");
    }
    
    int main ()
    {
       /* 注册终止函数 */
       atexit(functionA );
       
       printf("启动主程序...
    ");
    
       printf("退出主程序...
    ");
    
       return(0);
    }

    在一个程序中最多可以用atexit()注册32个处理函数,这些处理函数的调用顺序与其注册的顺序相反,也即最先注册的最后调用,最后注册的最先调用。

    执行结果:

    执行 
    before exit()!

    on_exit(3)

    相关函数 _exit,atexit,exit

    函数原型

     #include<stdlib.h>
     int on_exit(void (* function)(int,void*),void *arg);
     返回值 如果执行成功则返回0,否则返回-1,失败原因存于errno中。

    函数说明 on_exit()用来设置一个程序正常结束前调用的函数。当程序通过调用exit() 
    或从main中返回时,参数function所指定的函数会先被调用,然后才真正由exit()结束 
    程序。参数arg指针会传给参数function函数,详细情况请见范例。

     #include<stdlib.h>
    void my_exit(int status,void *arg)
    {
    printf(“before exit()!
    ”);
    printf(“exit (%d)
    ”,status);
    printf(“arg = %s
    ”,(char*)arg);
    }
    main()
    {
    char * str=”test”;
    on_exit(my_exit,(void *)str);
    exit(1234);
    }

    执行结果:

    执行 before exit()! 
    exit (1234) 
    arg = test


    4、子进程回收

    进程结束后,父进程会回收子进程资源,wait(2)、waitpid(2)系统调用可以回收子进程资源。

    如果父进程先于子进程结束,那么子进程的父进程将变为init进程。

    wait(2)

    #include<sys/type.h>

    #include<sys/wait.h>

    功能:

      这些系统调用用于获取子进程状态变化和状态信息。状态的变化可以认为是:(1)子进程终止;(2)子进程被信号中止;子进程被信号唤醒。

    参数:

      

    • 参数status如果不是一个空指针,则终止进程的终止状态就存放在statloc所指向的单元。
    • 参数status如果是一个空指针,则表示父进程不关心子进程的终止状态

      子进程的的退出状态存放在这块地址中,可以使用宏检测退出原因。

      
      WIFEXITED(status):如果子进程正常结束则为非0 值.
      WEXITSTATUS(status):取得子进程exit()返回的结束代码, 一般会先用WIFEXITED 来判断是否正常结束才能使用此宏.
      WIFSIGNALED(status):如果子进程是因为信号而结束则此宏值为真
      WTERMSIG(status):取得子进程因信号而中止的信号代码, 一般会先用WIFSIGNALED 来判断后才使用此宏.
      WIFSTOPPED(status):如果子进程处于暂停执行情况则此宏值为真. 一般只有使用WUNTRACED时才会有此情况.
      WSTOPSIG(status):取得引发子进程暂停的信号代码, 一般会先用WIFSTOPPED 来判断后才使用此宏.

      

    kill命令用于删除执行中的程序或工作。

    kill可将指定的信息送至程序。预设的信息为SIGTERM(15),可将指定程序终止。若仍无法终止该程序,可使用SIGKILL(9)信息尝试强制删除程序。程序或工作的编号可利用ps指令或jobs指令查看。

    返回值:  

      是一个阻塞函数,如果没有可以回收的子进程,则为阻塞状态

      如果无子进程,则返回-1

      如果回收成功,则返回子进程的pid

     
     1 #include <stdio.h>
     2 #include <sys/stat.h>
     3 #include <sys/types.h>
     4 #include <unistd.h>
     5 #include <wait.h>
     6 #include <errno.h>
     7 #include <stdlib.h>
     8 /***********************************************************
     9    
    10     
    11 
    12 ***********************************************************/
    13 void waitprocess();
    14 
    15 
    16 int main(int argc, char * argv[])
    17 {
    18   waitprocess();
    19   
    20 }
    21 
    22 void waitprocess()
    23 {
    24 
    25   int count = 0;
    26 
    27   pid_t pid = fork();
    28   int status = -1;
    29   
    30   if(pid<0)
    31   {
    32     printf("fork error for %m
    ",errno );
    33   }else if(pid>0)
    34   {
    35     printf("this is parent ,pid = %d
    ",getpid() );
    36     wait(&status);//父进程执行到此,马上阻塞自己,直到有子进程结束。当发现有子进程结束时,就会回收它的资源。
    37     
    38   }else
    39   {
    40     printf("this is child , pid = %d , ppid = %d
    ",getpid(),getppid() );
    41     int i;
    42     
    43     for (i = 0; i < 10; i++) {
    44       count++;
    45       sleep(1);
    46       
    47       printf("count = %d
    ", count)  ;
    48       
    49     }
    50 
    51     exit(5);
    52     
    53   }
    54   printf("child exit status is %d
    ", WEXITSTATUS(status));//status是按位存储的状态信息,需要调用相应的宏来还原一下
    55   
    56   printf("end of program from pid = %d
    ",getpid() );
    57   
    58   
    59 }

     执行结果

     通常利用如下代码来判断进程的终止原因,和终止信号来源。

    wait(&status);
    ...
    //检测进程是否被信号终止
    if(WIFSIGNALED(status)){
           //输出终止子进程的信号编号
          printf("signum:%d
    ",WTERMSIG(s));  
    }

    在命令行使用kill命令终止相关进程,上述代码即可输出终止进程的相关信息。

    子进程终止,但是父进程并未回收子进程的相关资源,这时候子进程处于僵尸状态,成为僵尸进程

     

    waitpid(2)

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

    功能:

      等待进程状态改变。waitpid可以等待任意子进程或者一组子进程的终止;可以设置成阻塞状态,也可以设置成非阻塞状态。

    参数:

      pid:
        pid>0 只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
        pid=-1 等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
        pid=0时 等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
        pid<-1 等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。

      status:
        参数status如果不是一个空指针,则终止进程的终止状态就存放在status所指向的单元。
        参数status如果是一个空指针,则表示父进程不关心子进程的终止状态

      option:

        WNOHANG 若由pid指定的子进程未发生状态改变(没有结束),则waitpid()不阻塞,立即返回0
        WUNTRACED 返回终止子进程信息和因信号停止的子进程信息
        WCONTINUED 返回收到SIGCONT信号而恢复执行的已停止子进程状态信息

        0:阻塞
    返回值:

      成功 成功结束运行的子进程的进程号

      失败 返回-1
      WNOHANG被设置时 没有子进程退出返回0

    wait(&status)等价于waitpid(-1,&status,0)

    疑问:如果被设置为非阻塞,父进程先于子进程结束,子进程变为孤进程?由init调用wait回收?

     


    笔记参考: https://www.cnblogs.com/king-77024128/articles/2684317.html

    https://www.cnblogs.com/leijiangtao/p/4483009.html

  • 相关阅读:
    vue实现图片预览旋转/放大缩小/上下切换等功能
    VMware安装遇到的问题
    webstrom弹出Server's certificate is not trusted 解决方法
    this.setData is not a function;at pages/index/index onLoad function;at api request success callback function TypeError: this.setData is not a function
    小程序结构目录
    第一个微信小程序
    用C#开发ActiveX控件给VB使用
    处理WIN7,winxp下安装vb6,出现config.nt 无法运行16位DOS程序故障的方法
    VISUALSVN: UNABLE TO CONNECT TO A REPOSITORY AT URL 无法连接主机的解决办法
    程序全屏开机运行,不允许操作电脑桌面,适用工控机触摸屏
  • 原文地址:https://www.cnblogs.com/ptfe/p/10972475.html
Copyright © 2020-2023  润新知