Linux 进程概念

目录

1. 冯诺依曼体系结构

1.1 五大硬件单元

1.2 操作系统

2. 进程概念

2.1 操作系统如何管理进程

2.2 操作系统如何描述进程 

2.3 查看进程:

2.4 创建进程(创建PCB):

2.5 进程状态

2.6 进程优先级 PRI

3. 环境变量

4. 程序地址空间

5. 进程调度


1. 冯诺依曼体系结构

现代计算机的硬件体系结构

  • 1.1 五大硬件单元

  1. 输入设备:键盘
  2. 输出设备:显示器
  3. 存储器:内存
  4. 运算器
  5. 控制器
  • 内存 -> 大脑正在思考的数据的区域(30Gbps,每秒30Gbit)
  • 硬盘 -> 大脑中存放记忆的区域(机械200MB/s,固态3-400MB/s  7-800MB/s)
  • CPU主频 -> 时钟震荡周期 -> 机器指令周期。主频越高,CPU单位时间内能处理的指令越多
  • 硬件结构决定软件行为

    • 所有设备都是围绕存储器(内存)工作的,所有数据都是围绕内存流动的。
    • 输入设备获取数据存储到内存中,CPU从内存中获取数据,处理数据,运算完毕放入内存,输出设备从内存中获取数据。

  • 两种主要CPU架构:RISC(精简指令集)、CISC(复杂指令集)

  • 通常说的32位、64位,值得是CPU一次可以读取32/64bits的数据

  • 1.2 操作系统

操作系统=内核+应用

  • 操作系统功能:管理计算机中的软硬件资源,为上层应用程序提供良好的执行环境。让计算机使用更加方便,用户体验更好。
  • 操作系统 -> 负责管理的软件 ,对下管理软硬件资源,对上提供良好的执行环境。
    • 管理:
      • 描述+高效的组织,先描述被管理者,再组织起来进行管理
      • 描述——结构体,组织——链表
    • 硬件驱动将硬件信息收集起来,交给操作系统。操作系统对这些信息进行管理。
    • 系统调用接口:操作系统向上层用户提供的操作接口,提供良好的执行环境。
      • 库函数(lib)和系统调用接口的关系:
        • 上下级的调用关系,库函数就是系统调用接口的一层封装。

2. 进程概念

  • 从用户角度来说,进程就是进行中的程序(抽象化概念),程序运行起来需要被加载到内存。
  • 从操作系统角度来说,进程就是操作系统为了更好的管理程序,对运行中的程序进行信息描述(具象化描述)- 进程描述符PCB。
    • 程序:存储在硬盘中的可执行程序文件
  • 2.1 操作系统如何管理进程

先描述后组织

  • 描述信息称为进程控制块PCB ,Linux下的PCB是结构体struct,称为task_struct。
  • 使用task_struct结构体描述进程,使用双向链表将这些结构体组织起来进行管理
  • task_struct包含着进程的信息,被装载到内存中。
  • 操作系统通过task_struct来运行程序。
  • 2.2 操作系统如何描述进程 

操作系统去双向链表中通过PID找PCB。

  • 2.2.1 task_struct内容分类:
    • 标识符pid:用于区别其他进程的唯一标识符
    • 进程状态
    • 上下文数据:保存程序当前正在处理的数据(学生休学要保留学籍,而上下文数据就是保留的学籍)
    • 程序计数器:保存即将被执行的下一条指令的地址(下一个需要处理的学生)
    • 进程优先级:cpu优先分配权,让操作系统运行更加合理
    • 内存指针:指向该进程运行起来后,程序以及数据被加载到内存中的位置
    • 文件状态信息:I/O请求、被进程使用的文件列表
    • 记账信息:进程在cpu上的运行时间
  • 进程按功能分类

交互式进程:与用户进行交互的进程,优先级较高

