C程序总是从main函数开始执行。main函数的原型是:
int main(int argc, char *argv[]);
其中,argc是命令行参数的数目,argv是指向参数的各个指针所构成的数组。
当内核执行C程序时,在调用main之前先调用一个特殊的启动例程。可执行程序文件将此启动例程指定为程序的起始地址——这是由链接编辑器设置的,而连接编辑器则由C编译器调用。启动例程从内核取得命令行参数和环境变量值,然后为按上述方式调用mian函数做好安排。
有8中方式使进程终止,其中5种为正常终止,它们是:
异常终止有3种方式,它们是:
3个函数用于终止一个程序
#include <stdio.h>
void exit(int status);
void _EXIT(int status);
#include <unistd.h>
void _exit(int status);
3个退出函数都带一个整型参数,称为终止状态。大多数UNIX系统shell都提供检查进程终止状态的方法。
下面3种情况下进程的终止状态是未定义的
若main的返回类型是整型,并且main执行到最后一条语句时返回,那么该进程的终止状态是0;
C语言的启动和终止
内核调用exec运行C启动例程,C启动例程调用main()函数,其他的所有函数都是有main函数直接或间接调用的。
内核与用户进程的交互,直接使用的只有三个函数:exec、_exit、_Exit
按照ISO C规定,一个进程可以登记多至32个函数,这些函数将有exit自动调用。这些函数为终止处理程序,并调用atexit函数来登记
#include <stdlib.h>
int atexit(void (*func)(void));
//返回值:若成功,返回0;若出错,返回非0
其中,atexit的参数是一个函数地址,当调用此函数时无需向它传递任何参数,也不期望它会返回一个值。exit调用这些函数的顺序与它们登记的顺序是相反的。同一函数若被登记多次,也会被调用多次。
使用:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
static void my_exit1();
static void my_exit2();
int main()
{
if (atexit(my_exit2) != 0)
{
printf("can't register my_exit2\n");
}
if (atexit(my_exit1) != 0)
{
printf("can't register my_exit1\n");
}
if (atexit(my_exit1) != 0)
{
printf("can't register my_exit1\n");
}
printf("main is done\n");
return 0;
}
static void my_exit1()
{
printf("first exit handler\n");
}
static void my_exit2()
{
printf("second exit handler\n");
}
main is done
first exit handler
first exit handler
second exit handler
当执行一个程序时,调用exec的进程可以将命令行参数传递给该新程序。
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
for(int i = 0; i < argc; ++i)
{
printf("argv[%d]: %s\n", i,argv[i]);
}
exit(0);
}
yanke@vm:~/Code/apue/ch7UnixForkEnv$ ./a.out test foo bar yanke
argv[0]: ./a.out
argv[1]: test
argv[2]: foo
argv[3]: bar
argv[4]: yanke
每个程序都接受到一张环境表。与参数表一样,环境表也是一个字符指针数组,其中每个指针包含一个以null结束的C字符串的地址。全局变量environ则包含了该指针数组的地址:
extern char **environ;
我们称environ为环境指针,指针数组为环境表,其中各指针指向的字符串为环境字符串。
可参考:http://blog.csdn.net/charles1e/article/details/51492638
参考:http://www.cnblogs.com/zhoutian6214/archive/2008/11/11/1331646.html
静态链接库:对函数库的链接是放在编译时期(compile time)完成的。所有相关的对象文件(object file)与函数库(library)被链接合成一个可执行文件(executable file)。程序在运行时,与函数库无关,通常文件名为“libxxx.a”的形式。
动态链接库(dynamic link library):把对一些库函数的链接载入推迟到程序运行的时期(runtime)
把库函数推迟到程序运行时期载入的好处:
ISO C 3个存储空间动态分配的函数如下:
#include <stdlib.h>
void *malloc(zise_t size);
void *calloc(size_t nobj, size_t size);
void *realloc(void *ptr, size_t newsize);
//3个函数返回值:若成功,返回非空指针;若出错,返回NULL
void free(void *ptr);
free为释放空间,一般释放后会送入可用内存池,以后申请空间时再分配。
ISO C定义了一个函数getenv,可以用其取环境变量值
#include <stdlib.h>
char *getenv(const char *name);
//返回值:指向与name关联的value的指针;若未找到,返回NULL
注意,此函数返回一个指针,它指向name = value 字符串中的value。我们应当使用getenv从环境中取一个指定环境变量的值,而不是直接访问environ。
设置环境变量的的函数
#include <stdlib.h>
int putenv(char *str);
//函数返回值:若成功,返回0;若出错,返回非0
int setenv(const char *name, const char *value, int rewrite);
int unsetenv(const char *name);
//两个函数返回值:若成功,返回0;若出错,返回-1
参考:http://blog.csdn.net/chenyiming_1990/article/details/8683413
setjmp和longjmp函数是非局部跳转语句。非局部指的是,这不是由普通C语言goto,语句在一个函数内实施的跳转,而是在栈上跳过若干调用帧,返回到当前函数调用路径上的某一个函数中。
#include <setjmp.h>
Int setjmp(jmp_buf env);
//返回值:若直接调用则返回0,若从longjmp调用返回则返回非0值的longjmp中的val值
Void longjmp(jmp_buf env,int val);
//调用此函数则返回到语句setjmp所在的地方,其中env 就是setjmp中的 env,而val 则是使setjmp的返回值变为val。
当检查到一个错误时,则以两个参数调用longjmp函数,第一个就是在调用setjmp时所用的env,第二个参数是具有非0值的val,它将成为从setjmp处返回的值。使用第二个参数的原因是对于一个setjmp可以有多个longjmp。
例子:
#include <stdio.h>
#include <setjmp.h>
static jmp_buf buf;
void second()
{
printf("first!\n");
longjmp(buf, 1);
}
void first()
{
second();
printf("first!\n");
}
int main()
{
if (!setjmp(buf))
first();
else
printf("main!\n");
return 0;
}
输出:
first!
main!
说明:调用first(),然后调用second(),setjmp返回1,继续进入主函数里面判断。每次longjmp之后都会去检测setjmp(buf).因此输出else的内容。
setjmp与longjmp结合使用时,它们必须有严格的先后执行顺序,也即先调用setjmp函数,之后再调用longjmp函数,以恢复到先前被保存的“程序执行点”。否则,如果在setjmp调用之前,执行longjmp函数,将导致程序的执行流变的不可预测,很容易导致程序崩溃而退出
longjmp必须在setjmp调用之后,而且longjmp必须在setjmp的作用域之内。具体来说,在一个函数中使用setjmp来初始化一个全局标号,然后只要该函数未曾返回,那么在其它任何地方都可以通过longjmp调用来跳转到 setjmp的下一条语句执行。实际上setjmp函数将发生调用处的局部环境保存在了一个jmp_buf的结构当中,只要主调函数中对应的内存未曾释放 (函数返回时局部内存就失效了),那么在调用longjmp的时候就可以根据已保存的jmp_buf参数恢复到setjmp的地方执行。
参考:http://www.cnblogs.com/niocai/archive/2012/04/01/2428128.html
可以使用getrlimit和setrlimit函数来查询和更改资源限制。获取或设定资源使用限制。每种资源都有相关的软硬限制,软限制是内核强加给相应资源的限制值,硬限制是软限制的最大值。非授权调用进程只可以将其软限制指定为0~硬限制范围中的某个值,同时能不可逆转地降低其硬限制。授权进程可以任意改变其软硬限制。RLIM_INFINITY的值表示不对资源限制。
#include <sys/resource.h>
int getrlimit(int resource, struct rlimit *rlim);
int setrlimit(int resource, const struct rlimit *rlim);
resource:可能的选择有
rlim:描述资源软硬限制的结构体,原型如下
struct rlimit {
rlim_t rlim_cur;
rlim_t rlim_max;
};