[linux]进程详解Part2
系列文章目录
提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 Python 机器学习入门之pandas的使用
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
一、进程状态
提示:这里可以添加本文要记录的大概内容:
1.1 运行状态
一个cpu处理一个调度队列;
只要这个进程在在调度队列中这个进程状态就是运行状态,要么正在被cpu运行要么准备被cpu运行;
1.2 阻塞状态
比如你想从键盘读取数据,但是键盘的状态可能在忙,这个进程在调度队列就处于阻塞状态,操作系统就会把该阻塞进程链接到设备的pcb中的struct device{}中的一个等待队列struct task_struct* wait_queue{}
1.2.1 阻塞挂起
当内存空间严重不足,操作系统就会把wait_queue里一些pcb对应的代码和数据换出到磁盘的swap分区上,腾出点空间给别的进程用,这一过程就是阻塞挂起。当内存空间足够的时候且硬件设备正常运行,pcb进程要被调度,再把其对应的代码和数据加载到内存里。
二、linux中task_struct的链表
他是把pre和next封装到一个结构体里,这样的话linux里面的链表其实上就是网状的结构,且可以通过偏移量来找到最初结构体的内置进行访问。这样的话task_struct就可以隶属多种数据结构

三、代码验证

S(sleeping)(1)阻塞状态
R(running)(0) 运行状态
T(4)暂停,操作系统怀疑代码运行逻辑有问题,状态改为暂停,停止进程运行,由用户决定是否继续
t(8)追踪暂停,用户在调试过程中运行到断点处停下,此时状态也被改为暂停,由用户决定是否继续
D(disk sleep)(2):当进程向磁盘写入数据的时候,进程的状态为D,此时操作系统无权杀掉该进程,避免数据丢失,该状态一般是进程处于高IO状态会出现
X(dead),(16),死亡状态;当一个进程运行结束要退出的时候;
Z(zomble)(32),僵尸状态。保留一个进程退出之前的状态,方便父进程去查看这个子进程的相关信息是什么?
问题:这个相关信息是什么?怎么去查看?
如果父进程不管这个僵尸子进程的返回信息,那么这个进程就会一直存在,从而导致内存泄漏;而且操作系统做不到对僵尸进程的回收,因为要维护这个进程,直到父进程接收返回信息;
关于内存泄漏
什么样的内存泄漏我们需要去操心?
答:打开一直去运行,除非用户自己去关闭的类似进程。
关于内核结构申请
进程里不断地去申请释放太浪费效率,于是当一个进程结束,可以把他的pcb(task_struct)放到一个unuse链表中,这就是数据结构的缓存,等到下一个需要申请pcd的进程来直接用链表中的,再把它放入调度链表中,这种技术在linux操作系统中叫做slab
父子关系中,如果父进程先退出,子进程要被1号进程systemd(操作系统)领养,这个被领养的进程就是孤儿进程(这个进程会变成后台进程,ctrl+c无法将其杀死,需用命令号kill指令)
3.1 进程的优先级和切换
什么是优先级
优先级就是进程得到cpu的先后顺序
为什么要有优先级
目标资源稀缺,导致要通过优先级来确认谁先谁后的问题
优先级本质就是task_struct里面的一个Int整型,就是一个数字
UID:linux中访问任何资源,都是进程访问,进程就代表着用户。
该处使用的url网络请求的数据。
PRI:进程的优先级,默认80
NI:进程优先级的修正数据,nice值
如何更改优先级?top—回车进入top指令
r—回车进入修改(renice)
输入NI值
每一次都是从默认值开始调整。
Linux调整优先级的系统调用?nice,renice命令调整
为什么要改Nice值,而不去直接修改PRI?
为了进程获取资源的公平性,如果你一直将自己的程序优先级调高就会导致那些优先级低的进程长时间拿不到cpu资源
四、进程
竞争性:系统进程数⽬众多,⽽CPU资源只有少量,甚⾄1个,所以进程之间是具有竞争属性的。为了⾼效完成任务,更合理竞争相关资源,便具有了优先级
独立性:多进程运⾏,需要独享各种资源,多进程运⾏期间互不⼲扰
并行:多个进程在多个CPU下分别,同时进⾏运⾏,这称之为并⾏
并发:多个进程在⼀个CPU下采⽤进程切换的⽅式,在⼀段时间之内,让多个进程都得以推进,称之为并发
4.1 进程的切换
4.1.1 死循环进程如何运行
一旦有一个进程占有cpu,
不会把自己的代码一次性跑完!
- cpu会给每一个进程分配一个时间片,在这个时间片内让这个进程运行,这个时间片到了之后,就让别的进程去使用cpu;
- 死循环进程,不会卡死操作系统,不会一直占用cpu
4.1.2 CPU,寄存器
cpu里面含有大量的寄存器,这些寄存器帮助我们存储正在运行的进程的临时数据,每一个寄存器都有对应的功能。
1.寄存器就是一个空间,能存储不同的内容
2.寄存器在cpu中占有少部分空间
4.1.3 如何切换
切换核心,就是保存和恢复当前进程的硬件上下文数据,即cpu内寄存器的内容;这些运行数据将会保存到进程的task_struct中的TSS(任务状态段)结构体中.
一个全局的指针变量struct task_struct* current,永远指向当前cpu正在运行的进程的task_struct的结构体。
操作系统分为两类:
分时操作系统:平等划分时间片进行调度
实时操作系统:立即对进程做出相应,一个进程跑完再去跑另一个进程
4.2 O(1)调度
调度和切换共同构成了OS中的调度器

