📓 APUE 一书的第七章学习笔记。java
有 8 种方式可使得进程终止,5 种为正常方式:数组
main
exit()
_exit
or _Exit
pthread_exit
from the last thread3 种非正常终止方式:bash
abort
函数原型:架构
#include <stdlib.h> void exit(int status); void _Exit(int status); #include <unistd.h> void _exit(int status);
区别:_exit/_Exit
会当即进入内核;exit
先执行清理处理(对全部打开的 Stream 执行 fclose
),后进入内核。函数
exit(k)
至关于在 main
函数中 return k
,最近一个进程的退出码能够在 Shell 中使用 echo $?
获取。布局
按照 ISO C 标准,一个进程最多能够登记 32 个回调函数,这些函数称为终止处理程序 (exit handler),由 exit
来调用。学习
函数原型:spa
int atexit(void (*func)(void)); // Returns: 0 if OK, nonzero on error
exit
调用终止处理函数的顺序与登记顺序相反(_exit/_Exit
则不会调用);若是登记屡次,也会被调用屡次。命令行
例子:指针
void test1() { printf("A "); } void test2() { printf("B "); } int main() { atexit(test1); atexit(test1); atexit(test2); // 若是调用下面 2 个函数,则不会有输出 // _exit(0), _Exit(0) } // Output: B A A
下图展现了一个 C 程序如何启动和终止的过程,也显示了 exit, _exit, _Exit, atexit
这 4 个函数的关系。
环境表 (Environment List), 与命令行参数 argv
同样,是一个 char*
数组。
下列程序能够打印全部的环境参数:
#include <stdio.h> extern char **environ; int main() { int i; for (i = 0; environ[i] != NULL; i++) puts(environ[i]); }
输出一大片:
TERM=xterm-256color SHELL=/bin/bash ... USER=sinkinben LS_COLORS=rs=0:di=01;... PATH=... MAIL=/var/mail/sinkinben PWD=/home/sinkinben/workspace/apue HOME=/home/sinkinben ... _=./a.out
以下图所示,一个 C 程序由如下部分组成:
static
修饰的变量。exec
初始化为 0 或者空指针。(确实,我还觉得是随机值)这是一种典型的逻辑布局,但不是全部的实现都是如此,具体取决于实际的 OS 和硬件。对于 32 位 Intel x86 架构的 Linux,text 段从 0x80480000 开始,栈从 0xC0000000 开始向低地址增加。
能够经过 size
命令获取一个 C 程序的各个段大小,dec, hex
分别是前 3 个数字的总和的十进制和十六进制:
$ size /bin/bash text data bss dec hex filename 997958 36496 23480 1057934 10248e /bin/bash
共享库 (Shared Libraries): 对于一些经常使用的公共函数库,只须要在全部进程均可引用的存储区保存一份副本。程序第一次调用某个库函数的同时,用动态连接的方式将程序与库函数相连接。这就减小了可执行文件的大小,但增长了一些运行时开销。
使用 Shared Libraries 的另一个优势是:能够动态更新某个库的版本,而不须要从新对使用该库的程序从新连接。
使用 gcc -static
可指定生成的可执行文件使用静态连接。
$ gcc test.c ; size ./a.out text data bss dec hex filename 1099 544 8 1651 673 ./a.out $ gcc test.c -static ; size ./a.out text data bss dec hex filename 823142 7284 6360 836786 cc4b2 ./a.out
相关函数:
#include <stdlib.h> void *malloc(size_t size); void *calloc(size_t nobj, size_t size); void *realloc(void *ptr, size_t newsize); // All three return: non-null pointer if OK, NULL on error void free(void *ptr);
做用:
malloc
: 申请指定字节数的内存,该内存的值不肯定。calloc
: 为指定数量,指定长度的对象分配内存,并初始化为 0 。realloc
: 增长或者减小 ptr
指向内存区的长度。当增长长度时,可能须要将以前的数据拷贝到另一个足够大的内存区(也有多是在原有基础上增长一段连续内存),新增区域的初始值不肯定。#include <stdlib.h> char *getenv(const char *name); // Returns: pointer to value associated with name, NULL if not found
获取环境变量。例子:
#include <stdio.h> #include <stdlib.h> int main() { puts(getenv("JAVA_HOME")); } // Output: /usr/local/java/jdk1.7
相关 API:
#include <stdlib.h> int putenv(char *str); // Returns: 0 if OK, nonzero on error int setenv(const char *name, const char *value, int rewrite); int unsetenv(const char *name); // Both return: 0 if OK, −1 on error
改变当前进程以及后续产生的子进程的环境变量(其实是修改进程的环境表)。做用分别以下:
putenv
: 把 name=value
的环境变量添加到环境表,若是 name
已存在,则删除原来的定义。setenv
: 将 name
设置为 value
。rewrite = 0/1
表示是否覆盖已有的 name
(若是有的话)。unsetenv
: 删除 name
,若是不存在则什么都不作。每一个进程都有一组资源限制,能够经过 getrlimit, setrlimit
进行查询和修改:
#include <sys/resource.h> int getrlimit(int resource, struct rlimit *rlptr); int setrlimit(int resource, const struct rlimit *rlptr); // Both return: 0 if OK, −1 on error
resource
是形如 RLIMIT_CPU
的一组宏定义。
结构体 rlimit
的定义以下:
struct rlimit { rlim_t rlim_cur; /* soft limit: current limit */ rlim_t rlim_max; /* hard limit: maximum value for rlim_cur */ };
修改操做须要遵循如下规则:
cur <= max
rlim_max
,但必须大于等于 rlim_cur
.rlim_max
。修改资源限制会应当当前进程和它的子进程,因此 Shell 中通常会内置 ulimit
命令来修改当前 Shell 的资源限制。