Linux进程

进程

描述进程

首先程序的本质是文件,存储在磁盘上,加载到内存中的程序称作进程

当程序要执行时,首先要加载到内存中。随着越来越多的程序加载到内存中,操作系统需要内存中的程序加载到CPU中,但是总不能杂乱无章地将所有程序加载到其中,这时操作系统便创建结构体对程序先进行描述,再组织;操作系统只需要管理链表就可以管理程序加载到CPU

组织进程

操作系统创建一个能够描述进程的结构体,称作PCB进程控制块

在这里插入图片描述

接着将每个进程对应的结构体连接在一起,对进程的控制就转化成对链表的控制

在这里插入图片描述

查看进程

指令:ps axj|grep 进程名称

在这里插入图片描述

  1. PPID 父进程
  2. PID 子进程
  3. STAT 进程状态

通过系统调用获取进程标识符

  1. 子进程id getpid()
  2. 父进程id getppid()

在这里插入图片描述

在图中会发现,运行和查看父进程的id没有变化;子进程id不一样,每次查看子进程都会发生变化

在这里插入图片描述

通过系统调用创建进程-fork()

在这里插入图片描述

  1. fork()函数作用是创建子进程
  2. 头文件
  3. pid_t fork(),若返回值大于零,将子进程的id赋值给父进程;若返回值等于零,赋值给子进程。函数执行前只有一个进程,执行之后父进程+子进程
  1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<unistd.h>                                                                                                                                                        
  4 int main()
  5 {         
  6   pid_t id=fork();
  7   if(id==0)       
  8   {        
  9     printf("子进程,pid:%d,ppid:%d,id:%d\n",getpid(),getppid(),id);
 10   }                                                               
 11   else if(id>0)
 12   {            
 13     printf("父进程,pid:%d,ppid:%d,id:%d\n",getpid(),getppid(),id);
 14   }                                                               
 15   else{
 16        
 17   }
 18   return 0;
 19 }          

在这里插入图片描述

进程状态

windows中的进程

如果电脑是单核CPU,再结合之前所学:进程运行时是由链表连接在一起的,通过控制链表进而来控制进程,所以单核也就意味着只有一个链表控制的进程在CPU中运行

  1. 运行
    在某一时刻CPU中只有一个进程,但在一小段时间中CPU可以运行许多进程,因为速度非常快,当进程被链表控制时便是运行状态

  2. 阻塞
    由于CPU的速度非常的快,所以进程是先加载到CPU中等待着被执行, 如果CPU执行某个较慢的进程,剩余的进程只能等待,随着等待的进程越来越多便造成了阻塞
    例如要求在键盘上进行输入的进程加载到CPU中,进行需要先在输入设备中输入内容,内容再加载到内存中,再将内容从内存中加载到CPU中,最后经过CPU的处理最后通过输出设备进行显示,整个过程中除了在CPU中进行处理,其余的过程都是很慢的,这就会导致,后面的进程一直在等待,从而造成阻塞

  3. 挂起
    挂起的前提是阻塞,当进程阻塞到一定程度,CPU无法承受时,便将一部分进程直接放回磁盘中,被放回磁盘中的进程就是挂起状态

所以在windows的操作系统中当进程进入链表中,便是运行状态,所谓进程的不同状态,本质上就是在等待不同的资源

状态介绍

  1. R运行状态:与windows类似,进程不一定在运行,可能在运行也可能在运行队列中在这里插入图片描述
    在这里插入图片描述
    当程序一直执行时,状态便是R+

  2. S睡眠状态:浅度睡眠,进程在等待被执行,阻塞状态的一种,可以被终止在这里插入图片描述
    在这里插入图片描述
    printf需要将数据打印到显示器上,由于显示器是外设所以速度比较慢,CPU需要等待显示器准备就绪,才能打印,所以程序处于睡眠状态

  3. D磁盘休眠状态:深度睡眠,在输入输出操作频繁的进程逐渐积累下,进程进行休眠状态,该状态下只有通过断电,或者进程醒来

  4. T停止状态:阻塞的一种
    将程序强制停止kill -19 进程代号
    在这里插入图片描述
    在这里插入图片描述
    将进程强制停止之后,状态就变成了 T;如果再将程序重新运行起来结果又是怎样的呢? kill -18 进程代号
    在这里插入图片描述
    仔细观察会发现,虽然程序重新运行了,但是状态却是 R;并且只有 Ctrl D才能将程序强制退出,这是为什么呢???
    其实是因为:进程重新运行之后就变成了后台进程;也就是说 R+是前台进程, Ctrl C便可退出; R是后台进程,只有 Ctrl D才能退出

  5. t暂停状态,表示该进程正在被追踪
    调试中打断点,再运行进程就被会被追踪在这里插入图片描述在这里插入图片描述

  6. Z僵尸状态

