【Linux第五课-进程上】PCB内部属性、标识符、进程状态、Linux下的进程状态、进程的优先级、Linux进程的调度与切换

news/2024/9/27 21:29:33 标签: linux, 运维, 服务器

目录

  • 体系结构 -- 硬件上
  • 操作系统 -- 软件上
  • 进程
    • PCB内部属性
      • 1、在linux里面看程序
      • 2、标识符
        • 获取程序的标识符
        • 父进程标识符 PPID
        • 查看进程的另一种方法
        • 通过系统调用创建进程 - fork
          • 杀掉一个进程
          • for循环创建多个代码
      • 3、进程状态
        • 进程排队 - 队列
        • 教程上关于进程状态表述
          • 运行
          • 阻塞
          • (阻塞)挂起
        • Linux下的具有进程状态
        • R(running)运行状态
        • S(sleep)睡眠状态
        • D(Disk sleep)磁盘休眠状态
        • t(stopped)暂停状态
        • T(tracing stop)暂停状态
        • Z(zombie)僵尸状态
          • 孤儿进程
        • X(dead)死亡状态
        • 信号
    • 进程的优先级
      • 是什么?
      • 为什么存在优先级
      • 调整优先级
    • Linux的调度与切换
      • 概念
      • 进程切换
      • 进程调度
    • PCB外部,整体使用

体系结构 – 硬件上

1、冯诺依曼体系结构
在这里插入图片描述
独立
CPU:运算器(算数和逻辑运算)+控制器(协调CPU内部之间 信息流动)
输入设备:话筒、摄像头、键盘、鼠标、磁盘、网卡等
输出设备:声卡、显卡、网卡、磁盘、显示器、打印机等
有的设备只做输入,只做输出,有的即做输入又做输出
存储器:内存(掉电易使)
设备是链接的:总线(主板)链接起来
链接不是目的,是手段。目的是设备之间的数据流动(本质:设备之间会进行数据的来回拷贝)

操作系统 – 软件上

进行软硬件管理的软件
在这里插入图片描述

什么时候读、什么时候写、都多少、写多少是操作系统管的
对任何事务做管理:先描述,在管理

操作系统给用户提供一系列的系统调用接口对操作系统内部的数据进行访问,不允许用户直接访问操作系统内部数据

系统调用接口,就是C语言设计的函数,系统调用函数(函数就有输入和输出)
操作系统给用户提供:数据方面的支持、功能方面的支持

进程

教材观点:
加载到内存的程序 – 进程
正在运行的程序

事实
(1)我们可以启动多个程序(每个程序都有一个exe) — 我们将多个exe加载到内存
(2)操作系统需要管理多个加载到内存的程序
(3)操作系统如何管理加载到内存的程序?先描述,在组织
一个加载到内存的程序我们称为进程

struct xxx
{
	//状态
	//优先级
	//内存指针字段
	//标识符
	//……包含进程几乎所有的属性字段
	struct xxx* next;
}

上述的描述进程的结构体,我们称为PCB(process ctrl block)进程控制块

struct PCB
{
	//状态
	//优先级
	//内存指针字段
	//标识符
	//……包含进程几乎所有的属性字段
	struct PCB* next;
}

对进程的管理转换成对PCB对象的管理,也就是对链表的增删查改

进程 = 内核PCB对象(内核数据结构) + 可执行程序

PCB,操作系统学科的叫法
具体的linux,PCB称为task_struct,操作系统内部的数据

PCB内部属性

1、标识符
2、状态
3、优先级
4、程序计数器:程序中即将要被执行的指令的地址
5、内存指针:找到代码和数据的指针
6、I/O状态信息:
7、记账信息

linux_71">1、在linux里面看程序

创建myprocess.c
在这里插入图片描述
创建Makefile
在这里插入图片描述
运行程序

第一条程序是myprocess进程
第二条程序是grep本身是一个指令,也就是一个程序,它是包含myprocess
几乎所有的独立的指令,就是程序,运行起来也要变成进程
在这里插入图片描述
看进程结束的过程

while :; do ps ajx | head -1 && ps ajx | grep myprocess | grep -v grep; sleep 1; done

在这里插入图片描述

