一. 进程基本概念
- 进程是程序执行的一个实例, 正在执行的程序. 担当分配系统资源的实体
- Linux下操作系统管理进程的方式是 :先描述, 后组织
- 描述指的是利用结构体task_struct描述进程的各项属性, 然后用链表组织所有进程
二. 了解task_struct结构体
- Linux下将进程描述成task_struct结构体
- Ubuntu 18 中, task_struct结构体定义在 /usr/src/linux-headers-4.15.0-46/include/linux/sched.h 560行左右
1. 进程id 系统中每一个进程都有一个唯一id Linux中id用 pid_t 类型表示
2. 进程状态 就绪, 运行, 挂起, 停止
3. 进程切换时保存的寄存器的数据
4. 优先级
5. 内存指针
6. 程序计数器等
...
三. fork() 函数
- 基本使用
pid_t fork(void); // fork 函数有两个返回值
返回值: 成功: 1.返回0 2.返回子进程id
失败: 返回-1
使用fork()函数通常要使用if判断父子进程执行后续逻辑
fork()函数创建的子进程会复制父进程的PCB以及程序地址空间全部信息
- 僵尸进程
僵尸进程: 当进程退出并且父进程没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
僵尸进程会占用资源造成资源浪费, 而且不能使用kill命令杀死僵尸进程
代码模拟僵尸进程:
int main(){
pid_t ret = fork();
if (ret == 0){ // 省略错误处理
sleep(3);
printf("child: %d die\n", getpid()); // 子进程运行结束
}else if(ret > 0){
while(1){
printf("parent is busy\n"); //父进程一直循环, 无法处理子进程
}
}
}
使用 ps -aux 命令查看子进程 可以看到子进程的状态是Z+, 子进程变成一个僵尸进程, 无法用kill杀死
- 孤儿进程
孤儿进程: 父进程先于子进程结束, 子进程被init进程领养, 被称为孤儿进程
代码模拟孤儿进程:
int main(){
pid_t ret = fork();
if (ret == 0){ // 省略错误处理
printf("child: pid[%d] --- parent[%d]\n", getpid(), getppid());
sleep(5);
printf("child: pid[%d] --- parent[%d]\n", getpid(), getppid());
}else if(ret > 0){
printf("parent: pid[%d]", getpid());
sleep(1);
}
}
输出:
parent: pid[3837]
child: pid[3838] --- parent[3837]
child: pid[3838] --- parent[1]
可以看出sleep一秒的父进程先退出后, 子进程被1号进程领养
- 父子进程共享(写时拷贝), 虚拟内存和物理内存
fork()后, 子进程拷贝父进程的虚拟地址空间, 代码段, 数据段, 堆栈都会拷贝, PCB不一样
如果子进程对一个变量进行写操作, 就会拷贝一份新的在物理内存, 否则父子进程共用同一个物理内存中的变量
int g_val = 0;
int main(){
pid_t ret = fork();
if(ret == 0){
g_val = 100;
printf("child[%d], g_val = %d, g_val adress: %p\n", getpid(), g_val, &g_val);
}else if (ret > 0) {
g_val = 50;
printf("parent[%d], g_val = %d, g_val adress: %p\n", getpid(), g_val, &g_val);
sleep(1);
}
输出结果:
parent[4587], g_val = 50, g_val adress: 0x55fcb1179014
child[4588], g_val = 100, g_val adress: 0x55fcb1179014
可以看到父子进程都修改了 g_val 变量, g_val变量打印的地址相同,值不同,因为子进程拷贝了父进程的虚拟地址空间,所以
地址是一样的, 但是父子进程都在物理内存中开辟了一块新的空间,所以值不同 这就是写时拷贝

四. 环境变量相关
- 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数
- 常见环境变量:
1.PATH : 指定命令的搜索路径
2.HOME : 当前家目录
3.SHELL : 当前Shell,它的值通常是/bin/bash
- 环境变量相关命令:
1.echo : 显示某个环境变量的值
2.export: 设置一个新的环境变量
3. env: 显示所有环境变量
- 通过代码获取环境变量(两种方式)
1.通过命令行第三个参数
int main(int argc, char** argv, char** env){ //env中保存了所有的环境变量
for(int i = 0; env[i]; i++){
printf("%s\n", env[i]);
}
return 0;
}
2.通过外部的变量获取
int main(){
extern char **environ; // environ 没有包含在头文件中, 使用的时候要使用extern声明
for(int i = 0; env[i]; i++){
printf("%s\n", env[i]);
}
return 0;
}
- 环境变量相关函数
头文件:<stdlib.h>
char *getenv(const char *name); // 获取环境变量的值 失败返回NULL
int setenv(const char *name, const char *value, int overwrite); //修改或添加一个环境变量
返回 : 0成功 -1失败
int unsetenv(const char *name);
返回 : 0成功 -1失败