运行队列里有一个结构体数组struct prio_array_t[2],array[0]指的就是活跃队列,struct prio_array* active指的就是它;array[1]就是过期队列,struct prio_array *expired指向它;当一个进程在cpu运行过他的时间片之后,这个进程从cpu剥离下来就会放到过期队列,当活跃队列中的进程全部运行完毕,在进行交换swap(&active,&expired);
queue[140]就是他的优先级,但是我们说优先级不是(60-99)一共40个优先级吗,答案是queue中前100个是实时优先级,日常操作系统用不到,通常在工业领域使用;具有相同优先级的进程挂到queue的同一下标下,根据哈希算法映射(优先级-80)+(140-40)下标。
五、环境变量
5.1 是什么?
一般是操作系统用来指定操作系统操作系统运行环境的参数;
环境变量可以帮助编译器查找要进行链接的动静态库;且他们通常具有一些特殊用途,在系统中具有全局特性。
5.1.1 命令行参数
首先进行一个提问,我们通常在用C语言写main函数的时候,有没有参数?
答案是有的
int main(
int argc,char* argv[])
第一个参数是argv字符指针数组的个数;那么这个argv里面都是什么?
我们输入的命令就是命令行参数;
我们直到main函数使我们自己程序的额入口,但是main函数也是需要被调用的,在系统当中,第一个不是执行的main函数而是startup函数,由他去调用main()
命令行参数可以完成通过不同的选项去完成不同的子功能。
为什么我们自己写的程序需要加上./,而执行系统命令就不用呢?
要执行对应的程序首先需要找到他,而执行系统指令由环境变量来查找。/user/bin 目录下默认就能找到。
5.2 怎么用
env命令用于查找系统中所有的环境变量。
环境变量用名称来标识唯一性,要查找内容:echo $环境变量

如果直接把自己的程序所在路径直接复值给环境变量,就会把原来环境变量的的内容覆盖,就导致执行不了系统指令(解决方法:可以把原来的路径内容再重新给环境变量赋值一遍,或者重启机器,因为环境变量是内存级别的)
正确做法应该是拼接做法:

5.2.1 如何理解环境变量(从存储的角度)
系统一经启动,bash进程就开始循环运行,在bash内部维护着两张表,一张是环境变量的指针数组,一张是这个数组每一个指针指向的对应环境变量的内容,即命令行参数表,当执行命令的的时候,bash就去着两张表里去查找并执行。
得出结论:环境变量是存储在bash进程里的上下文数据里的。
5.2.2 环境变量的来源
环境变量一开始就存在在系统的配置文件中

所以bash起动的时候也是从配置文件中加载过去,形成来环境变量表和命令行参数表;也就是说,我们想不带路径直接执行自己的程序,也可以直接修改配置文件
vim .bashrc
在最后一行添加(自己程序的路径)
export PATH=$PATH:/home/zyt/path_of_-linux/lesson4
这是我写的程序code
#include<stdio.h>
#include<string.h>
int main(int argc,char* argv[])
{
if(argc!=2)
{
printf("%s:Usage [-a/-b/-c]\n",argv[0]);
}
else if(strcmp(argv[1],"a")==0)
{
printf("这是功能一\n");
return 0;
}
else if(strcmp(argv[1],"b")==0)
{
printf("这是功能二\n");
return 0;
}
else if(strcmp(argv[1],"c")==0)
{
printf("这是功能三\n");
return 0;
}
else
{
printf("%s:Usage [-a/-b/-c]\n",argv[0]);
}
// for(int i=0;i<argc;i++)
// {
// printf("argc[%d]:%s\n",i,argv[i]);
// }
return 0;
}

