一、进程与程序
程序链接加载和传参
c语言从main函数执行。事实上操作系统下的应用程序在运行main函数前需要执行一段引导代码,由引导代码调用main函数
编译链接时,由链接器将引导代码链接到我们的应用程序当中,一起构成最终的可执行文件。
程序运行需要操作系统中的加载器(一段程序),执行程序时,加载器负责将这段程序加载到内存中执行。
终端执行程序,命令行参数由shell进程逐一解析,shell会将这些参数传递给加载器,加载器加载应用程序时会将其传递给应用程序引导代码,当引导程序调用main函数时将参数一并传递。
程序结束
就是进程终止,包括正常终止和异常终止
正常终止:
-
return返回
-
exit(),exit(),Exit()调用
异常终止:
-
abort()
-
接收到特殊信号,比如SIGKILL
注册进程终止处理函数
atexit()
注册一个进程在正常终止时要调用的函数
int atexit(void (*function)(void));
返回值:成功返回0,失败返回非0
程序使用_exit()和 _Exit()退出不会调用终止处理函数
进程:进程就是一个可执行程序的实例,可执行程序被运行
进程时一个动态过程,不是静态文件,是程序的一个运行过程,当应用程序被加载到内存中运行之后它就是一个进程,程序运行结束意味着进程终止,这就是进程的一个生命周期。
进程号
Process ID(PID),一个正数,用于唯一标识系统中的某一个进程。可以用ps命令查看进程号
1、getpid函数
获取本进程的进程号
pid_t getpid(void);
2、getppid函数
获取父进程的进程号
pid_t getppid(void);
二、进程的环境变量
每个进程都有一组相关的环境变量,环境变量以字符串的形式存储在一个字符串数组列表中,该数组也叫环境列表。
每个字符串都以"name = value"形式定义,环境变量是名称-值的成对集合。
终端可以用env命令查看shell进程的所有环境变量
export添加环境变量 eg:export LINUX_APP=123456
删除环境变量 eg: export -n LINUX_APP
应用程序中获取环境变量
进程的环境变量是从父进程中继承过来的
环境变量存放在一个字符串数组中,应用程序中,通过全局变量environ指向它,申明即可使用
extern char **environ; // 申明外部全局变量 environ
1、getenv函数
获取指定环境变量
char *getenv(const char *name);
返回值:存在返回指针,不存在返回NULL
不要修改返回的字符串,这回导致环境变量被修改
2、putenv函数(劣)
添加环境变量
int putenv(char *string);
string: 指向name = value的字符串,不应为自动变量(在栈中分配的字符数组)
返回值:成功返回0,失败返回非0
3、setenv函数(优)
添加或者修改环境变量
int setenv(const char *name, const char *value, int overwrite);
overwrite:为0,不改变环境变量的值,非0,如果那么存在则覆盖,不存在则创建新的环境变量
setenv和putenv的区别:
-
putenv不会为name=value字符串分配内存
-
setenv可以通过overwrite控制添加和修改环境变量(setenv使用自动变量也可以)
终端向进程传参:
NAME=value ./testAPP
如果有多个环境变量要传入进程,则使用空格隔开
4、unsetenv函数
从环境变量表中移除name环境变量
int unsetenv(const char *name);
清空环境变量
1、将全局变量赋值为NULL
environ = NULL;
2、clearenv函数
int clearenv(void);
某些情况使用setenv和clearenv会导致内存泄露,setenv会为环境变量分配一块内存缓冲区,随之成为进程的一部分,调用clearenv并不知道有这块缓冲区,故而无法释放,反复调用这两个函数会造成内存泄漏。
环境变量的作用
HOME:用户家目录
USER: 当前用户名
SHELL:shell解析器名称
PWD:当前所在目录
三、进程的内存布局
堆(heap):由用户申请释放,若用户不释放,程序结束可能由OS回收
栈(stack):编译器自动分配释放,存放函数参数,局部变量的值
静态区/全局区(static):存放静态变量和全局变量,初始化的在(.rwdata or .data),未初始化的在(.bss),C++不区分data和bss
文字常量区(.rodata):常量字符串,程序结束由系统释放
代码段(.txt):存放函数体的二进制代码
Linux下的size命令可以查看文本段、数据段、bss段的段大小
四、进程的虚拟地址空间
大多数系统采用虚拟内存管理技术
每一个进程都有自己独立的地址空间。
32位系统,每个进程的逻辑地址均为4GB,用户3GB(0x00000000~0xc0000000),内核1GB(0xc0000000~0xffffffff)
Linux系统下,应用程序运行在虚拟地址空间中。
程序中读写的内存地址是虚拟地址,不是真正的地址。如应用程序读写0x80000000这个地址,并不是对应硬件的0x800000000物理地址
为什么引入虚拟地址?
如果应用程序使用物理地址:
-
多个程序需要运行时,要保证内存总量小于实际物理内存大小