1、只有打印到显示屏上的内容才能被重定位和追加重定位
(1) '>':重定向
(2)‘>>’:追加重定向
blog.csdnimg.cn/direct/91879d6b99a14e19b5761449f2343282.png)
在这里插入图片描述
2、‘echo’:后面跟着string直接打印string,后面跟着$sht则打印变量sht的值
3、'|':表示管道,上一条命令的输出,作为下一条命令参数进行传递,如:ls | grep “aa”,在ls的输出中查找aa字符串。
4、'head':打印一个文件的前多少行
(1)head:默认打印前十行
(2)head -n:打印前n行
5、&&:表示前一条命令执行成功时,才执行后一条命令 ,如 echo '1‘ && echo ‘2’
在这里插入图片描述
6、grep
(1)grep xxx:过滤出来包含xxx的
(2)grep -v grep:反向过滤,不把含义grep的过滤出来
在这里插入图片描述

2、标识符

PID就是标识符

ps axj

COMMAND代表你这个进程在启动的时候对应的可执行程序是谁
在这里插入图片描述

获取程序的标识符

函数调用

getpid

man getpid
在这里插入图片描述

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 
  5 int main()
  6 {
  7     pid_t id = getpid();
  8     printf("%d\n", id);
  9     printf("hello zyh\n");
 10     return 0;                                                                 
 11 }

在这里插入图片描述

在Linux中,普通进程,一般都有父进程

父进程标识符 PPID
getppid

在这里插入图片描述

  1 #include<stdio.h>                                                             
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 
  5 int main()
  6 {
  7     pid_t id = getpid();
  8     pid_t fid = getppid();
  9     printf("id:%d\n", id);
 10     printf("fid:%d\n", fid);
 11     printf("hello zyh\n");
 12     return 0;
 13 }

每一次启动进程的pid都会变化,因为我们每次启动的都是一个新进程

其中bash就是命令行解释器
在这里插入图片描述

查看进程的另一种方法

ls
-l 参数 以详细格式列表
-d 参数 仅列当前目录本身

通过/proc系统文件夹进行查看
在这里插入图片描述
当我们要查看某特定进程
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

为何删了之后还能正常运行,因为程序正在运行被加载到了内存,删除磁盘上的可执行程序没有影响
cwd是当前工作目录

fopen(“./文件名”, “w”);
如果没有此文件就在当前路径下创建
改变当前的工作目录:chdir()
在这里插入图片描述
在这里插入图片描述

当前路径:当前进程所在的路径cwd
在这里插入图片描述

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 
  5 int main()
  6 {
  7     pid_t pid = getpid();
  8     printf("pid: %d\n", pid);
  9 
 10     FILE *fp = fopen("./zyh.txt","w");
 11     if(fp == NULL) return 0;
 12     printf("zyh.txt文件创建成功!\n");                                        
 13     fclose(fp);
 14     sleep(20);
 15     return 0;
 16 }

如果进程的所在路径cwd被修改,那么文件也会被创建在修改后的路径
改变当前的工作目录:chdir()
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 
  5 int main()
  6 {
  7     pid_t pid = getpid();
  8     printf("pid: %d\n", pid);
  9     sleep(20);
 10     printf("修改当前路径\n");
 11     chdir("/home/hui");                                                       
 12     printf("修改当前路径成功\n");
 13     sleep(20);
 14     FILE *fp = fopen("./zyh.txt","w");
 15     if(fp == NULL) return 0;
 16     printf("zyh.txt文件创建成功!\n");
 17     fclose(fp);
 18     sleep(20);
 19     return 0;
 20 }
通过系统调用创建进程 - fork

在这里插入图片描述
对于fork的返回:对于父进程返回创建进程的id,对于子进程返回0

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

一般而言,我们想让父子做不同的事情
pid都是大于等于0的数字

父进程先被创建了
在这里插入图片描述
此时子进程也被创建了
在这里插入图片描述
在这里插入图片描述

进程 = 内核数据结构 + 可执行程序的代码和数据
创建子进程时,是以父进程为模板的。内核数据结构大部分和父进程一样,但也有些少部分不一样(如,pid、ppid)

(1)fork为何会返回两个值为什么会这样?
把子进程的pid返回给父进程,目的是方便父进程对子进程的控制和唯一性确认。子进程不需要这些,只需要知道创建成功没成功,因此返回0就可以了(父:子 = 1:n,子进程找父进程是唯一的,父进程找子进程就需要pid)
(2)fork为何会返回两次?
如果一个函数,已经运行到了return的时候,说明这个函数的核心逻辑已经做完了
在调用fork函数的时候,运行到return这条语句之前,子进程就已经被创建,因此return id这条语句是同时被父进程和子进程执行的
在这里插入图片描述

(3)id怎么可能同一个变量,既大于0,又等于0

一个进程崩溃了,不会影响其他进程
进程(任意)之间是具有独立性的,互相不受影响。(os在设计的时候就必须保证的)