5.2.3
更多环境变量
HOME:自己的家目录,如cd ~快速回到家目录
SHELL:用户在登陆时用的是哪一个版本的shell,把对应的路径给标识出来了

USER:su命令只是用了root的账号,不会更改user和LOGNAME,但是su -会修改,相当于让root重新登录
HESTSIZE:bash记录的最多的历史命令个数

bash记录历史命令
ctrl+r 搜索
!v直接执行最近一次vim命令
上下翻搜索
OLDPWD:上一次的工作路径:cd - 快速切换最近两次的工作路径
5.2.4 获取环境变量的方法
给bash的环境变量表导入自己建的环境变量
export 变量名=变量内容
取消环境变量
unset 环境变量名称
父进程的环境变量可以传递给子进程,环境变量具有全局特性
- 第一种就是通过main函数的第三个参数,char*env[]
- 第二种方法时通过函数getenv();查到了就返回这个环境变量内容字符串的首地址,查询失败返回NULL;(头文件stdlib.h)
- 第三种方法是通过extern char** environ
5.2.5 环境变量的特性
bash会记录本地变量和环境变量;
本地变量不会被子进程继承,set命令可以查看本地变量和环境变量。
环境变量存在于bash里面,export是内键命令,不创建子进程,由bash自己执行。
六、程序地址空间
指针用到的地址都是虚拟地址,不是内存地址。
6.1 什么是虚拟地址空间

当父进程和子进程拿着同一个虚拟地址但却映射不同的物理地址空间。这就解释了为什么一个函数却有两个不同的返回值。
当子进程要对父进程拷贝过来的代码和数据进行写入,就会进行写时拷贝;
写时拷贝并不是说把父进程的数据全部进行拷贝,而是子进程需要改动那个就写时拷贝那部分数据,这样更加节省内存,减少内存浪费,节省子进程创建时间
虚拟地址就是在内核中,操作系统给每一个进程创建的结构体对象。这个结构体对象里最重要的就是记录各个区域的开始和结束的数据。
一句话总结:虚拟地址空间就是PCB里的一个结构体对象,存着各区域的开始结束的数据。
一个进程对应一套虚拟地址空间,一个进程对应一个页表;页表就是把虚拟地址空间和内存中的物理地址空间进行一一映射,包含着每一个地址的(r,w,e权限);
6.2 为什么要有虚拟地址空间
1.将内存中的代码和数据从
“无序”变成“有序”
有时候磁盘中的代码和数据过大,加载到内存中会代码会进行分段,分散到不同地址区域下;且不同进程的代码和数据交叉分布,是“无序的”;而虚拟地址空间恰好通过页表把分散的代码和数据整合到一起,使其变得有序;
2.地址转换的过程中,可以对内存的地址和操作进行合理的保护;
有时候访问野指针,操作系统在页表中找不到该地址对应的内容,就会进行拦截报错,杀掉进程,进而保护内存中的数据;
比如不小心修改了常量字符串,那么运行时也会崩,因为页表对应的地址只有r权限,这就是权限拦截;‘’
3.让进程管理和内存管理进行一定程度上的解耦合。
当磁盘中的代码和数据过大,磁盘不会一股脑得把代码和数据加载到内存里,是虚拟地址空间先把要用的空间开够,磁盘在加载一部分代码和数据到内存中,页表进行映射,当运行完这一段代码之后,虚拟地址合法但是物理地址不在内存里,页表还有一个标记位(标记物理地址是否在内存里,如果不在,OS自动做缺页中断)在重新加载一部分,重新填充页表进行映射,再次运行;
小贴士:
- 进程是先创建task_struct,在进行加载代码和数据;即当一个进程被创建,可以不加载代码和数据,等需要在进行加载
- 如何理解进程挂起?我们说当一个进程为S(阻塞挂起),且内存空间严重不足,OS自动把挂起的进程交换到磁盘的swap分区里,实际上就是把
页表中的物理地址清空,把内存中对应的代码和数据放到磁盘的swap分区里,等需要再进行加载和页表的映射
更多推荐










所有评论(0)