C7 : 进程环境



1. 引言

1:程序执行时,main是如何被调用的

2:命令行参数如何传递给新程序

3:典型的存储空间布局样式

4:如何分配额外的储存空间

5:进程如何使用环境变量

6:进程的不同终止方式

7longjmpsetjmp函数以及它们和栈的交互方式

8:进程的资源限制

2.main函数

main函数的原型是int main(int argc, char* argv[]);

内核执行c程序的步骤

内核调用一个启动例程

可执行程序文件将此启动例程指定为程序的起始地址

启动例程从内核获取命令行参数和环境变量参数

运行可执行文件

3.进程终止

5种正常终止方式

1、从main函数返回

2、调用exit

  • void exit (int __status)

• int atexit (void (*__func) (void))

• exit返回时会调用atexit或者on_exit注册的回调函数

  • 执行一些清理动作后返回到内核

执行标准io库的清理关闭操作,对于所有打开的流调用fclose

3、调用_exit_Exit

  • void _Exit (int __status)

• _Exit返回时不调用atexit注册的回调函数

  • 立即返回到内核,不清理

二者的区别

 

 

 

 

4、最后一个线程从启动例程中返回

5、从最后一个线程调用pthread_exit

3种异常终止

调用abort

接收到一个signal

最后一个线程对取消请求的响应

程序的启动和终止

 

 

 

 

4.命令行参数

argc

argv

ISO CPOSIX都要求argv[argc]null

5.环境表

extern char** environ;

字符指针数组

 

 

 

 

6. 存储空间布局

正文段

CPU执行的指令部分

  • 通常为只读,防止程序意外修改指令
  • int a = 10;
  • 存放程序中已初始化的全局变量(包含静态变量)的一块内存区域
  • const char* a = "abc";

初始化数据段

程序中需要明确付初值的变量

可分为read write区域和read only区域

• "abc"read only区域

• aread write区域

未初始化数据段,bssblock started by symbol

long sum[1000];

存放程序中未初始化的全局变量(静态变量)的一块内存区域

自动变量以及函数调用需要保存的信息

  • 最近被调用的函数都会在栈上为其自动和临时分配存储空间
  • 递归函数调用自身时,生成一个新的栈帧。因此,一次函数的调用实例不会影响下一次

堆中进行动态内存分配

典型的存储器排列

 

 

 

 

测试案例:https://www.geeksforgeeks.org/memory-layout-of-c-program/

高地址顶部:命令行参数和环境变量

  • 程序启动时,由内核获取

7.共享库

8.存储空间分配

malloc

void *malloc (size_t __size)

分配指定长度的存储区,初始值不确定

calloc

void *calloc (size_t __nmemb, size_t __size)

分配指定长度的存储区,且每一位都初始化为0

realloc

void *realloc (void *__ptr, size_t __size)

增加或减少之前的分配区,可能需要将之前分配的内容移动到另一个足够大的区域。新增区域的初始值不确定

以上三个分配函数的指针一定是适当对齐的,可适用于任何数据对象

free

void free (void *__ptr)

9.环境变量

getenv

char *getenv (const char *__name)

putenv

int putenv (char *__string)

形式为name=value的字符串,覆盖已有名称的name

setenv

int setenv (const char *__name, const char *__value, int __replace)

设置namevalue,如果name存在,且replace0,则替换现有定义

unsetenv

int unsetenv (const char *__name)

删除当前的name

环境表位于存储空间的顶部,下面就是栈区

 

 

 

 

修改name对于的value

  • 如果长度小于等于原value的长度,则覆盖原value即可
  • 如果长度大于原value的长度,则malloc后重新传递指针给name
  • 没看明白

增加新的name

 

 

 

 

 

10. setjmplongjmp

函数的调用栈

 

 

 

这俩函数用于实现函数栈帧之间的跳转

setjmp

  • int _setjmp (struct __jmp_buf_tag __env[1])

返回0表示直接调用

返回非0,表示longjmp的跳转后的结果

  • 将当前setjmp所在行的栈帧的信息存储在jmp_buf

 

 

 

longjmp

  • void longjmp (struct __jmp_buf_tag __env[1], int __val)
  • 需要跳转的地方执行longjmp

第一个参数是setjmp缓存的jmpbuf

第二个参数是跳转到setjmp后,setjmp的返回结果

longjmp之后的问题

1.自动变量、寄存器变量、易失变量volatile

  • longjmp之后,这些变量的值不确定

不进行任何优化时,自动变量、全局变量、静态变量、寄存器变量、volatile变量都不会改变

优化后,全局变量、static变量、volatile变量不会改变,而其他变量可以改变

  • volatile关键字

• 1:编译器不知道多线程访问的变量特征,不知情哪些变量是在别的线程中修改的

• 2:程序运行过程中,变量会被存储到寄存器的值,而寄存器内的值不会被中断(外部改变变量)影响

• 3:编译器优化,通常,只知晓当前线程的操作,对变量的访问判断仅进行一次读取,后续访问均从寄存器获得。尽管外部线程对该变量进行了改写,当前线程依旧使用当前线程的寄存器(旧的值)

• 4:加上volatile关键字后,编译器不会再对该变量进行任何优化。访问时,会从变量地址处重新获取数据

举例:signal是外部变量,多线程共享

 

 

 

编译器优化的,在初始赋值后,ax不会再有任何改变

 

 

 

这是加上volatile关键字后,循环内部每次访问到ax,则重新读取

 

 

 

11.进程的资源限制

getrlimit

int getrlimit (__rlimit_resource_t __resource,      struct rlimit *__rlimits)

setrlimit

int setrlimit (__rlimit_resource_t __resource,      const struct rlimit *__rlimits)

资源限制,软限制和硬限制(软限制的最大值)

 

posted on 2021-05-17 11:36  炽离  阅读(99)  评论(0编辑  收藏  举报

导航