本文共 7092 字,大约阅读时间需要 23 分钟。
C程序总是从main
函数开始执行的。main
函数的原型是:
int main(int argc,char* argv[]);
argc
:命令行参数的数目argv
:由指向各命令行参数的指针所组成的数组。ISOC
和POSIX
都要求argv[argc]
是一个空指针当内核执行 C 程序时,在调用main
之前先调用一个特殊的启动例程。
exec
函数实现的main
函数做好安排有 8 种方式使得进程终止,其中 5 种为正常终止,3 种异常终止:
main
函数返回exit
函数_exit
函数或者_Exit
函数pthread_exit
函数abort
函数关于多线程部分,在多线程章节讲述
当内核执行 C 程序时,在调用main
之前先调用一个特殊的启动例程。该启动例程是这样编写的:从main
函数返回后立即调用exit
函数。其形式为exit(main(argc,argv))
。
实际上启动例程通常是汇编语言编写的
exit/_Exit/_exit
函数:正常终止一个程序
#includevoid exit(int status);void _Exit(int status);#include void _exit(int status);
status
:终止状态上述三个退出函数的区别:
_exit
和_Exit
函数:立即进入内核exit
函数:先执行一些清理处理,然后返回内核 “清理”代表什么?见下文
exit
和_Exit
是由ISOC
说明的, _exit
是由POSIX
说明的 ,因此头文件不同main
执行了一个无返回值的return
语句,则该进程的终止状态是未定义的main
没有声明返回类型为整型,则该进程的终止状态是未定义的main
声明返回类型为整型,并且main
执行到最后一条语句时返回(隐式返回),则该进程的终止状态是 0main
函数返回一个整型值与用该值调用exit
是等价的。即main
函数中,exit(100);
等价于return 100;
atexit
函数:登记由exit
函数调用的清理函数
#includeint atexit(void (*func) (void));
func
:函数指针。它指向的函数的原型是:返回值为void
,参数为void
一个进程可以登记最多 32 个函数,这些函数将由exit
函数自动调用。这些函数称作终止处理程序(exit handler
)。而atexit
函数就是登记exit handler
的
exit
调用这些exit handler
的顺序与它们登记的时候顺序相反exit handler
被登记多次,则它也会被调用多次 通常操作系统会提供多于32个
exit handler
的限制。可以用sysconf
函数查询这个限制值
C程序的启动和终止
exit
函数首先调用各终止处理程序,然后关闭(通过fclose
)所打开的流。exec
函数exit
函数)的调用_exit
或者_Exit
每个程序都会接收一张环境表。
null
结束的 C 字符串,这些字符串称之为环境字符串null
envrion
包含了该指针数组的地址:extern char **envrion
。我们称environ
为环境指针,它位于头文件unistd.h
中name=value
这种格式的字符串组成
C程序的存储空间布局:C程序一直由下列几部分组成:
N
次,但是该程序的正文段在内存中只需要有一份而不是N
份bss
段。在程序开始执行之前,内核将此段中的数据初始化为0或者空指针。 通过这种方式使用栈,C 递归函数可以工作。递归函数每次调用自身时,就创建一个新的栈帧,因此某一次函数调用中的变量不影响下一次调用中的变量
注意:
size
命令可以查看程序的正文段、数据段 和bss
段长度(以字节为单位)malloc/calloc/realloc
函数:动态分配存储空间
#includevoid *malloc(size_t size);void *calloc(size_t nobj,size_t size);void *realloc(void *ptr,size_t newsize);
参数:
对于malloc
函数:
size
:动态分配的存储空间的大小(字节数)对于calloc
函数:
nobj
:动态分配的对象的数量size
:每个对象的大小(字节数)对于realloc
函数:
ptr
:由malloc/realloc
返回的指针,指向一个动态分配的空间 ptr
是NULL
,则realloc
与malloc
功能相同,是分配一个指定长度为newsize
字节的动态存储空间newsize
:调整后的动态空间的大小(字节数)NULL
注意:
realloc
可以增加、减少之前分配的动态存储区长度。对于增加动态存储区的情况: realloc
分配另一个足够大的动态存储区,然后将原先的内容移动到新的存储区。然后释放原存储区,返回新分配存储区的指针sbrk
系统调用实现。该系统调用用于扩充或者缩小进程的堆空间。 free()
函数进行释放。 malloc
函数但是没有调用free
函数,则该进程占用的存储空间就会连续增加,这称作内存泄漏。free
一次。如果free
多次则会发生错误必须用不同的变量保存realloc
返回的值:
char * ptr=malloc(10);ptr=realloc(1000); # 错误行为
因为,一旦realloc
失败,则ptr
赋值为NULL
。ptr
原来指向的动态内存区再也不能访问,也就无法释放,从而发生内存泄漏
环境字符串的形式是:name=value
。UNIX内核并不查看这些字符串,这些字符串的具体意义由各应用程序解释
getenv
函数:获取环境变量的值:
#includechar *getenv(const char*name);
name
:环境变量名name
关联的value
的指针NULL
注意:
environ
全局变量访问到环境字符串,但是推荐使用getenv
函数 "HOME"
:home
目录"LANG"
:语言"LOGNAME"
:登录名"PATH"
:搜索路径"PWD"
:当前工作目录的绝对路径名"SHELL"
:用户首选的SHELL"TERM"
:终端类型"TMPDIR"
:在其中创建临时文件的目录路径名"TZ"
:时区信息putenv/setenv/unsetenv
函数:设置环境变量的值
#includeint putenv(char *str);int setenv(const char *name,const char *value,int rewrite);int unsetenv(cosnt char *name);
参数:
对于putenv
函数:
str
:形式为name=value
的字符串,将其放置到进程的环境表中。如果name
已经存在,则先删除其原来的语义对于setenv
函数:
name
:环境变量名value
:环境变量的值rewrite
:指定覆写行为。 name
在环境表中已存在,则直接返回而不修改。同时也不报错name
在环境表中已存在,则首先删除它现有的定义,然后添加新的定义对于unsetenv
函数:
name
:环境变量名返回值:
对于 putenv
函数:
对于setenv/unsetenv
函数:
注意:
unsetenv
是从环境表中删除name
的定义。如果name
不存在,则也不算出错name
: value
长度少于或等于现有value
的长度,则只需要将新字符串复制到原字符串所用的空间即可value
长度大于现有value
的长度,则必须调用malloc
为新字符串分配空间,然后将新字符串复制到该空间,接着使环境表中对name
的指针指向新分配区并释放旧分配区name
: name
: malloc
为新指针表分配空间name=value
字符串的指针存放到该指针表的表尾,environ
指向新指针表name
,则可知以前已经调用了malloc
: realloc
,以分配比原空间多存放一个指针的空间name=value
字符串的指针存放到该指针表的表尾,name
:则只需要先在环境表中找到该指针,然后将所有的后续指针都向环境表的首部依次顺序移动一个位置即可在C语言中, goto
语句是不能够跨越函数的。如果想执行跨函数跳转功能,则使用setjmp
和longjmp
,它们称作非局部goto
。
goto
语句在一个函数内实施的跳转,而是在栈上跳过若干调用帧,返回到当前函数调用路径上setjmp/longjmp
函数:非局部goto
#includeint setjmp(jmp_buf env);void longjmp(jmp_buf env,int val);
参数:
对于setjmp
函数:
env
是一个特殊类型jmp_buf
。 setjmp
函数填写longjmp
使用通过一个env
jmp_buf
类型就是某种形式的数组,其中存放的是在调用longjmp
时能用来恢复栈状态的所有信息- 简单地说,
env
参数就是在setjmp
和longjmp
之间传递状态信息
longjmp
函数: env
:它就是setjmp
所设置的env
。它就像是一个锚点,从而跳转时知道跳到哪个位置val
:用于标识本次longjmp
。 setjmp
可能有多个longjmp
对应。因此这些jmp
之间可以用val
分辨。setjmp
就知道是从哪个longjmp
跳转过来的longjmp
的val
参数就是setjmp
的返回值 但是setjmp
的返回值不一定是longjmp
的val
参数setjmp
的返回值:
longjmp
返回,则为 非0 (其实就是所跳转的那个longjmp
的val
参数)注意:
setjmp
之前,有变量包括:全局变量global_var
、局部静态变量static_var
、以及自动变量auto_var
,则跨longjmp
这些变量都不会回滚到setjmp
之前的状态goto
时,在声明自动变量的函数已经返回后,不能引用这些自动变量。因为它们已经被释放了每个进程都有一组资源限制,其中一些可以通过getrlimit/setrlimit
函数查询和修改:
#includeint getrlimit(int resource,struct rlimit *rlptr);int setrlimit(int resource,struct rlimit *rlptr);
resource
:指定资源rlptr
:指向struct rlimit
的指针。在getrlimit
中,它返回资源限制值;在setrlimit
中,它存放待设置的资源限制值其中struct rlimit
为:
struct rlimit{rlim_t rlim_cur; //软限制:当前的限制值rlim_t rlim_max; //硬限制:最大值
注意:
这种降低,对普通用户而言不可逆,因为普通用户不可提高其硬限制值
RLIM_INFINITY
指定了一个无限量的限制resource
可以取下列的常量值之一: RLIMIT_AS
:进程总的可用存储空间的最大长度(字节) 这会影响到sbrk
函数和mmap
函数RLIMIT_CORE
:core
文件的最大字节数。如果为0,则阻止创建core
文件RLIMIT_CPU
:CPU时间的最大量值(秒),如果超过此软限制时,向该进程发送SIGXCPU
信号RLIMIT_DATA
:数据段的最大字节长度(包括初始化数据、非初始以及堆的总和)RLIMIT_FSIZE
:可以创建的文件的最大字节长度。当超过此软限制时,向该进程发送SIGXFSX
信号RLIMIT_MEMLOCK
:一个进程使用mlock
能够锁定在存储空间中的最大字节长度RLIMIT_MSGQUEUE
:进程为POSIX
消息队列可分配的最大存储字节数RLIMIT_NICE
:为了影响进程的调度优先级,nice
值可设置的最大限制RLIMIT_NOFILE
:每个进程能打开的最多文件数。更改此限制将影响到sysconf
函数在参数_SC_OPEN_MAX
中返回的值RLIMIT_NPROC
:每个实际用户ID
可以拥有的最大子进程数。更改此限制将影响到sysconf
函数在参数_SC_CHILD_MAX
中返回的值RLIMIT_RSS
:最大驻内存集字节长度RLIMIT_SIGPENDING
:一个进程可排队的信号的最大数量。这个限制是sigqueue
函数实施的RLIMIT_STACK
:栈的最大字节长度转载地址:http://lvdoi.baihongyu.com/