杀掉一个进程
kill -9 进程id

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

linux中可以使用同一个变量名,表示不同的内存
当代码被编译成二进制,代码中的变量名就不在了

for循环创建多个代码
  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <stdlib.h>
  4 #include <sys/types.h>
  5 const int num = 10;
  6 
  7 void work()
  8 {
  9     int x = 10;
 10     while(x > 0)
 11     {
 12         printf("I am a child process, my id: %d\n",getpid());
 13         sleep(1);
 14         --x;
 15     }
 16 }
 17 
 18 int main()
 19 {
 20     for(int i = 0; i < num; i++)
 21     {
 22         pid_t id = fork();
 23         sleep(1);
 24         if(id < 0) break;                                             
 25         else if(id == 0)
 26         {
 27             //子进程
 28             work();
 29             exit(1);
 30         }
 31         //父进程
 32         printf("creat %d child process, subid:%d\n", i, id);
 33     }
 34     sleep(20);
 35     return 0;
 36 }

正在创建子进程
在这里插入图片描述
正在杀死子进程
在这里插入图片描述

3、进程状态

进程排队 - 队列

进程排队一定是在等待某种资源

进程不是一直在运行的,进程放在CPU上,也不会一直运行 — 时间片
排队:是进程的task_struct在排队

Linux中进程pcb的排序
在这里插入图片描述

教程上关于进程状态表述

运行、阻塞、挂起
所谓的状态,就是一个整型变量,再task_struct中的一个整型变量

#define New 1
#define Ready 2
#define running 3
#define block 4

struct task_struct
{
	int status;
	……
}
运行

把进程的PCB放到一个CPU的运行队列中,我们就称为这个队列的运行状态
在这里插入图片描述

一个CPU一个运行队列

阻塞

在这里插入图片描述
当我们的进程在进行等待软硬件资源的时候,资源如果没有就绪,我们进程的task_struct只能

1、将自己设置为阻塞状态
2、将自己的pcb连入等待的资源提供的等待队列

状态的变换,就是把进程的PCB放到不同的队列中

(阻塞)挂起

在这里插入图片描述

挂起状态
前提:计算机资源(eg:内存资源)已经比较吃紧了
阻塞挂起:当进程在阻塞的状态下,资源吃紧了,操作系统就会把阻塞进程的代码和数据从内存写回到磁盘的swap分区

进程的PCB(task_struct)不会换出
创建进程时先创建对应进程的PCB(内核数据结构)

Linux下的具有进程状态

在这里插入图片描述

R(running)运行状态

并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
在这里插入图片描述

代码在运行为何显示是S,因为代码里面只有一行printf,printf导致进程大部分时间都在等待
在这里插入图片描述
把进程改成一个死循环就是R状态了
在这里插入图片描述
在这里插入图片描述

+”:是指这个进程在前台,运行此进程时,无法执行其他命令
./myprocess &后台进程
(1)后台进程ctrl+c无法杀掉,只能kill -9 进程id

前台进程
在这里插入图片描述
后台进程
在这里插入图片描述

S(sleep)睡眠状态

意味着进程在等待事件完成,也就是一种阻塞状态,Linux中也称为浅度睡眠

ctrl+c可以结束进程

在这里插入图片描述

D(Disk sleep)磁盘休眠状态

称为不可中断睡眠,又称深度睡眠D状态也是阻塞状态,有些进程执行的内容很重要,操作系统不能把它杀死

t(stopped)暂停状态

让进程处于暂停状态,当某些进程操作比较危险,操作系统会将其暂停。也是阻塞状态
(1)程序开始运行,创建了进程此时进程是前台进程
在这里插入图片描述
(2)kill -9 进程id对进程进行暂停
在这里插入图片描述
(3)kill -19 进程id对进程进行启动,再次启动的进程有前台进程变为后台进程

在这里插入图片描述

T(tracing stop)暂停状态

进程处于被追踪状态,等待gdb给的控制命令,gdb不让进程跑了。也是阻塞状态
(1)在生成可执行程序的时候注意加-g
在这里插入图片描述
(2)进入gdb; l + 行号:从第几行开始显示代码
在这里插入图片描述
(3)p:打断点; r:运行到断点
此时的进程就是t状态,等待gdb的下一步指令
在这里插入图片描述

Z(zombie)僵尸状态

也是终止状态,当进程完成时或发生意外,代码和数据可以释放,但进程的PCB不能被释放,父进程或操作系统还需要去读其退出信息

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