批处理进程:在后台一直运行的进程,优先级较低

  • 进程的切换调度:
    • cpu的分时机制(并发):cpu只是在每个进程上运行很短的一段时间(时间片),如果cpu处理某个进程的时候,时间片用尽,namecpu就要去调度运行个下一个进程了
    • 一个进程并不是一直在被cpu处理的,而是不断地轮询切换被cpu调度运行
  • 一个进程运行时,就会创建一个目录,保存进程运行信息,当进程退出时,其目录就会被删除掉。
    • /dev/pts/0  终端文件
  • 2.3 查看进程:

    • /proc  目录保存当前运行的进程信息

      • 获取PID为1的进程信息 ->  /proc/1 

    • ps:查看进程信息
      • -ef         
        • 用标准的格式显示进程
          • UID:用户id
          • PID :进程id
          • PPID :父进程id
          • C:进程占用CPU的百分比
          • STIME:进程启动到现在的时间
          • TTY:该进程在那个终端上运行 -> 若与终端无关,则显示?   若为pts/0等,则表示由网络连接主机进程
          • TIME:该进程实际使用CPU运行的时间
          • CMD:命令的名称和参数
      • aux        
        • 用BSD的格式来显示进程
          • USER
          • PID
          • %CPU:进程占用的CPU百分比
          • %MEM :占用内存的百分比 
          • VSZ :该进程使用的虚拟內存量(KB)
          • RSS:该进程占用的固定內存量(KB)
          • TTY
          • STAT:进程的状态 
          • START:该进程被触发启动时间 
          • TIME
          • COMMAND
    • top:查看服务器负载
    • getpid():获取进程pid 
    • getppid():获取父进程pid 
#include<stdio.h>
#include<unistd.h>

int main(){
    //pid_t getpid(void);
    //获取调用进程的pid,谁调用返回谁的pid
    pid_t pid=getpid();
    printf("pid:%d\n",pid);
    printf("ppid:%d\n",getppid());
    while(1){
         sleep(1);
     }
     return 0;
}
  • 2.4 创建进程(创建PCB):

fork():操作系统提供的创建进程的接口。

  • 通过复制调用进程创建一个新的PCB(子进程),复制的是父进程的PCB  (PID不同)
    • ->  子进程与父进程代码和数据指向同一块内存区域(数据和代码运行的完全一样),并且子进程复制了程序计数器、上下文数据。
      • ->  当前的子进程不是从代码的起始位置开始运行,而是从子进程创建成功(fork())的下一步指令开始运行的。
  • 总结上述内容:父子进程代码共享,数据独有
#include <stdio.h>
#include <unistd.h>
#include <errno.h>

int main(){
    //pid_t fork(void);
    //通过复制调用进程,创建一个新的子进程
    pid_t pid=fork();
    //因为父子进程返回值不同,虽然代码相同,父进程因为返回值会进入不同的判断执行体
    if(pid==0){
        printf("i am child! pid:%d\n",getpid());
    }else if(pid>0){
        printf("i am parent! pid:%d\n",getpid());
    }else{   //<0,出错
        //perror:打印上一个系统调用的错误信息
        perror("fork error");
        return -1;
    }
    printf("pid:%d\n",getpid());
    while (1){
		printf("this is public pid:%d  !\n", getpid());
		Sleep(1);
        //每间隔1s,打印两条,说明父子进程同时运行
        //printf("----\n");
    }
    return 0;
}
  • 2.5 进程状态