僵尸进程

进程被创建出来的目的是为了完成任务,并且还需要知道是如何完成的;当进程退出时,不可以立刻释放资源,需要等待操作系统进行读取结果

概念:子进程先退出,父进程还在运行,但是父进程没有读取到子进程状态,此时子进程便是僵尸进程
在这里插入图片描述
在这里插入图片描述
当子进程退出后,父进程一直在运行;但是由于其一直没有读取到子进程状态,所以子进程便处于僵尸进程Z;危害便是:内存泄漏

孤儿进程

既然存在子进程先退出,父进程还在运行,那么也应该存在父进程先退出,子进程还在运行的状态

在这里插入图片描述
在这里插入图片描述

当父子进程同时运行时,直接将父进程杀掉kill -9 14071,便只剩下子进程在运行;可能会有疑问,为什么父进程没有变成僵尸呢?其实是父进程的父进程对其进行资源回收,所以便没有变成僵尸进程;仔细观察会发现,此时子进程的父进程代号变成1,也就是系统进程,表明,子进程已经被系统进程领养,如果领养的话,当子进程退出时,变成的僵尸进程,资源便不能回收;所以领养子进程同样也是为了资源管理;如果前台进程创建的子进程,变成孤儿进程后,自动变成后台进程

进程状态查看

ps axj 命令

在这里插入图片描述

进程优先级

基本概念

CPU资源分配的先后顺序就是进程的优先权,优先权存在的原因是因为CPU资源有限,

查看系统进程

在这里插入图片描述

  1. UID:执行者身份
  2. PID:进程代号
  3. PPID:父进程代号
  4. PRI:进程的优先级,值越小优先级越高
  5. NI:进程的nice值

PRI && NI

PRI,是进程的优先级,其值越小进程的优先级越高;NI,nice值,表示进程优先级的修正数值;PRI值越小越先被执行,加入NI之后,PRI(new)=PRI(old)+nice,PRI(old)值为80;调整优先级的也就是调整nice值;nice的取值范围是(-20,19)

top命令更改已存在进程的nice

  1. sudo top
  2. r->输入PID->输入待修改的nice值

将nice修改为-10,结果如下

在这里插入图片描述

其他概念

  1. 竞争性:系统进程数目众多,而CPU只有少量,甚至1个,所以进程之间具有竞争性;为了高效完成任务,更合理地竞争相关资源,便具有了优先级
  2. 独立性:多进程运行,需要独享各种资源,多进程运行期间互不干扰
  3. 并行:多个进程在多个CPU下,同时进行运行,称之为并行
  4. 并发:多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发

环境变量

基本概念

环境变量,操作系统为了满足不同的应用环境,而预先在系统内设置了一大批全局变量,这些变量在整个系统中可以被其他进程访问

环境变量是指在操作系统中用来指定操作系统运行环境的参数,通常具有特殊用途,在系统中具有全局特性

如果要执行一个程序或指令,需要先找到,然后再执行

举个栗子:

在这里插入图片描述
执行mytest.c,需要先进行make指令,然后在当前的目录中执行程序
在这里插入图片描述

在这里插入图片描述

从上面的指令行中会发现一个疑问:为什么在最后执行程序时,是./后加路径;而在执行ll指令时前面却什么都不加,并且二者都是可执行程序,这是为什么呢???

Linux操作系统安装指令的路径中所包含的指令,一般是在默认搜索路径下进行的,默认情况下不需要加任何有关路径; ./代表当前路径,是为了让系统找到程序,并执行它

可以将./test指令也放到系统安装指令中,这样其也会变成默认的
sudo cp 指令 /usr/bin

在这里插入图片描述

通常不建议这样操作,可能会污染系统的指令池

建议采取环境变量PATH的方式,系统之所以可以找到指令,是因为PATH是系统的命令搜索路径:

在这里插入图片描述

每个之间的是系统默认的搜索路径,指令会先在第一条路径中寻找,找不到换下一条路径,依次类推

将指令放到默认搜索路径中: export PATH=$PATH:指令所在路径

在这里插入图片描述

常见环境变量

  1. PATH:指定命令的搜索路径
    在这里插入图片描述
    上面便是系统默认的搜索路径

  2. HOME:指定用户的工作目录(登录Linux时默认的目录)
    在这里插入图片描述

  3. USER:标识当前Linux用户
    在这里插入图片描述
    这里通过指令来获取的Linux用户,接下来尝试使用函数来获取Linux用户
    char *getenv(const char *name);
    在这里插入图片描述
    该函数可以搜索环境变量,返回指向变量的指针
    在这里插入图片描述
    在这里插入图片描述
    USER最大的意义是:可以表示当前Linux的使用用户;将上面的代码进行修改,进一步体现USER的意义在这里插入图片描述
    在这里插入图片描述

