main
函数
C程序总是从main
函数开始执行。main
函数原型是:
int main(int argc, char *argv[]);
argc
是命令行参数个数,argv
是指向参数的各个指针所构成的数组。当内核执行C程序时,在调用
main
函数前先调用一个特殊的启动例程。可执行程序文件将此启动例程指定为程序的起始地址——这是有连接编辑器设置的,而连接编辑器则由C编辑器调用。启动例程从内核取得命令行参数和环境变量值,然后按上述方式调用main
函数做好安排。
进程终止
有8中方式使进程终止,其中有5种为正常终止,它们是:
- 从
main
返回 - 调用
exit
- 调用
_exit
或_Exit
- 最后一个线程从其启动例程返回
- 从最后一个线程调用
pthread_exit
异常终止有3种方式,它们是:
- 调用
abort
函数 - 接到一个信号
- 最后一个线程对取消请求做出响应
1. 退出函数
3个用于正常终止的函数。_exit
和_Exit
函数立即进入内核,exit
函数先执行一些清理处理,然后返回内核。
#include <stdlib.h>
void exit(int status);
void _Exit(int status);
#include <unistd.h>
void _exit(int status);
main
函数返回一个整型值与该值调用exit
是等价的。于是在main
函数中exit(0);
等价于return (0);
测试示例:
#include <stdio.h>
main()
{
printf("hello, world\n");
}
2. 函数atexit
按照ISO C的规定,一个进程可以登记多至32个函数,这些函数将由exit
函数自动调用。这些函数被称之为终止处理程序,并调用atexit
函数来登记这些函数。
#include <stdlib.h>
int atexit(void (*func)(void));
返回值:若成功,返回 0;若出错,返回非 0。
atexit
函数的参数是一个函数地址,当调用此函数时无需向它传递任何参数,也不期望它返回一个值。exit
函数调用这些函数时的顺序与它们被登记时的顺序相反。同一函数如果被登记多次,也会被调用多次。
测试示例:
#include "../../include/apue.h"
static void my_exit1(void);
static void my_exit2(void);
int main(void)
{
if(atexit(my_exit2) != 0)
err_sys("can't register my_exit2");
if(atexit(my_exit1) != 0)
err_sys("can't register my_exit1");
if(atexit(my_exit1) != 0)
err_sys("can't register my_exit1");
printf("main is done\n");
return 0;
}
static void my_exit1(void)
{
printf("first exit handler\n");
}
static void my_exit2(void)
{
printf("second exit handler\n");
}
结果如下:
由上图可知,终止处理函数的执行顺序与它们的注册顺序是相反的,并且my_exit1
函数两次被注册,则其就被执行两次。
命令行参数
当执行一个程序时,调用exec
的进程可将命令行参数传递给该新程序。这是UNIX shell的一部分常规操作。
测试示例:
将所有命令行参数回显到标准输出上,但是,通常的echo
程序无法回显第0个参数。
#include "../../include/apue.h"
int main(int argc, char *argv[])
{
int i;
//for(i = 0; i < argc; i++)
for(i = 0; argv[i] != NULL; i++)
printf("argv[%d]: %s\n", i, argv[i]);
return 0;
}
结果如下:
环境表
每个程序都接收到一张环境表,与参数表一样,环境表也是一个字符指针数组,其中每个指针包含一个以null(’\0’)结束的C字符串的地址。全局变量environ
则包含了该指针数组的地址:
extern char **environ;
我们称environ
为环境指针,指针数组为环境表,其中各指针指向的字符串为环境字符串。在历史上,大多数UNIX系统支持main函数带3个参数,其中第3参数就是环境表地址:
int main(int argc, char *argv[], char *envp[]);
因为ISO C规定main
函数只有两个参数,而且第3个参数与全局变量environ
相比并未带来更多益处,所以POSIX.1也规定应使用environ
而不是第3个参数。通常使用getenv
和putenv
函数来访问环境变量,而不是用environ
变量。但是,如果要查看整个环境,则必须使用environ
指针。