阻塞态、运行态、就绪态

  • linux下的进程状态:
  1. 运行  R -> Running    (上述的运行+就绪)
  2. 可中断睡眠  S -> sleeping    (随时可被唤醒)
  3. 不可中断睡眠   D -> disk sleep     (特殊手段才可被唤醒,操作完毕后自动唤醒)
  4. 停止  T -> stopped  (被暂停的进程可 以通过发送 SIGCONT 信号让进程继续运行 )
  5. 僵尸  Z -> zombie
  6. 追踪状态   t -> tracing stop
  7. 死亡  X -> dead
  • 僵尸进程:处于僵尸状态的进程(还未完全退出,仍占用系统资源)
    • 危害:资源泄露,僵尸进程过多导致正常进程可能无法创建
    • 产生原因:子进程先于父进程退出,操作系统检测到子进程退出后通知父进程,但没有联系到父进程,操作系统不敢擅自释放子进程的资源(子进程的pcb中包含有退出原因,一旦释放就没地方保存退出原因)。这时,子进程处于退出但是资源没有完全释放的状态(既没有运行又没有退出),子进程就成了僵尸进程。
      • 父进程需要根据子进程的退出原因判断子进程是否成功完成任务
    • 解决:杀死父进程(退出原因的保存已经毫无意义)
    • 预防:进程等待
    • 一个进程无法杀死,该进程可能就是僵尸进程
  • 孤儿进程:
    • 产生原因:父进程先于子进程退出,子进程成为了孤儿进程,该孤儿进程将被“孤儿院” init 进程领养,子进程退出后将由init进程来回收释放资源,因此孤儿进程不会产生僵尸进程。
  • 守护进程/精灵进程:特殊的孤儿进程 -> 在孤儿进程的基础上进行一定的操作。
  • 2.6 进程优先级 PRI

    • 数字,决定进程CPU资源的优先分配权
    • PRI值越小,优先级越高
      • 默认优先级80,优先级无法直接修改,通过设置NI值进而对优先级做出设置
      • PRI=PRI+NI
    • 查看: ps -l
    • 交互式进程优先级较高,批处理进程优先级较低
    • 设置:
      • renice
        • 程序运行后设置
          • renice -n ni_val -p pid  将标识符为PID的进程的NI值修改为ni_val
      • nice(NI)
        • 程序运行时设置
          • nice取值范围:-20~19
          • nice -n ni_val 程序   将该程序的进程的NI值修改为ni_val
    • 进程相关概念:
      • 竞争性:操作系统有多个进程,而CPU只有一个进程,进程会抢占CPU资源。
      • 独立性:进程之间相互独立,每个进程都不受到其他进程的影响,保证进程稳定运行。
      • 并发:CPU资源不够时,切换调度运行。
      • 并行:CPU资源足够的情况下,多个进程同时运行。

3. 环境变量

操作系统中用来设置操作系统运行环境参数的变量,具有全局性

  • 作用
    • 让系统设置更加方便,程序运行更加高效(因为环境变量具有全局特性)、规范
  • 指令
    • env     查看所有环境变量
    • echo $变量     查看指定环境变量
    • set      查看所有变量(包含环境变量)
    • unset 变量      删除一个变量 / 环境变量
    • export  变量=值    (定义)声明一个环境变量,不加export则声明的是局部变量
//环境变量的全局特性演示

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(){
	//char *getenv(const char *name)
	//通过环境变量名称来获取环境变量值
	char *ptr=getenv("MYVAL");
	printf("MYVAL:%s\n",ptr);
	return 0;
}
  • 环境变量在代码中的获取使用及全局特性:
    • 接口 getenv():char *getenv(char *env_name)
    • main第三个参数:main(int argc,char *argv[],char *env[])
    • 声明全局变量char **environ:extern char **environ
  • 常见环境变量:PATH HOME SHELL

4. 程序地址空间

  • 每个程序都有自己的程序地址空间,程序地址空间实际上是一个虚拟地址空间。
    • 虚拟地址空间:假的内存地址,是一个结构体(进程的内存描述符):mm_struct
      • 结构体包含
        • 代码的起始与结束地址(code_start、code_end)
        • 数据的起始与结束地址(data_start、data_end)
        • 总的大小(size)
      • 内存地址
        • 内存区域的一个编号,地址指向内存的某个位置
        • 虚拟地址空间使用的就是虚假的内存地址编号

  • 为什么使用结构体描述一段内存,而不是直接使用物理内存?
    • 缺乏内存访问控制、内存利用率低
    • 采用分页式内存管理(虚拟地址空间+页表)
      • 页表
        • 记录虚拟地址与物理地址之间的映射关系
        • 对内存的访问控制 -> 在页表中,可以将一个虚拟内存地址标为只读的

5. 进程调度

操作系统切换调度进程(PCB)运行在cpu上,若存在很多进程,则依次运行,但是进程具有优先级。

  • CPU O(1)调度算法 (空间换时间)
    • 若使用双向链表来进行管理,需要将进程遍历一遍,找出优先级最高的进行调度,效率过低。
    • 空间换时间,有n个优先级,创建n个队列,组成队列数组。若优先级为n,加入到第n个队列。之后找优先级为0的队列,下标越小,越优先被调度。
    • 由于仍然需要判断每个队列是否为空,为了提高效率,使用位图。再设置n个比特位,0代表队列为空;1代表队列非空,表示有进程需要被调度。
    • 进程调度完成后,需要出队。再创建n个队列,出队后放入新队列。队列全部出完后,可调度队列不存在。新的队列可认为是一个过期队列。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值