程序本身就是进程,mycmd也会变成进程并且是程序的子进程;环境变量具有全局属性,所以mycmd会继承USER的属性

  1. SHELL:命令行解释器,当前shell,值为 /bin/bash

环境变量相关命令

  1. echo:显示环境变量值
  2. export:设置新的环境变量
  3. env:显示所有环境变量
  4. unser:清除环境变量
  5. set:显示本地定义的shell变量和环境变量

查看环境变量

指令获取

echo $name //name 环境变量名称

在这里插入图片描述

为什么同样是显示环境变量,第一个可以,第二个却不行呢???
其实是因为,val并不是环境变量,而是本地变量,所以第二个并不会打印;也可以采用上面指令的方式进行验证

在这里插入图片描述
在这里插入图片描述

既然val不是环境变量,那就将其转化为环境变量;export val

在这里插入图片描述

./mycmd,就是进程;bash就是一个系统进程, ./mycmd也就是它的子进程,由于环境变量具有全局性,也就继承了bash的环境变量
val=1,本质是本地变量,只能在当前进程内有效

环境变量的组织方式

在这里插入图片描述

命令行参数

在这里插入图片描述
在这里插入图片描述

argc表示命令行中指令的个数;char *argv[]是指针数组,用来指向命令行中的数组

在这里插入图片描述

对代码进一步更改

在这里插入图片描述
在这里插入图片描述

再次观察是不是与指令后加选项一模一样,本质上就是这样的

通过代码获取环境变量

采用命令行方式获取环境变量
在这里插入图片描述

这里的 char*env[]指针数组指向的不是命令行,反而是系统中的环境变量,通过指针可以将全部环境变量打印出来
结果如下:

在这里插入图片描述

程序地址空间

回顾

在之前的学习中,地址空间如下所示,对于地址空间的本质是什么,接下来需要对其进行学习

在这里插入图片描述

地址空间是内存吗?可能是,因为其中包含与代码有关的空间;观察下列代码

在这里插入图片描述
在这里插入图片描述

输出的变量值和地址一样,由于是父子进程,子进程继承父进程,对没有对变量进行修改,好像变量是存储在真实的物理空间中的;对代码进行修改,再次观察

在这里插入图片描述
在这里插入图片描述
此时出现问题,再子进程中将变量进行修改,结果父子进程打印的数值不同,但是变量的地址却还是一样的,这并不符合逻辑;所以,地址空间一定不是内存

既然不是内存,那会是什么呢?为什么不是呢?
在接下来的学习中,会揭晓的

地址空间的本质

地址空间其实是虚拟地址空间(相对于物理真实存在的空间)

进程的角度:
程序对于的进程会认为自己是独占系统资源的(其实并不是),系统会将全部的资源给自己;在进程向系统索要资源时,并不会一次将所有的资源都索要

系统的角度:
所有进程都是认为只有自己,并不知道还有其他进程的存在;系统便会对每个进程都口头承诺给予进程自己所有的资源,也就是所谓的大饼,可以更好地管理每个进程

系统给每个进程所画的大饼其实就是进程地址空间

在这里插入图片描述

既然是画饼,先理解什么是画饼:给你构建一个蓝图,也就是数据结构,口头承诺一些事
生活中:被画饼的对象是员工,目的是为了更好地管理
系统中:被画饼的对象是进程,也是为了更好地管理

当然画饼也是要有逻辑地,所以进程地址空间也是要有逻辑的;所以便采取结构体对进程地址空间进行管理

地址空间的本质:内核的数据结构-结构体

在这里插入图片描述

进程控制块与进程地址空间

在这里插入图片描述

进程地址空间

将进程地址空间加入程序运行的整个过程中,进一步学习进程

在这里插入图片描述

编译器在编译代码时,已经按照虚拟地址空间的方式对代码和数据进行编址,所以可执行程序mytest.exe,在磁盘中已经已经有自己的虚拟地址;当可执行程序加载到内存时,操作系统给其真实的物理地址(0x11112222);CPU通过进程控制块(PCB),找到进程对应的地址空间(mm_struct);地址空间通过自己的区域划分,找到对应的代码区的地址,通过页表(后面再介绍),找到程序在内存中的真实地址;最后将内存中的可执行程序加载到CPU中执行

现在,可以尝试解决上面的问题
在这里插入图片描述

当子进程想要在内存中尝试写入时,操作系统先将数据进行拷贝,更改页表所对应的物理地址,之后再让进程进行修改,变向地保护了数据,也体现了进程的独立性,如果进程对共享的数据进行修改,并且影响其他进程,则进程不具有独立性

总结

  1. 进程空间的存在,变相地保护内存中的数据
  2. 可以更方便地进行进程与进程的数据代码解耦,保证了进程的独立性
  3. 让进程以统一的视角来看待进程对应的代码和数据的各个区域,方便使用
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值