《UNIX 环境高级编程》笔记0x6:进程环境

此为第七章笔记

main函数

  1. 内核使用函数 exec 执行C程序时,会先启动一个特殊的启动例程
  2. 启动例程从内核中获取命令行参数环境变量值
  3. C编译器调用连接编辑器。
  4. 连接编辑器设置,可执行程序文件将此启动例程指定为程序的起始地址

进程终止

正常终止

  • main 返回
  • 调用 exit
  • 调用 _exit_Exit
  • 最后一个线程从其启动例程返回
  • 从最后一个线程调用 pthread_exit

exit 函数总是执行一个标准I/O库的清理关闭操作:对于所有打开流调用 fclose 函数。三个退出函数都带一个整型参数,称为 终止状态(exit status)


退出函数

atexit 函数可以注册程序退出时执行的清理函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <apue.h>
#include <stdio.h>
#include <stdlib.h>

static void my_exit1(void);
static void my_exit2(void);

int main(int argc, char *argv[])
{
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");
}
exit(EXIT_SUCCESS);
}

static void my_exit1(void)
{
printf("first exit handler\n");
}

static void my_exit2(void)
{
printf("second exit handler\n");
}

输出如下

1
2
3
first exit handler
first exit handler
second exit handler

image1.png


异常终止

  • 调用 abort
  • 接到一个信号
  • 最后一个线程对取消请求做出相应

命令行参数

通过 main 的参数 argcargv。由于其最后以 NULL 结尾,所以可以按照如下遍历

1
2
3
for (i = 0; argv[i] != NULL; i++)
// 或
for (i = 0; i < argc; i++)

获取环境变量

使用全局变量 __environenviron ,定义在 <unistd.h> 中,称为 环境指针

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
char ** env = __environ;
for (int i = 0; env[i] != NULL; i++) {
char *str = env[i];
printf("%s\n", str);
}
exit(EXIT_SUCCESS);
}

列出了一大堆环境变量

image2.png

更好的方式是使用 getenv 函数

1
2
3
#include <stdlib.h>

char *getenv(const char *name);

以下为一些环境变量列表

image4.png


C程序的存储空间布局

  • 正文段:CPU执行的机器指令部分。只读。
  • 初始化数据段
  • 未初始化数据段(bss)

image3.png

使用 size 命令查看程序的相关数据

1
2
3
> size a.out
text data bss dec hex filename
5210 672 64 5946 173a a.out

setjmp和longjmp

书中的例子比较长,所以我在网上找了一个简单的例子。这两个函数的功能是可以跨函数跳转,可以实现类似于 try ... catch 的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>

# define ERR_DIVIDED_BY_ZERO 1

jmp_buf buf;

double divide(double a, double b)
{
if (b == 0.0)
longjmp(buf, ERR_DIVIDED_BY_ZERO); // throw

return a / b;
}

int main(int argc, char *argv[])
{
int ret = setjmp(buf);

switch (ret) {
case 0: // try
printf("%f\n", divide(5.0, 0.0));
break;
case ERR_DIVIDED_BY_ZERO: // catch
printf("divisioning by zero\n");
break;
}

exit(EXIT_SUCCESS);
}

输出为

1
divisioning by zero

资源限制

getrlimitsetrlimit

PDFp195


书后习题

printf 何时被真正输出

如果是在命令行输出,为行缓冲方式,则是遇到换行符的时候。如果是重定向至文件中的,为全缓冲方式,则要等到标准I/O清理执行时才被输出。


修改 atexit 的原型

1
2
typedef void Exitfunc(void);
int atexit(Exitfunc *void);