int main()
{
	pid_t id = fork();
	//子进程
	if(id == 0)
	{
		int cnt = 5;
		while(cnt)
		{
			printf("I am child, pid: %d, ppid: %d\n", getpid(), getppid());
			sleep(1);
			cnt--;
		}
		exit(0); //子进程退出
	}
	//只有父进程,父进程一直死循环就不会读取子进程的退出信息,子进程就处于僵尸状态
	while(1)
	{
		printf("I am father, pid: %d, ppid: %d\n", getpid(), getppid());
		sleep(1);
	}
	return 0;
}

在这里插入图片描述

为何会有Z状态?
创建进程是希望这个进程给用户完成工作的,子进程必须有结果数据,PCB中的。
什么是Z?
进程已经退出,但当前进程的状态需要自己维持住,供上层读取,必须处于Z
如果父进程就不读取?
僵尸状态会一直存在,task_struct对象也要一直存在,都是要占据内存的。内存泄漏
最初创的进程的父进程是bash,会自动回收退出信息,如果在自己创建的进程下面再fork一个子进程,此时子进程的父进程不会自动回收退出信息

bash进程会自动读取其下面子进程的退出信息,但这里创建的father进程不会自动读出子进程的退出信息
在这里插入图片描述
在这里插入图片描述

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
	pid_t id = fork();
	//子进程
	if(id == 0)
	{
		int cnt = 5;
		while(cnt)
		{
			printf("I am child, pid: %d, ppid: %d\n", getpid(), getppid());
			sleep(1);
			cnt--;
		}
		exit(0); //子进程退出
	}
	//只有父进程,父进程一直死循环就不会读取子进程的退出信息,子进程就处于僵尸状态
	int cnt = 10;
	while(cnt)
	{
		printf("I am father, pid: %d, ppid: %d\n", getpid(), getppid());
		sleep(1);
		cnt--;
	}

	//父进程读取子进程的退出信息
	wait(NULL);
	printf("father wait child done...\n");

	sleep(5);

孤儿进程

一个进程的父进程先退出了,那该进程会被1号进程领养,1号进程是操作系统

一个进程被领养后,不仅变成孤儿进程,还会变成后台进程

在这里插入图片描述

    pid_t id = fork();
	//子进程
	if(id == 0)
	{
		while(1)
		{
			printf("I am child, pid: %d, ppid: %d\n", getpid(), getppid());
			sleep(1);
			cnt--;
		}
		//exit(0); //子进程退出
	}
	//只有父进程,父进程一直死循环就不会读取子进程的退出信息,子进程就处于僵尸状态
	int cnt = 10;
	while(cnt)
	{
		printf("I am father, pid: %d, ppid: %d\n", getpid(), getppid());
		sleep(1);
		cnt--;
	}

X(dead)死亡状态

终止状态,是一个瞬时状态很难查到

信号
kill -l

9:SIGKILL
19:SIGSTOP
18:SIGCONT
在这里插入图片描述

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

进程的优先级

是什么?

前提:进程要访问某种资源,进程通过一定的方式(排队),确认享受资源的先后顺序

优先级和权限的区别
优先级:你先还是我先的问题
权限:能不能的问题

为什么存在优先级

资源过少,相对的概念
PRI (Priority):进程的优先级

linux的优先级在进程运行中或运行前是可以被修改的,优先级范围[60,99]–>40
nice值为**[-20, 19]**
数字越小优先级越高

在这里插入图片描述

调整优先级

1、top进入任务管理器
2、r(renice)-> 输入对于pid -> 要修改的nice -> q退出
输入r之后的界面
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

Linux系统允许用户调整优先级,但是不能直接让修改pri,而是修改nice值
nice:进程优先级的修正数据[-20, 19]
pri = pri(old) + nice pro(old)每次都是80

为何优先级调整在60-99之间
如果不加限制,将自己进程的优先级调整的非常高,别人的优先级调成非常低
优先级较高的进程,优先得到资源,后面还有源源不断的进程产生。常规进程很难享受到CPU资源 — 进程饥饿

Linux的调度与切换

概念

1、进程在运行的时候,放在CPU上,直接必须把进程跑完,才行吗?
不行,现在操作系统都是基于时间片进行轮转执行的
2、
竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级
独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发

进程切换

进程在运行时,会产生大量的临时数据放到CPU的寄存器中

所有的保存都是为了最终的恢复,所有的恢复,都是为了继续上一次的运行位置继续运行。可以认为寄存器里的数据保存在PCB保护上下文
虽然寄存器数据放在一个共享的CPU里面,但是所有的数据,其实都是被进程私有的

