进程终止的8种方式,其中前5种为正常终止,他们是:
(1)从main返回
(2)调用exit
(3)调用_exit或_Exit
(4)最后一个线程从其启动例程返回
(5)从最后一个线程调用pthread_exit
异常终止方式有3种,它们是:
(6)调用abort
(7)接到一个信号
(8)最后一个线程对取消请求做出响应
1、退出函数
三个函数用于正常终止一个程序:_exit和_Exit立即进入内核,exit先执行一些清理处理,然后返回内核。
#include<stdlib.h>//由ISO C说明的
void exit(int status);
void _exit(int status);
#include<unistd.h>//由POSIX.1说明的
void _exit(int status);
3个退出函数都有一个整形参数,称为终止状态。
如果:调用这些函数时不带终止状态、main执行了一个无返回值的return语句、main没有声明返回类型为整形。则该进程的终止状态是未定义的。
如果:main返回类型是整形,并且执行到最后一条语句返回,则终止状态为0。(exit(0)等价于return (0))
#include<iostream>
main()
{
cout<<"hello world"<<endl;
}
编译完成echo $? 打印终止状态
2、函数atexit
按照ISO C规定,一个进程可以登记至多32个函数,这些函数由exit自动调用,这些函数称为终止处理函数。将在主程序退出后进行一些处理操作。需要用atexit函数登记,调用顺序与登记顺序相反,登记多次会被调用多次。
#include<iostram>
static void my_exit1();
static void my_exit2();
int main(){
atexit(my_exit2);
atexit(my_exit1);
atexit(my_exit1);
cout<<"main is done"<<endl;
return 0;
}
static void my_exit1(){
cout<<"first"<<endl;
}
static void my_exit2(){
cout<<"second"<<endl;
}
3、命令行参数
当执行一个程序时,调用exec的进程将命令行参数传递给该新程序。
#include<iostream>
using namespace std;
int main(int argc,char *argv[]){
for(int i = 0; i < argc; i++){
cout<<argv[i]<<" ";
}
return 0;
}
编译执行:./a.out 123 abc ABC,输出:123 abc ABC
4、环境表
每个程序都接收一个环境表,与参数表一样,环境表也是一个字符指针数组,其中每个指针包含一个以null结束的C字符串的地址。全局变量environ则包含该指针数组的地址:extern char **environ;每个字符串为name=value的格式。
5、C程序的存储空间布局
由地地址到高地址依次为:正文段-->初始化数据段-->未初始化数据段-->堆-->栈。
6、存储空间分配
3种用于存储空间动态分配的函数。
(1)malloc,分配指定字节数的存储区。不进行初始化。
(2)calloc,为指定数量指定长度的对象分配存储空间。空间中每一位都初始化为0.
(3)realloc,增加或减少以前分配区的长度。
#include<stdlib.h>
void *malloc(size_t size);
void *calloc(size_t nobj, size_t size);
void *realloc(void *ptr, size_t newsize);
void free(void *ptr);
7、环境变量
getenv获得某环境变量
putenv将name=value放到环境表中,如果name存在,则先删除原定义。
setenv将name设置为value,name如果存在,rewrite非0则先删除,rewrite=0,不删除不操作
unsetenv删除name定义,不存在也不会报错。
#include<stdlib.h>
char *getenv(const char *name);
int putenv(char *str);
int setenv(const char *name,const char *value,int rewrite);
int unsetenv(const char *name);
8、函数setjmp和longjmp
goto语句不能跨越函数,在深层次调用跳转用setjmp和lonjmp
#include<iostream>
#include<setjmp.h>
using namespace std;
int fun1(int i);
int fun2(int i);
jmp_buf jmpbuffer;
int main(){
setjmp(jmpbuffer);
for(int i=0;i<10;i++){
fun1(i);
}
return 0;
}
int fun1(int i){
if(i>7)
longjmp(jmpbuffer,1);
fun2(i);
return 0;
}
int fun2(int i){
if(i>5)
longjmp(jmpbuffer,2);
return 0;
}
main将所需信息存入jmpbuffer里,并返回1,调用fun1,又调用fun2,假设fun2处出现错误,将返回到setjmp记录的栈位置,将下面的栈帧抛弃,setjmp返回的是2(longjmp的 第二个参数)。
然而在执行longjmp后,main中的自动变量和寄存器变量状态不好确定,如果不想回滚到调用fun1之前的状态,则自动变量使用volatile,全局变量或者静态变量在执行longjmp时保持不变。