从零开始学习Linux(6)----进程控制

1.环境变量

环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数,我们在编写C/C++代码时,链接时我们不知道我们链接的动态静态库在哪里,但可以连接成功,原因是环境变量帮助编译器进行查找,环境变量通常具有某些特殊用途,还有在系统当中中通常具有全局特性。

环境变量不是一个而是一堆,彼此之间其实没有关系。

环境变量一般是系统内置的具有特殊作用的变量。

定义变量的本质,其实是开辟空间,在运行期间我们的程序也能开辟空间,操作系统/bash是用C语言写的程序,它能在运行中开辟空间,系统的环境变量,本质就是系统自己开辟空间,给他名字和内容即可。

1.命令行参数

cpp 复制代码
int main(int argc,char *argv[],char *env[])
cpp 复制代码
#include <stdio.h>
#include <string.h>
int main(int argc,char *argv[])
{
    if(argc!=2)
    {
        printf("Usage:\n\t%s -number[1-3]\n",argv[0]);
        return 1;
    }
    if(strcmp("-1",argv[1])==0)
    {
        printf("function 1\n");
    }                                                     
    else if(strcmp("-2",argv[1])==0)
    {
        printf("function 2\n");
    }
    else if(strcmp("-3",argv[1])==0)
    {
        printf("function 3\n");
    }
    else
    {
        printf("unknown!\n");
    }
}

我们可以通过不同的选项,让我们的同一个程序执行它内部不同的功能。命令行参数时Linux指令的基础。

在上面的演示中,为什么我们不能像ls命令一样执行而是./的方式来执行,这时需要将你的程序所在的路径添加到环境变量中。

指令env查看系统所有环境变量。命令行参数env[]中存放的也是系统所有环境变量,export命令导入环境变量。环境变量是可以被子进程继承下去的!(方式1),环境变量具有全局属性,会被所有的子进程继承。还可通过第三方变量environ,和getenv方法来获取。本地变量只能bash内部有效。

2.程序地址空间

子进程按照父进程为模版,父子并没有对变量进行任何修改,如果子进程先修改,完成之后父进程再读取,我们发现父子进程输出地址是一致的,但是变量内容不一样,所以:
变量内容不一样,所以父子进程输出的变量绝对不是同一个变量但地址值是一样的,说明,该地址绝对不是物理地址! 在Linux地址下,这种地址叫做 虚拟地址,我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理。OS必须负责将 虚拟地址 转化成 物理地址 。
同一个变量,地址相同,其实是虚拟地址相同,内容不同其实是被映射到了
不同的物理地址。

1.什么是地址空间

进程地址空间,每一个进程,都会存在一个进程地址空间,32【0,4GB】;进程地址空间是数据结构,就是特点数据结构的对象。区域划分本质就是区域内的各个地址,都可以使用,我们的地址空间不具备对我们的代码和数据的保存能力,将地址空间上的地址转化到物理内存中,给我们的进程提供一张映射表---页表。

2.为什么要有地址空间+页表

将物理内存从无序变有序,让进程以统一的视角看待内存。

将进程管理和内存管理进行解耦合。

地址空间+页表是保护内存安全的重要手段。

1.写时拷贝

进程调用fork,当控制转移到内核中的fork代码后,内核做:

  • 分配新的内存块和内核数据结构给子进程
  • 将父进程部分数据结构内容拷贝至子进程
  • 添加子进程到系统进程列表当中
  • fork返回,开始调度器调度

通常,父子代码共享,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本。所以,fork之前父进程独立执行,fork之后,父子两个执行流分别执行。

3.进程终止

main函数的返回值叫做进程的退出码,一般0,表示进程执行成功,非零表示失败,不同的数字表示不同的错误原因。可以通过echo $?查看进程退出码。

错误码转化为错误描述:1.可以使用系统自带的。2.自定义。除了进程退出,函数退出,我们怎么知道函数的执行情况。函数有返回值,调用函数,我们通常想看到两种结果:1.函数的执行结果。2.函数的执行情况--成功,失败,原因。

进程退出的场景就三种:1.代码执行完,结果是正确的。2.代码执行完,结果是不正确的。3.进程代码没有执行完,进程出异常了(是进程收到了异常信号,每个信号都有不同的编号,不同的信号编号表明异常的原因)。

任何进程最终的执行情况,我们可以使用两个数字表明具体执行的情况。
exit就是用来终止进程的。exit(退出码)在我们的进程代码中,任意地方调用exit,都表示进程退出,exit会支持刷新缓冲区,_exit不支持,我们之前所说的缓冲区绝对不是操作系统里面的缓冲区,而是上层的库的缓冲区。

4.进程等待

  • 之前讲过,子进程退出,父进程如果不管不顾,就可能造成'僵尸进程'的问题,进而造成内存泄漏。
  • 另外,进程一旦变成僵尸状态,那就刀枪不入,"杀人不眨眼"的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。

1.父进程通过wait方式,回收子进程的资源(必然)

2.通过wait方式,获取子进程的退出信息(可选的)

wait默认会进行阻塞等待,等待任意一个子进程,返回值>0:等待成功,等待子进程的pid,<0:等待失败。

pid_t wait(int*status);
返回值:
成功返回被等待进程pid,失败返回-1。
参数:
输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
waitpid
pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
pid:
Pid=-1,等待任一个子进程。与wait等效。
Pid>0.等待其进程ID与pid相等的子进程。
status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options:
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进 程的ID。

  • 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
  • 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
  • 如果不存在该子进程,则立即出错返回。
    获取子进程status
  • wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
  • 如果传递NULL,表示不关心子进程的退出状态信息。
  • 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
  • status不能简单的当作整形来看待,可以当作位图来看待(只研究status低16比特位)
相关推荐
小政同学7 分钟前
【NFS故障】共享的文件无法执行
linux·运维·服务器
AI木马人27 分钟前
3.【Prompt工程实战】如何设计一个可复用的Prompt系统?(避免每次手写提示词)
linux·服务器·人工智能·深度学习·prompt
lwf00616427 分钟前
导数学习日记
学习·算法·机器学习
ch3nyuyu39 分钟前
Ubuntu(乌班图)基础指令
linux·运维·网络
qeen871 小时前
【编程日记】现阶段总结
学习
minglie11 小时前
gcc编译器汇总
linux
挽安学长1 小时前
保姆级教程,通过GACCode使用Claude Code Desktop!
运维·服务器
firstacui2 小时前
MGRE实验
运维·服务器·网络
白菜欣3 小时前
Linux —《开发三件套:gcc/g++、gdb、make/Makefile 全解析》
linux·运维
何中应3 小时前
Grafana如何给列表设置别名
运维·grafana·监控