CPU内部的所有临时数据,称作进程的硬件上下文
CPU内的寄存器只有一套,寄存器内保存的数据可以有多套

进程调度

Linux实现进程调度的算法,考虑优先级、饥饿、效率

分时操作系统:公平,每个进程在一定时间段内享受到资源。即便有优先级,大家优先级也不明显。eg:互联网买东西,大家优先级都一样
实时操作系统:进程跑起来必须跑完才能跑下一个,必须严格按照顺序跑,有更高优先级的进程允许插队。 eg:做车载系统,一脚油门下去必须立马反应,不能基于时间片慢慢跑

0-99不考虑实时优先级
100-139:这四十个对应着进程的优先级**[60,99]**
在这里插入图片描述

每次从头查数组各个位置是否为空还是很麻烦的int bitmap[5]
32*5=160
比特位的位置,表示哪个队列
比特位的内容,表示该队列是否为空
检测那个队列中是否有进程,检测对应的比特位是否为1

如果一直有高优先级的进程排队就会产生饥饿问题,解决方法:过期进程和活跃进程
CPU从活跃队列选择进程,新来的进程在过期队列
在这里插入图片描述

struct q* activestruct q* expired
CPU只会去活跃队列里面拿进程,当进程运行时间片结束或运行结束或新建进程,操作系统会将其放入到过期队列

struct q* active = &array[0]
struct q* expired = & array[1]

在这里插入图片描述

活跃队列里面的进程不断增多,过期队列中的进程不断减少,对后只需要对active和expired进行交换指针的内容即可

swap(&active, &expired)

PCB外部,整体使用


http://www.niftyadmin.cn/n/5679594.html

相关文章

tomcat 文件上传 (CVE-2017-12615)

漏洞描述&#xff1a; 当 Tomcat 运行在 Windows 主机上&#xff0c;且启用了 HTTP PUT 请求方法 影响范围&#xff1a; Apache Tomcat 7.0.0 - 7.0.79 漏洞复现&#xff1a; 创建vulfocus靶场容器 poc #CVE-2017-12615 POC import requests import optparse import ospar…

服务器的地址如何伪装起来

要伪装服务器的地址&#xff0c;可以通过多种方式来隐藏或改变其实际IP地址&#xff0c;使得外部无法轻易知道服务器的真实位置。这些方法通常用于保护隐私、提高安全性、绕过地理限制或防止攻击。以下是几种常见的伪装服务器地址的方法&#xff1a; 1. 使用代理服务器 概念&…

字节豆包C++一面-面经总结

talk is cheap show me the code lc206&#xff1a;链表反转&#xff1a;给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 class Solution { public:ListNode* reverseList(ListNode* head) {if(headnullptr||!head->next)return head…

Unity角色控制及Animator动画切换如走跑跳攻击

Unity角色控制及 Animator动画切换如走跑跳攻击 目录 Unity角色控制及 一、 概念 1、角色控制 1) CharacterController(角色控制器) 2) CapsuleCollider + Rigidbody(使用物理刚体控制) 2、角色动画-Animation、Animator 1) 旧版动画系统

Efficient DETR: Improving End-to-End Object Detector with Dense Prior

原文链接 [2104.01318] Efficient DETR: Improving End-to-End Object Detector with Dense Prior (arxiv.org)https://arxiv.org/abs/2104.01318 原文笔记 What 1、一种针对DETR的objectquery初始化的方法 2、针对Deformable DETR进行改进&#xff0c;改进之后的模型具有…

只能说是未来可期(AI进阶篇:FLUX-5 ControlNetIP-Adapter)

大家好我是安琪&#xff01;&#xff01;&#xff01; 大部分有了一定AI绘画基础的小伙伴在AI创作的过程中应该都离不开一个东西&#xff0c;那就是ControlNet。 ControlNet一度被视为是AI绘图模型在可控性上的终极解决方案&#xff0c;要知道AI绘画在早期最令人诟病的地方就…

创新型相亲交友平台:怎么吸引年轻用户

在当今这个数字化的时代&#xff0c;相亲交友平台也在不断地进化和发展&#xff0c;以适应年轻一代的社交需求。年轻人追求个性化的体验、高效的沟通以及安全的环境。为了更好地吸引这些用户群体&#xff0c;相亲交友平台需要在技术和服务上不断创新。作者h17711347205本文将探…

npm依赖安装的时候vue版本号报错处理

以下报错显示vue版本不对&#xff0c;需要改成这个版本"vue": "2.6.14"对应的版本 先看一下package.json中vue版本是多少 解决&#xff1a; npm install vue2.6.14