【Linux】进程终止

来源:互联网 发布:js脚本注入方法 编辑:程序博客网 时间:2024/06/11 02:47
进程终止的方式
1、正常终止
1)从main函数返回;
2)调用exit函数;
3)调用_exit或_Exit;
4)最后一个线程从启动例程返回;
5)最后一个线程调用pthread_exit;
2、异常终止
      1)调用abort函数;
      2)接到一个信号并终止;
      3)最后一个线程对取消请求做出响应;
main函数
        我们通常认为C语言的起始函数是main函数,实质上一个程序的启动函数并不一定是main函数,这个可以采用链接器来设置,但是gcc中默认main就是C语言的入口函数,在main函数启动之前,内核会调用一个特殊的启动例程,这个启动例程从内核中取得命令行参数值和环境变量值,为调用main函数做好准备,因此对应程序而言main函数并不是起始,但是对应C 语言而言,main函数就是入口地址,其他的链接器帮助我们完成,实际上mian函数的执行是使用了exec函数,这是一个函数族,这也是内核执行一个程序的唯一方法,这在进程控制部分将进行分析。  
       简单说就算是,当内核使用一个exec函数执行C程序时,在调用main函数之前先调用一个特殊的启动例程,可执行程序将此例程指定为程序的起始地址。启动例程从内核获取命令行参数和环境变量,然后为调用main函数做好准备。
exit、_exit和_Exit函数
        #include <stdlib.h>
        void exit( int status );
        void _Exit( int status );
        #include <unisth.h>
        void _exit( int status );
        这三个函数用于正常终止一个程序,_exit和_Exit立即进入内核,而exit则要先做一些清理工作(调用执行各终止处理程序,按需对所有打开的流调用fclose(),关闭所有标准I/O流,这样会造成所有缓冲的输出数据都被冲洗即写入文本中),再进入内核。三个函数所带的整型参数(status)称为终止状态或退出状态
        如果满足以下几点,则进程的终止状态是未定义的。
        1)调用这些函数不带参数
        2)main函数中的return语句无返回值
        3)main函数没有声明返回类型为整型
        mian函数返回一个整型值与用该值调用exit是等价的。eg:exit(3)和main函数返回3等价
对比exit和return、about、_exit进行进程的终止
    1、exit与return
         exit:一个函数,有参数。exit执行完后把控制权交给系统
         return:函数执行完后的返回。return执行完后把控制权交给调用函数
    2、exit与about
         exit:正常终止进程
         about:异常终止进程
    3、exit与_exit
        相同:都是用来终止进程的。当程序执行到exit或者_exit时,系统无条件的停止剩下的所有操作,清楚包括PCB在内的各种数据结构,并终止本进程的运行。exit中的参数exit_code为0代表进程正常终止,若为其他值表示程序执行过程中有错误发生。
        异同:
        1)头文件不同
                exit()函数的头文件——->stdlib.h
               _exit()函数的头文件——>unistd.h
        2)执行后的动作不同
                 a、 _exit()在执行后立即返回给内核,而exit()要先执行一些清除工作,然后把控制权交给内核。
                 b、调用_exit()函数时,会关闭进程所有的文件描述符,清理内存及其他一些内核清理函数,但不会刷新流                                                                    (stdin,stdout,stderr等)
                 c、exit()函数是在_exit()函数之上的一个封装,它会调用_exit()函数,并在调用之前刷新流。   
                 
atexit函数
          #include<stdlib.h>
          int atexit(void(* fun)(void));
         1、 atexit函数功能:注册终止函数,即在main执行结束后调用的函数。
         2、 其中,atexit的参数是一个函数地址,当调用此函数时无须传递任何参数,该函数也不能返回值,atexit函数称为终止处理程序注册程序,注册完成以后,当函数终止是exit()函数会主动的调用前面注册的各个函数,但是exit函数调用这些函数的顺序与这些函数登记的顺序相反,我认为这实质上是参数压栈造成的,参数由于压栈顺序而先入后出(即和函数调用的栈帧过程相似)。
        3、一个进程可以注册若干个函数,这些函数由exit自动调用,这些函数被称为终止处理函数,atexit函数可以注册这些函数。
        4、如果一个函数被多次登记,那么该函数也将多次的执行。
            我们知道exit是在main函数调用结束以后调用,因此这些函数的执行肯定在main函数之后。
写一个程序,在Linux环境下验证一下atexit函数调用注册函数的顺序
      
如果main函数中输出部分不加\n,则main函数要输出的内容会先放到标准输出缓冲区中,当main中调用exit函数的时候,会做一些自身清理工作,同时刷新标准输出缓冲区中的内容
环境表
       每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以'\0'结尾的环境字符串,环境指针environ是一个全局变量,指向指针数组的地址,通常getenv和putenv函数来访问特定的环境变量,而不是environ全局变量。如果要查看整个环境,则必须用environ全局变量。
C程序的空间布局
           正文段:CPU执行的机器指令部分,是共享和只读。
           初始化数据段:又称作数据段,包含了程序中明确需要赋初值的变量。
           非初始化数据段:在程序开始执行前,内核将此段中的数据初始化为0或空指针。
           栈:自动变量以及每次函数调用时所需保存的数据都存放在此段中。
           堆:用于动态存储分配。堆位于栈和非初始化数据段之间。
存储器分配
       #incldue<stdlib.h>
       void *malloc(size_t size);
       void *calloc(size_t nobj, size_t size);
       void *realloc(void *ptr, size_t size);
       void free(void *ptr);
       1、malloc函数分配指定字节数的存储区,该存储区中的初始值不确定;、
       2、calloc函数为指定数量且指定长度的对象分配存储空间,该空间中的每一位都初始化为0;
       3、realloc函数更改存储区的长度(增加或减少),新增区域内的初始值不确定,如果ptr为空,realloc和malloc的功能相同。
以上函数的大多数实现所分配的存储空间都比所要求的大一些,额外的空间用来存储管理信息。如果在一个超过已分配区的尾端进行写操作,就会重写下一个分配区的管理记录;同样,在一个已分配区的起始位置之前写入,会重写本分配区的管理记录。这种错误是灾难性的,但因为不会很快暴露出来,所以很难发现。
0 0