当前位置: 首页 > news >正文

Linux——进程概念

目录

  • 一、系统调用和库函数概念
  • 二、基本概念
  • 三、描述进程-PCB
    • 3.1 task_struct-PCB的一种
    • 3.2 task_ struct内容分类
  • 四、组织进程
  • 五、查看进程
  • 六、通过系统调用获取进程标示符
  • 七、通过系统调用创建进程- fork初始
    • 7.1 fork函数创建子进程
    • 7.2 fork 之后通常要用 if 进行分流
  • 八、进程状态
    • 8.1 课本给出的图(引入)
    • 8.1.1运行状态
    • 8.1.2 阻塞状态
    • 8.2 Linux进程状态
      • 8.2.1 运行状态-R
      • 8.2.2 浅度睡眠状态-S
      • 8.2.3 深度睡眠状态-D
      • 8.2.4 暂停状态-T
      • 8.2.5 死亡状态-X
      • 8.2.6 僵尸状态-Z
    • 8.3 僵尸进程
      • 8.3.1僵尸进程的危害
    • 8.4 孤儿进程
  • 九、进程优先级
    • 9.1 基本概念
    • 9.2 查看系统进程
    • 9.3 PRI与NI
    • 9.4 查看进程优先级信息
    • 9.5 通过top命令更改进程的nice值
    • 9.6 renice命令更改进程的nice值
    • 9.7 四个重要概念
  • 十、环境变量
    • 10.1 基本概念
    • 10.2 常见环境变量
    • 10.3 查看环境变量方法
      • 10.3.1 测试PATH
      • 10.3.2 测试HOME
      • 10.3.3 测试SHELL
    • 10.4 和环境变量相关的命令
    • 10.5 环境变量的组织方式
    • 10.6 通过代码如何获取环境变量
    • 10.7 通过系统调用获取环境变量
    • 10.8 环境变量通常是具有全局属性的
  • 十一、进程地址空间
    • 11.1 地址空间是什么?如何理解地址空间的区域划分?
    • 11.2 为什么要有进程地址空间?
  • 十二、inux2.6内核进程调度队列
    • 12.1 一个CPU拥有一个runqueue
    • 12.2 优先级
    • 12.3 活动队列
    • 12.4 过期队列
    • 12.5 active指针和expired指针


一、系统调用和库函数概念

  • 在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用。
  • 系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。

二、基本概念

  • 课本概念:程序的一个执行实例,正在执行的程序等
  • 内核观点:担当分配系统资源(CPU时间,内存)的实体

对进程初步的概念
一个已经加载到内存的程序(就是冯诺依曼体系),叫做进程
正在运行的程序,叫做进程

理解
按照冯诺依曼体系结构
在这里插入图片描述

你向朋友认识介绍你的室友,你会讲:他的姓名、年龄、身高、爱好等等。

  • 描述的都是属性,其实生活中描述任何一件事,一个人都是从他的属性描述,当属性够多,这一堆属性的集合,就是目标对象。

在这里插入图片描述

  • 根据进程的PCB类型,为该进程创建对应的PCB对象

1.比如你高考完后你所报考的大学去你的高中把你的大学的档案转到你所报考的大学,而你还在放暑假中,你的数据信息已经先过去了。2.开学时你进了学校,学校有你的个人基本信息,也有你个人的存在,那么你就是这所学校的一名学生。

PCB就是你的个人基本信息,而你就是这个磁盘中的文件

在这里插入图片描述

如你学校(操作系统)的一名保安他是不是你学校的学生呢?当然不是,他个人(文件)虽然在这所学校,但学生管理系统中并没有保安(文件)的个人基本信息,那么校长的这份管理表并不会对保安做管理。

  • 所以光把一个可执行程序的二进制代码加载到内存就如同在学校的保安一样并不是这个学校的学生

描述进程的PCB结构体和该进程对应的代码和数据合起来才是进程PCB是由操作系统内部维护的,代码和数据是由我们写的,当加载进程时本质上不仅仅是代码和数据加载到内存了而且操作系统还根据操作系统内描述进程的控制块为当前的进程创建对应的PCB,把该进程的相关属性填充好并初始化形成一个PCB,这个结构体对象、结构体变量,是由操作系统自己形成的。

所以我们这里对进程的理解:
进程 = 内核PCB数据结构对象 + 我们自己的代码和数据
在这里插入图片描述
所谓的对进程做管理本质是对PCB做管理,对我们写的代码和数据(二进制文件)压根就不管
那么进程所对应我们的代码和数据在内存的什么位置?怎么找到代码和数据?
PCB属性中包含我们对应的相关指针信息,通过指针找到进程所对应的代码和数据。

当然操作系统中不止一个进程,那如果是多个进程呢?
在这里插入图片描述到现在我们对进程的理解:

  • 进程 = 内核PCB数据结构对象 + 我们自己的代码和数据
  • 操作系统管理的是进程在内核中创建的PCB对象,并不是我们写的代码数据,将PCB对象用链表全部连接起来,转换成了对链表的增删查改

三、描述进程-PCB

  • 进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合
  • 课本上称之为PCB(process control block),Linux操作系统下的PCB是: task_struct。

3.1 task_struct-PCB的一种

在Linux中描述进程的结构体叫做task_struct。
task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。

3.2 task_ struct内容分类

  1. 标示符: 描述本进程的唯一标示符,用来区别其他进程。
  2. 状态: 任务状态,退出代码,退出信号等。
  3. 优先级: 相对于其他进程的优先级。
  4. 程序计数器: 程序中即将被执行的下一条指令的地址。
  5. 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。
  6. 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
  7. I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
  8. 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
  9. 其他信息

四、组织进程

可以在内核源代码里找到它。所有运行在系统里的进程都以task_struct链表的形式存在内核里。
Linux内核中,最基本的组织进程task_struct的方式是采用双链表组织的。
PCB不仅仅属于某个双链表

五、查看进程

进程的信息可以通过 /proc 系统文件夹查看
系统当中动态运行的所有进程相关信息
在这里插入图片描述
每一个进程在系统运行期间终止在启动PID是变化的
在这里插入图片描述如果不想显示grep
在这里插入图片描述如果想终止一个进程
在这里插入图片描述

kill -9 pid:杀掉一个进程(也是终止一个进程)
为什么是-9呢?
使用kill命令可以列出当前系统所支持的信号集

在这里插入图片描述

查看PID唯一标识符对应的目录属性
在这里插入图片描述主要看cwd和exe
在这里插入图片描述

六、通过系统调用获取进程标示符

查看指定pid的进程文件:

如果想只查看这个目录我们可以:

[ling@iZ2zefqbzvwrp9dqb7s3h5Z lesson]$ ls /proc/

通过使用系统调用函数,getpid和getppid即可分别获取进程的PID和PPID。
在这里插入图片描述
当运行该代码生成的可执行程序后,循环打印该进程的pid和ppid。
在这里插入图片描述通过ps命令循环打印查看该进程的信息,即可发现通过ps命令得到的进程的pid和ppid与使用系统调用函数getpid和getppid所获取的值相同。
在这里插入图片描述

七、通过系统调用创建进程- fork初始

7.1 fork函数创建子进程

  • fork是一个系统调用级别的函数,其功能就是创建一个子进程。
  • fork有两个返回值

如下:

在这里插入图片描述
实际上,使用fork函数创建子进程,在fork函数被调用之前,代码被父进程执行,而fork函数之后的代码,则默认情况下父子进程都可以执行。

  • 父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)

在这里插入图片描述

1.为什么fork给子进程返回0,给父进程返回子进程的pid?
因为返回不同的返回值,是为了区分让不同的执行流,执行不同的代码块。父进程是唯一的,子进程可以有很多个,父进程要拿到子进程的pid用来标记子进程的唯一性,而子进程调用getpid直接就能获取进程的pid

2.怎么理解一个函数返回两次?
在这里插入图片描述

fork是个函数且有返回值,由于代码共享执行return语句时父子进程各执行一次,这样fork函数就有两次返回值了。

2.fork代码层面

7.2 fork 之后通常要用 if 进行分流

当调用fork后,创建子进程,后续的代码是共享的,父和子进程各自return形成两次返回,两次返回变量在接收时发生写时拷贝让父子进程在接收变量时发生不同的值,所以后续可以使用对接收的值进行if判断对父子进程进行分流,让父子进程执行不同的代码块

八、进程状态

8.1 课本给出的图(引入)

在这里插入图片描述就绪状态:进程已经准备好执行,但尚未被CPU分配时间片。处于就绪状态的进程在等待被调度器选中以便获得CPU资源。

运行状态:进程正在CPU上执行其指令。一个进程只有在运行状态下才能进行实际的工作。

阻塞状态:进程因为等待某些事件的发生(如I/O操作完成、获取锁等)而无法继续执行。处于阻塞状态的进程不会占用CPU资源。

8.1.1运行状态

所有处于运行状态,即可被调度的进程,都被放到运行队列当中,当操作系统需要切换进程运行时,就直接在运行队列中选取进程运行
在这里插入图片描述为了避免CPU被一个进程一直占用,例(死循环)。
每一个进程都有一个叫:时间片的概念:每个进程最多在CPU待XXX秒。
所以我们就看到了在一个时间段内,所有进程代码都会被执行——并发执行。
又有大量的把进程从CPU放上去,拿下来的动作——进程切换。

8.1.2 阻塞状态

在这里插入图片描述

8.2 Linux进程状态

一个进程从创建而产生至撤销而消亡的整个生命期间,有时占有处理器执行,有时虽可运行但分不到处理器,有时虽有空闲处理器但因等待某个时间的发生而无法执行,这一切都说明进程和程序不相同,进程是活动的且有状态变化的,于是就有了进程状态这一概念。

Linux操作系统kernel源代码当中对于进程状态有如下定义:

```
/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char *task_state_array[] = {
"R (running)",       /*  0*/
"S (sleeping)",      /*  1*/
"D (disk sleep)",    /*  2*/
"T (stopped)",       /*  4*/
"T (tracing stop)",  /*  8*/
"Z (zombie)",        /* 16*/
"X (dead)"           /* 32*/
};
```

进程的当前状态是保存到自己的进程控制块(PCB)当中的,在Linux操作系统当中也就是保存在task_struct当中的。
在Linux操作系统当中我们可以通过 ps aux 或 ps axj 命令查看进程的状态。

[ling@iZ2zefqbzvwrp9dqb7s3h5Z ~]$ ps aux

在这里插入图片描述

[ling@iZ2zefqbzvwrp9dqb7s3h5Z ~]$ ps axj    

在这里插入图片描述

8.2.1 运行状态-R

一个进程处于运行状态(running),并不意味着进程一定处于运行当中,运行状态表明一个进程要么在运行中,要么在运行队列里。也就是说,可以同时存在多个R状态的进程。

在这里插入图片描述可见虽然程序一直在运行还是S状态这是因为对于执行该一个进程来说CPU太快了外设太慢,处于CPU大多时间在等待外设(这里也就是我们的显示器)

如下设为死循环该进程一直占用着CPU,就为R状态了
在这里插入图片描述

R+:在前台运行
R:在后台运行

8.2.2 浅度睡眠状态-S

一个进程处于浅度睡眠状态(sleeping),意味着该进程正在等待某件事情的完成,处于浅度睡眠状态的进程随时可以被唤醒,也可以被杀掉(这里的睡眠有时候也可叫做可中断睡眠(interruptible sleep))。

在这里插入图片描述或者scanf函数,等待键盘输入数据,一直都处于等待状态也就是浅度睡眠。

8.2.3 深度睡眠状态-D

一个进程处于深度睡眠状态(disk sleep),表示该进程不会被杀掉,即便是操作系统也不行,只有该进程自动唤醒才可以恢复。该状态有时候也叫不可中断睡眠状态(uninterruptible sleep),处于这个状态的进程通常会等待IO的结束。

  • 不响应任何请求

例:某个进程对磁盘进行写入操作,那么在磁盘进行写入的期间,该进程处于深度睡眠,虽然该进程不是R状态操作系统也无法将该进程杀掉,因为改进等待磁盘的回复(写入是否成功)以做出对进程相应回复,进程在反馈给用户是否写入成功。

8.2.4 暂停状态-T

在Linux当中,我们可以通过发送SIGSTOP信号使进程进入暂停状态(stopped),发送SIGCONT信号可以让处于暂停状态的进程继续运行。

例如,我们对一个进程发送SIGSTOP信号,该进程就进入到了暂停状态。
在这里插入图片描述
这时候可以 kill -18 pid让它继续运行

8.2.5 死亡状态-X

死亡状态只是一个返回状态,当一个进程的退出信息被读取后,该进程所申请的资源就会立即被释放,该进程也就不存在了,所以你不会在任务列表当中看到死亡状态(dead)。

8.2.6 僵尸状态-Z

当一个进程将要退出的时候,在系统层面,该进程曾经申请的资源并不是立即被释放,而是要暂时存储一段时间,以供操作系统或是其父进程进行读取,如果退出信息一直未被读取,则相关数据是不会被释放掉的,一个进程若是正在等待其退出信息被读取,那么我们称该进程处于僵尸状态(zombie)。

简单的说就是子进程完成了自己的任务或是因异常而死掉,该子进程并不能直接结束释放资源,操作系统会保留该进程的状态等待父进程读取,等待期间就是僵尸状态。

例如,我们写代码时都在主函数最后返回0。
在这里插入图片描述
实际上这个0就是返回给操作系统的,告诉操作系统代码顺利执行结束。在Linux操作系统当中,我们可以通过使用echo $?命令获取最近一次进程退出时的退出码。
在这里插入图片描述

  • 进程退出的信息(例如退出码),是暂时被保存在其进程控制块当中的,在Linux操作系统中也就是保存在该进程的task_struct当中。

8.3 僵尸进程

相反父进程先退出
子进程的pid为1,1就是操作系统

一个进程若是正在等待其退出信息被读取,那么我们称该进程处于僵尸状态。而处于僵尸状态的进程,我们就称之为僵尸进程。
如下:子进程完成自己的任务进入Z状态
在这里插入图片描述

在这里插入图片描述
子进程一般退出时,如果父进程没有主动回收,内存资源一直被占用,造成内存泄漏
子进程信息会一直让自己处于Z状态
进程的相关信息资源尤其stack_struct结构体不能被释放

8.3.1僵尸进程的危害

  1. 僵尸进程的退出状态必须一直维持下去,因为它要告诉其父进程相应的退出信息。可是父进程一直不读取,那么子进程也就一直处于僵尸状态。
  2. 僵尸进程的退出信息被保存在task_struct(PCB)中,僵尸状态一直不退出,那么PCB就一直需要进行维护。
  3. 若是一个父进程创建了很多子进程,但都不进行回收,那么就会造成资源浪费,因为数据结构对象本身就要占用内存。
  4. 僵尸进程申请的资源无法进行回收,那么僵尸进程越多,实际可用的资源就越少,也就是说,僵尸进程会导致内存泄漏。

8.4 孤儿进程

在Linux当中的进程关系大多数是父子关系,若子进程先退出而父进程没有对子进程的退出信息进行读取,那么我们称该进程为僵尸进程。但若是父进程先退出,那么将来子进程进入僵尸状态时就没有父进程对其进行处理,此时该子进程就称之为孤儿进程。
若是一直不处理孤儿进程的退出信息,那么孤儿进程就会一直占用资源,此时就会造成内存泄漏。因此,当出现孤儿进程的时候,孤儿进程会被1号init进程领养,此后当孤儿进程进入僵尸状态时就由int进程进行处理回收。

如:父进程先退出

在这里插入图片描述

在这里插入图片描述

九、进程优先级

9.1 基本概念

优先级实际上就是获取某种资源的先后顺序,而进程优先级实际上就是进程获取CPU资源分配的先后顺序,就是指进程的优先权(priority),优先权高的进程有优先执行的权力。

优先级存在的原因?
优先级存在的主要原因就是资源是有限的,而存在进程优先级的主要原因就是CPU资源是有限的,一个CPU一次只能跑一个进程,而进程是可以有多个的,所以需要存在进程优先级,来确定进程获取CPU资源的先后顺序。

9.2 查看系统进程

在Linux或者Unix操作系统中,用ps -l命令会类似输出以下几个内容:

```powershell
[ling@iZ2zefqbzvwrp9dqb7s3h5Z test12]$ ps -l
```

在这里插入图片描述列出的信息当中有几个重要的信息,如下:

  • UID:代表执行者的身份。用户id
  • PID:代表这个进程的代号。
  • PPID:代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号。
  • PRI:代表这个进程可被执行的优先级,其值越小越早被执行。
  • NI:代表这个进程的nice值。这个进程优先级的修正数据。

查看用户id
显示账号名称和编号
在这里插入图片描述

9.3 PRI与NI

  • PRI代表进程的优先级(priority),通俗点说就是进程被CPU执行的先后顺序,该值越小进程的优先级别越高。
  • NI代表的是nice值,其表示进程可被执行的优先级的修正数值。
  • PRI值越小越快被执行,当加入nice值后,将会使得PRI变为:PRI(new) = PRI(old) + NI。
  • 若NI值为负值,那么该进程的PRI将变小,即其优先级会变高。
  • 调整进程优先级,在Linux下,就是调整进程的nice值。
  • NI的取值范围是-20至19,一共40个级别。

注意: 在Linux操作系统当中,PRI(old)默认为80,即PRI = 80 + NI。

9.4 查看进程优先级信息

当我们创建一个进程后,可以使用ps -al命令查看该进程优先级的信息。

[ling@iZ2zefqbzvwrp9dqb7s3h5Z test12]$ ps -al

在这里插入图片描述

注意: 在Linux操作系统中,初始进程一般优先级PRI默认为80,NI默认为0。

9.5 通过top命令更改进程的nice值

top命令就相当于Windows操作系统中的任务管理器,它能够动态实时的显示系统当中进程的资源占用情况。
在这里插入图片描述
使用top命令后按“r”键,弹出输入待调整nice值的进程的PID。
在这里插入图片描述
输入进程PID并回车后,弹出提示输入调整后的nice值。
在这里插入图片描述
输入nice值按q退出,这里输入10,ps命令查看进程的优先级信息,即可发现进程的NI变成了10,PRI变成了90(80+NI)。
在这里插入图片描述注意: 若是想将NI值调为负值,也就是将进程的优先级调高,需要使用sudo命令提升权限。

9.6 renice命令更改进程的nice值

使用renice命令,后面跟上更改后的nice值和进程的PID即可。
在这里插入图片描述输入完后,用ps命令查看进程的优先级信息,也可以发现进程的NI变成了15,PRI变成了95(80+NI)。
在这里插入图片描述使用renice命令将NI值调为负值,也需要使用sudo命令提升权限。

9.7 四个重要概念

  • 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便有了优先级。

  • 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰。

  • 并行: 多个进程在多个CPU下分别同时进行运行,这称之为并行。

  • 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发

前三个都比较容易理解

为了防止一个进程独占CPU,每个进程在CPU上运行是有时间限制的,每个进程在CPU最多只能运行XXX的时间,依次按队列排下去,称为时间片。
又防止一个进程的优先级较高该进程运行完在队列中总是排在靠前,所以Linux维护两个队列,一个队列是第一次待运行的进程,另一个队列是已经运行过一次,依次按队列拍下去。这称为并发也是进程的切换基于时间片轮转的调度算法

十、环境变量

10.1 基本概念

  • 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。
  • 例如,我们编写的C/C++代码,在各个目标文件进行链接的时候,从来不知道我们所链接的动静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
  • 环境变量通常具有某些特殊用途,并且在系统当中通常具有全局特性。

10.2 常见环境变量

  • PATH: 指定命令的搜索路径。(Linux指令的所搜路径)
  • HOME: 指定用户的主工作目录(即用户登录到Linux系统中的默认所处目录)。
  • SHELL: 当前Shell,它的值通常是/bin/bash。

10.3 查看环境变量方法

echo命令来查看环境变量,方式如下:

echo $NAME //NAME为待查看的环境变量名称

例:查看环境变量PATH

在这里插入图片描述

10.3.1 测试PATH

为什么执行ls命令的时候不用带./就可以执行,而我们自己生成的可执行程序必须要在前面带上./才可以执行?
在这里插入图片描述容易理解的是,要执行一个可执行程序必须要先找到它在哪里,既然不带./就可以执行ls命令,说明系统能够通过ls名称找到ls的位置,而系统是无法找到我们自己的可执行程序的,所以我们必须带上./,以此告诉系统该可执行程序位于当前目录下。

而系统就是通过环境变量PATH来找到ls命令的,查看环境变量PATH
在这里插入图片描述
如上环境变量PATH当中有多条路径,这些路径由冒号隔开,当你使用ls命令时,系统就会查看环境变量PATH,然后默认从左到右依次在各个路径当中进行查找。

而ls命令实际就位于PATH当中的某一个路径下,所以就算ls命令不带路径执行,系统也是能够找到的。

在这里插入图片描述如果让我们的指令也不加./直接使用

方法一:将可执行程序拷贝到环境变量PATH的某一路径下。
既然在未指定路径的情况下系统会根据环境变量PATH当中的路径进行查找,那我们就可以将我们的可执行程序拷贝到PATH的某一路径下,此后我们的可执行程序不带路径系统也可以找到了。

```powershell
[ling@iZ2zefqbzvwrp9dqb7s3h5Z test13]$ sudo cp proc /usr/bin
```

在这里插入图片描述

方式二:将可执行程序所在的目录导入到环境变量PATH当中。
export PATH=$PATH:mycmd程序所在路径。

将可执行程序所在的目录导入到环境变量PATH当中,这样就可以没有指定路径时系统就会来到该目录下进行查找了。
在这里插入图片描述拷贝到PATH路径后就可以不加./直接运行了
在这里插入图片描述

10.3.2 测试HOME

任何一个用户在运行系统登录时都有自己的主工作目录(家目录),环境变量HOME当中即保存的该用户的主工作目录。

用root和普通用户,分别执行 echo $HOME

  • 普通用户
    在这里插入图片描述
  • root
    在这里插入图片描述

10.3.3 测试SHELL

我们在Linux操作系统当中所输入的各种命令,实际上需要由命令行解释器进行解释,而在Linux当中有许多种命令行解释器(例如bash、sh),我们可以通过查看环境变量SHELL来知道自己当前所用的命令行解释器的种类。
在这里插入图片描述
而该命令行解释器实际上是系统当中的一条命令,当这个命令运行起来变成进程后就可以为我们进行命令行解释。
在这里插入图片描述

10.4 和环境变量相关的命令

  1. echo:显示某个环境变量的值。
  2. export:设置一个新的环境变量。
  3. 显示所有的环境变量。
    在这里插入图片描述
环境变量名称表示内容
PATH命令的搜索路径
HOME用户的主工作目录
用户的主工作目录当前Shell
HOSTNAME主机名
TERM终端类型
HISTSIZE记录历史命令的条数
SSH_TTY当前终端文件
USER当前用户
MAIL邮箱
LANG编码格式
LOGNAME登录用户名
  1. set:显示本地定义的shell变量和环境变量。
    在这里插入图片描述

  2. unset:清除环境变量。

10.5 环境变量的组织方式

环境变量是系统提供的一组name=value形式的变量,不同的环境变量有不同的用户,通常具有全局属性
在这里插入图片描述每个程序都会收到一张环境变量表,环境表是一个字符指针数组,每个指针指向一个以’\0’结尾的环境字符串,最后一个字符指针为空。

10.6 通过代码如何获取环境变量

在有些教材会看到main函数这样的写法命令行参数
int main(int argc, char *argv[])
main函数其实有三个参数,只是我们平时基本不用它们,所以一般情况下都没有写出来。

  • main函数的前两个参数意思。

在Linux操作系统下,编写以下代码,生成可执行程序并运行。
在这里插入图片描述
运行当前可执行程序带选项
在这里插入图片描述

main函数的第二个参数是一个字符指针数组,数组当中的第一个字符指针存储的是可执行程序的位置,其余字符指针存储的是所给的若干选项,最后一个字符指针为空,而main函数的第一个参数代表的就是字符指针数组当中的有效元素个数。
在这里插入图片描述编写一个简单的代码,该代码运行起来后会根据你所给选项给出不同的提示语句。在这里插入图片描述

输入选项运行结果如下:
在这里插入图片描述

  • main函数的第三个参数

main函数的第三个参数接收的实际上就是环境变量表,我们可以通过main函数的第三个参数来获取系统的环境变量。
在这里插入图片描述除了使用main函数的第三个参数来获取环境变量以外,我们还可以通过第三方变量environ来获取。
在这里插入图片描述
其结果是一样的
在这里插入图片描述
注意: libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时要用extern进行声明。

10.7 通过系统调用获取环境变量

除了通过main函数的第三个参数和第三方变量environ来获取环境变量外,我们还可以通过系统调用getenv函数来获取环境变量。
getenv函数可以根据所给环境变量名,在环境变量表当中进行搜索,并返回一个指向相应值的字符串指针。
例如,使用getenv函数获取环境变量PATH的值
在这里插入图片描述
运行结果:
在这里插入图片描述

10.8 环境变量通常是具有全局属性的

当程序变成进程启动时,要调用main函数把两张核心向量表传进来,命令行参数表和环境变量表

  • 环境变量通常具有全局属性,可以被子进程继承下去

十一、进程地址空间

在学C语言的时候,大家应该都见过这样的空间布局图
在这里插入图片描述
通过Linux操作系统中以下代码对该布局图进行验证:
在这里插入图片描述

运行结果如下,与布局图所示是吻合的:在这里插入图片描述栈是向下生长,堆是向上生长意思是创建多个栈时第一个栈总是比第二个栈的地址小,依次类推,堆则反之,地址越来越大

看一下前面的fork函数

在这里插入图片描述

fork创建一个子进程,子进程g_val的值改为200,而父进程先休眠一秒等待子进程打印完,输出g_val的值并没有改变还是全局变量的100

运行如下
在这里插入图片描述

可以看到父进程打印的全局变量g_val的值仍为之前的100,更奇怪的是在父子进程中打印的全局变量g_val的地址是一样的,也就是说父子进程在同一个地址处读出的值不同。

我们发现,父子进程,输出地址是一致的,但是变量内容不一样!能得出如下结论:

  • 变量内容不一样,所以父子进程输出的变量绝对不是同一个变量
  • 但地址值是一样的,说明该地址绝对不是物理地址
  • 在Linux地址下,这种地址叫做虚拟地址
  • 我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理OS必须负责将虚拟地址转化成物理地址
    在这里插入图片描述

虚拟地址和物理地址之间的转化由操作系统完成。

小结:一个进程要在CPU运行要经过的阶段,1.先描述PCB的属性再对应将我们的代码和数据运行,只不过前面我们学的代码和数据不够全面,其实对应的是进程地址空间。2.再组织接着展开进程地址空间阶段,由于我们的语言编成的这些代码都是虚拟地址而我们要读取到的是物理地址的内容,这个是通过页表来将所谓的虚拟地址映射到物理地址进行读取内容。而如fork这种函数产生的写时拷贝(修改值时操作系统重新在物理地址内存开辟一段空间进行修改)在我们看到同一个地址(虚拟地址)发生两个不一样的值,其实就是父子进程各自有自己的页表,其中页表中的虚拟地址相同,但两虚拟地址转换的物理地址不同,而物理地址我们是没办法查看的。

11.1 地址空间是什么?如何理解地址空间的区域划分?

地址空间引入:进程访问地址空间转换页表访问物理内存前提是当前进程正在运行。当一个进程在CPU运行时,要访问对应的内存时一定要知道该进程在内存中访问的是哪一块地址,CPU根据它的地址总线来访问对应的物理内存。
32位计算机中有32位的地址和数据总线,冯诺依曼里是一个个独立的设备,所有设备要进行通信就要用线连起来,宏观上分为三类线:地址总线、数据总线、控制总线。CPU和内存连起来的线叫系统总线,内存和外设连起来的叫IO总线,按我们32位计算机的32根的地址总线,每一根地址总线只有0、1,组合起来一共就有2^32 种,内存寻址的基本单位是字节,就注定了内存的大小:2^32 *1byte=4GB,这就是32位的计算机最多能装4GB的内存空间。
所以地址空间是地址总线排列组合形成的地址范围[0,2^32]也就是能访问物理内存的最大范围。

地址空间区域划分引入:
在这里插入图片描述比如经济学类书可以为start=90;end=120,在范围内,连续的空间中,每一个最小单位都可以有地址。

地址空间区域划分:本质是描述进程可视范围的大小,存在各种区域划分对线性地址进行start和end,地址空间内核的一个数据结构对象,类似于PCB一样,地址空间也要被操作系统管理:先描述再组织。

进程地址空间就类似于一把尺子,尺子的刻度由0x00000000到0xffffffff,尺子按照刻度被划分为各个区域,例如代码区、堆区、栈区等。而在结构体mm_struct当中,便记录了各个边界刻度,例如代码区的开始刻度与结束刻度,如下图所示:
在这里插入图片描述在结构体mm_struct当中,各个边界刻度之间的每一个刻度都代表一个虚拟地址,这些虚拟地址通过页表映射与物理内存建立联系。由于虚拟地址是由0x00000000到0xffffffff线性增长的,所以虚拟地址又叫做线性地址。

11.2 为什么要有进程地址空间?

本来进程可以直接访问物理内存现在多了进程地址空间和页表,是更麻烦吗?肯定不是的,增加进程虚拟地址空间可以让我们访问内存时,增加一个转换的过程,这个转换过程中,可以对我们寻址请求进行审查,所以一旦异常访问,直接拦截,该请求不会到达物理内存,从而保护物理内存。

每个进程被创建时,其对应的进程控制块(task_struct)和进程地址空间(mm_struct)也会随之被创建。而操作系统可以通过进程的task_struct找到其mm_struct,因为task_struct当中有一个结构体指针存储的是mm_struct的地址。
例如,父进程有自己的task_struct和mm_struct,该父进程创建的子进程也有属于其自己的task_struct和mm_struct,父子进程的进程地址空间当中的各个虚拟地址分别通过页表映射到物理内存的某个位置。
如图:
在这里插入图片描述
而当子进程刚刚被创建时,子进程和父进程的数据和代码是共享的,即父子进程的代码和数据通过页表映射到物理内存的同一块空间。只有当父进程或子进程需要修改数据时,才将父进程的数据在内存当中拷贝一份,然后再进行修改。
在这里插入图片描述这种在需要进行数据修改时再进行拷贝的技术,称为写时拷贝技术

1.为什么数据要进行写时拷贝?

进程具有独立性。多进程运行,需要独享各种资源,多进程运行期间互不干扰,不能让子进程的修改影响到父进程。

2.为什么不在创建子进程的时候就进行数据的拷贝?

子进程不一定会使用父进程的所有数据,并且在子进程不对数据进行写入的情况下,没有必要对数据进行拷贝,我们应该按需分配,在需要修改数据的时候再分配(延时分配),这样可以高效的使用内存空间。

3、代码会不会进行写时拷贝?

90%的情况下是不会的,但这并不代表代码不能进行写时拷贝,例如在进行进程替换的时候,则需要进行代码的写时拷贝。

为什么要有进程地址空间?

1、有了进程地址空间后,就不会有任何系统级别的越界问题存在了。例如进程1不会错误的访问到进程2的物理地址空间,因为你对某一地址空间进行操作之前需要先通过页表映射到物理内存,而页表只会映射属于你的物理内存。总的来说,虚拟地址和页表的配合使用,本质功能就是包含内存。
2、有了进程地址空间后,每个进程都认为看得到都是相同的空间范围,包括进程地址空间的构成和内部区域的划分顺序等都是相同的,这样一来我们在编写程序的时候就只需关注虚拟地址,而无需关注数据在物理内存当中实际的存储位置。
3、有了进程地址空间后,每个进程都认为自己在独占内存,这样能更好的完成进程的独立性以及合理使用内存空间(当实际需要使用内存空间的时候再在内存进行开辟),并能将进程调度与内存管理进行解耦或分离。

对于创建进程的现阶段理解:
一个进程的创建实际上伴随着其进程控制块(task_struct)、进程地址空间(mm_struct)以及页表的创建。
进程=内核数据结构(tack_struct && mm _struct&& 页表)+ 程序的代码和数据

十二、inux2.6内核进程调度队列

在这里插入图片描述

12.1 一个CPU拥有一个runqueue

如果有多个CPU就要考虑进程个数的父子均衡问题。

12.2 优先级

queue下标说明:

  • 普通优先级:100~139。
  • 实时优先级:0~99。

我们进程的都是普通的优先级,前面说到nice值的取值范围是-20~19,共40个级别,依次对应queue当中普通优先级的下标100 ~ 139。
注意: 实时优先级对应实时进程,实时进程是指先将一个进程执行完毕再执行下一个进程,现在基本不存在这种机器了,所以对于queue当中下标为0~99的元素我们不关心。

12.3 活动队列

时间片还没有结束的所有进程都按照优先级放在活动队列当中,其中nr_active代表总共有多少个运行状态的进程,而queue[140]数组当中的一个元素就是一个进程队列,相同优先级的进程按照FIFO规则进程排队调度。

调度过程如下:

  1. 从0下标开始遍历queue[140]。
  2. 找到第一个非空队列,该队列必定为优先级最高的队列。
  3. 拿到选中队列的第一个进程,开始运行,调度完成。
  4. 接着拿到选中队列的第二个进程进行调度,直到选中进程队列当中的所有进程都被调度。
  5. 继续向后遍历queue[140],寻找下一个非空队列。

bitmap[5]:queue数组当中一共有140个元素,即140个优先级,一共140个进程队列,为了提高查找非空队列的效率,就可以用5 × \times × 32个比特位表示队列是否为空,这样一来便可以大大提高查找效率。
小结: 在系统当中查找一个最合适调度的进程的时间复杂度是一个常数,不会随着进程增多而导致时间成本增加,我们称之为进程调度的O(1)算法。

12.4 过期队列

  1. 过期队列和活动队列的结构相同。
  2. 过期队列上放置的进程都是时间片耗尽的进程。
  3. 当活动队列上的进程被处理完毕之后,对过期队列的进程进行时间片重新计算。

12.5 active指针和expired指针

  • active指针永远指向活动队列。
  • expired指针永远指向过期队列。

由于活动队列上时间片未到期的进程会越来越少,而过期队列上的进程数量会越来越多(新创建的进程都会被放到过期队列上),那么总会出现活动队列上的全部进程的时间片都到期的情况,这时将active指针和expired指针的内容交换,就相当于让过期队列变成活动队列,活动队列变成过期队列,就相当于又具有了一批新的活动进程,如此循环进行即可。

相关文章:

Linux——进程概念

目录 一、系统调用和库函数概念二、基本概念三、描述进程-PCB3.1 task_struct-PCB的一种3.2 task_ struct内容分类 四、组织进程五、查看进程六、通过系统调用获取进程标示符七、通过系统调用创建进程- fork初始7.1 fork函数创建子进程7.2 fork 之后通常要用 if 进行分流 八、进…...

强化学习笔记(5)——PPO

PPO视频课程来源 首先理解采样期望的转换 变量x在p(x)分布下,函数f(x)的期望 等于f(x)乘以对应出现概率p(x)的累加 经过转换后变成 x在q(x)分布下,f(x)*p(x)/q(x) 的期望。 起因是:求最大化回报的期望,所以对ceta求梯度 具体举例…...

Java设计模式:行为型模式→状态模式

Java 状态模式详解 1. 定义 状态模式(State Pattern)是一种行为型设计模式,它允许对象在内部状态改变时改变其行为。状态模式通过将状态需要的行为封装在不同的状态类中,实现对象行为的动态改变。该模式的核心思想是分离不同状态…...

postgresql的用户、数据库和表

在 PostgreSQL 中,用户、数据库和表是关系型数据库系统的基本组成部分。理解这些概念对数据库管理和操作至关重要。下面是对这些概念的详细解释: 1. 用户(User) 在 PostgreSQL 中,用户(也称为 角色&#…...

什么是Rust?它有什么特点?为什么要学习Rust?

什么是Rust?它有什么特点?为什么要学习Rust? 如果你是一名编程初学者,或者已经有一些编程经验但对Rust感兴趣,那么这篇文章就是为你准备的!我们将用简单易懂的语言,带你了解Rust是什么、它有什…...

Maven(Ⅱ):依赖范围,依赖传递,依赖阻断,可选依赖

1. Maven 依赖范围 概念 依赖范围(Dependency Scope)用于控制依赖在不同构建阶段的可见性和可用性。Maven 定义了几种不同的依赖范围,每种范围都有其特定的使用场景。 常见依赖范围及用途 compile:默认的依赖范围,…...

个人c项目 java项目解释

1. 测试环境与方法 中文: 本地测试环境:可以在一台配置中等的电脑上构建一个测试环境,利用现成的大词库数据(例如英文词典或自定义数据集)来构建 Trie。使用 C 语言的编译器(例如 gcc)编译项目&…...

51单片机看门狗系统

在 STC89C52 单片机中,看门狗控制寄存器的固定地址为 0xE1。此地址由芯片厂商在硬件设计时确定,但是它在头文件中并未给出,因此在使用看门狗系统时需要声明下这个特殊功能寄存器 sfr WDT_CONTR 0xE1; 本案将用一个小灯的工作状况来展示看门…...

爬虫基础(五)爬虫基本原理

目录 一、爬虫是什么 二、爬虫过程 (1)获取网页 (2)提取信息 (3)保存数据 三、爬虫可爬的数据 四、爬虫问题 一、爬虫是什么 互联网,后面有个网字,我们可以把它看成一张蜘蛛网…...

Android 使用ExpandableListView时,需要注意哪些细节

1. 布局属性设置 尺寸属性 宽度和高度:要合理设置 android:layout_width 和 android:layout_height 属性。如果设置为 match_parent,它会填满父容器;设置为 wrap_content,则会根据内容自动调整大小。例如,若想让 Exp…...

人工智能赋能企业系统架构设计:以ERP与CRM系统为例

一、引言 1.1 研究背景与意义 在数字化时代,信息技术飞速发展,人工智能(Artificial Intelligence, AI)作为一项具有变革性的技术,正深刻地影响着各个领域。近年来,AI 在技术上取得了显著突破,…...

使用HttpClient和HttpRequest发送HTTP请求

项目中经常会用到向第三方系统发送请求来传递数据或者获得信息&#xff0c;一般用的比较多的为HttpClient 和 HttpRequest&#xff0c;这里简要总结一下 HttpClient 和 HttpRequest 的用法 一、HttpClient 1. 发送get请求 public static String get(String url, Map<Stri…...

深度解析:网站快速收录与服务器性能的关系

本文转自&#xff1a;百万收录网 原文链接&#xff1a;https://www.baiwanshoulu.com/37.html 网站快速收录与服务器性能之间存在着密切的关系。服务器作为网站运行的基础设施&#xff0c;其性能直接影响到搜索引擎对网站的抓取效率和收录速度。以下是对这一关系的深度解析&am…...

Android记事本App设计开发项目实战教程2025最新版Android Studio

平时上课录了个视频&#xff0c;从新建工程到打包Apk&#xff0c;从头做到尾&#xff0c;没有遗漏任何实现细节&#xff0c;欢迎学过Android基础的同学参加&#xff0c;如果你做过其他终端软件开发&#xff0c;也可以学习&#xff0c;快速上手Android基础开发。 Android记事本课…...

DeepSeek-R1大模型学习笔记

DeepSeek-R1模型架构设计 DeepSeek-R1基于DeepSeek-V3 base模型&#xff0c;提出了一系列训练策略&#xff0c;包括基于纯强化学习的训练&#xff08;DeepSeek-R1-Zero&#xff09;、基于多阶段的训练和冷启动&#xff08;DeepSeek-R1&#xff09;、知识蒸馏等。下面的思维导图…...

Unity游戏(Assault空对地打击)开发(4) 碰撞体和刚体的添加

前言 飞机和世界的大小关系不太对&#xff0c;我稍微缩小了一下飞机。 详细步骤 选中所有地形对象&#xff0c;如果没有圈起的部分&#xff0c;点击Add Component搜索添加。 接着选中Player对象&#xff0c;添加这两个组件&#xff0c;最好&#xff08;仅对于本项目开发&#x…...

每日一题——滑动窗口的最大值

滑动窗口的最大值 题目描述示例说明 解题思路双端队列的特点实现步骤代码实现&#xff08;C语言&#xff09;代码解析 总结 题目描述 给定一个长度为 n 的数组 num 和滑动窗口的大小 size&#xff0c;找出所有滑动窗口里数值的最大值。 例如&#xff0c;如果输入数组 {2, 3, …...

DeepSeek 的含金量还在上升

大家好啊&#xff0c;我是董董灿。 最近 DeepSeek 越来越火了。 网上有很多针对 DeepSeek 的推理测评&#xff0c;除此之外&#xff0c;也有很多人从技术的角度来探讨 DeepSeek 带给行业的影响。 比如今天就看到了一篇文章&#xff0c;探讨 DeepSeek 在使用 GPU 进行模型训练…...

list容器(详解)

list的介绍及使用&#xff08;了解&#xff0c;后边细讲&#xff09; 1.1 list的介绍&#xff08;双向循环链表&#xff09; https://cplusplus.com/reference/list/list/?kwlist&#xff08;list文档介绍&#xff09; 1. list是可以在常数范围内在任意位置进行插入和删除的序…...

FinRobot:一个使用大型语言模型的金融应用开源AI代理平台

“FinRobot: An Open-Source AI Agent Platform for Financial Applications using Large Language Models” 论文地址&#xff1a;https://arxiv.org/pdf/2405.14767 Github地址&#xff1a;https://github.com/AI4Finance-Foundation/FinRobot 摘要 在金融领域与AI社区间&a…...

【llm对话系统】大模型 Llama 源码分析之 LoRA 微调

1. 引言 微调 (Fine-tuning) 是将预训练大模型 (LLM) 应用于下游任务的常用方法。然而,直接微调大模型的所有参数通常需要大量的计算资源和内存。LoRA (Low-Rank Adaptation) 是一种高效的微调方法,它通过引入少量可训练参数,固定预训练模型的权重,从而在保持性能的同时大…...

为AI聊天工具添加一个知识系统 之86 详细设计之27 数据处理:ETL

本文要点 ETL 数据提取 作为 数据项目的起点。数据的整个三部曲--里程碑式的发展进程&#xff1a; ETL : 1分形 Type()-层次Broker / 2完形 Method() - 维度Delegate /3 整形 Class() - 容器 Agent 1变象。变象 脸谱Extractor - 缠度&#xff08;物理 皮肤缠度&#xf…...

「全网最细 + 实战源码案例」设计模式——策略模式

核心思想 策略模式&#xff08;Strategy Pattern&#xff09;是一种行为型设计模式&#xff0c;用于定义一系列算法或策略&#xff0c;将它们封装成独立的类&#xff0c;并使它们可以相互替换&#xff0c;而不影响客户端的代码&#xff0c;提高代码的可维护性和扩展性。 结构 …...

框架与代码的形状

​ 作为一个代码的设计者&#xff0c;我之前讨论过代码的形状&#xff0c;从“名字”出发&#xff0c;进行讨论。代码的形状&#xff1a;重构的方向-CSDN博客 从比喻的角度来看&#xff0c;名字似代码的血和肉&#xff0c;而框架则似代码的骨架。 猎豹和大象 在大自然中&…...

解决vscode扩展插件开发webview中的请求跨域问题

在webview中是无法发送跨域请求的&#xff0c;可以通过消息机制&#xff0c;在插件中发请求&#xff0c;然后将请求结果传递给webview 我的代码是基于vscode-webview-ui-toolkit-samples-vue来写的 webview vue组件中的代码示例 async function initData() {// 向插件发送消…...

junit5定制点

一、JUnit 5 自定义定制点是什么&#xff1f; JUnit 5 提供了强大的扩展模型&#xff08;Extension Model&#xff09;&#xff0c;允许开发者通过实现特定接口&#xff08;如 BeforeEachCallback、ParameterResolver&#xff09;自定义测试行为。这些接口称为定制点&#xff…...

基于SpringBoot的信息技术知识赛系统的设计与实现(源码+SQL脚本+LW+部署讲解等)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…...

【Rust自学】20.1. 最后的项目:单线程Web服务器

喜欢的话别忘了点赞、收藏加关注哦&#xff08;加关注即可阅读全文&#xff09;&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 20.1.1. 什么是TCP和HTTP Web 服务器涉及的两个主要协议是超文本传输​​协议(Hypertext T…...

LabVIEW涡轮诊断系统

一、项目背景与行业痛点 涡轮机械是发电厂、航空发动机、石油化工等领域的核心动力设备&#xff0c;其运行状态直接关系到生产安全与经济效益。据统计&#xff0c;涡轮故障导致的非计划停机可造成每小时数十万元的经济损失&#xff0c;且突发故障可能引发严重安全事故。传统人…...

想品客老师的第十天:类

类是一个优化js面向对象的工具 类的声明 //1、class User{}console.log(typeof User)//function//2、let Hdclass{}//其实跟1差不多class Stu{show(){}//注意这里不用加逗号&#xff0c;对象才加逗号get(){console.log(后盾人)}}let hdnew Stu()hd.get()//后盾人 类的原理 类…...

注解(Annotation)

注解&#xff08;Annotation&#xff09;在 Java 中可以用来简化类的使用&#xff0c;使得被注解的类能够被自动发现、自动创建并在需要的地方直接调用&#xff0c;而不需要手动创建实例。具体来说&#xff0c;注解是用来标识类、方法、字段等的&#xff0c;它们通常与一些框架…...

使用开源项目:pdf2docx,让PDF转换为Word

目录 1.安装python 2.安装 pdf2docx 3.使用 pdf2docx 转换 PDF 到 Word pdf2docx&#xff1a;GitCode - 全球开发者的开源社区,开源代码托管平台 环境&#xff1a;windows电脑 1.安装python Download Python | Python.org 最好下载3.8以上的版本 安装时记得选择上&#…...

编程AI深度实战:AI编程工具哪个好? Copilot vs Cursor vs Cody vs Supermaven vs Aider

Cursor自己可以看成一个IDE,而且有强大的RAG功能,这让它对你的意图感知非常厉害,可以精确补全,可以感受代码片段 Aider可以看作一个袖珍,灵活,强大的扳手,怎么用都行,可以放在脚本里调用,可以看代码,可以修改代码。相比Cursor而言,它感受的是文件级别,颗粒度有些不…...

如何安全地管理Spring Boot项目中的敏感配置信息

在开发Spring Boot应用时&#xff0c;我们经常需要处理一些敏感的配置信息&#xff0c;比如数据库密码、API密钥等。以下是一个最佳实践方案&#xff1a; 1. 创建配置文件 application.yml&#xff08;版本控制&#xff09; spring:datasource:url: ${MYSQL_URL:jdbc:mysql…...

为AI聊天工具添加一个知识系统 之77 详细设计之18 正则表达式 之5

本文要点 昨天讨论了 本项目&#xff08;AI聊天工具添加一个知识系统&#xff09;中正则表达式模板的设计中可能要考虑到的一些问题&#xff08;讨论到的内容比较随意&#xff0c;暂时无法确定 那些考虑 是否 应该是正则表达式模板设计要考虑的以及 是否完整&#xff09;。今天…...

Ubuntu下Tkinter绑定数字小键盘上的回车键(PySide6类似)

设计了一个tkinter程序&#xff0c;在Win下绑定回车键&#xff0c;直接绑定"<Return>"就可以使用主键盘和小键盘的回车键直接“提交”&#xff0c;到了ubuntu下就不行了。经过搜索&#xff0c;发现ubuntu下主键盘和数字小键盘的回车键&#xff0c;名称不一样。…...

安全实验作业

一 拓扑图 二 要求 1、R4为ISP&#xff0c;其上只能配置IP地址&#xff1b;R4与其他所有直连设备间均使用共有IP 2、R3-R5-R6-R7为MGRE环境&#xff0c;R3为中心站点&#xff1b; 3、整个OSPF环境IP基于172.16.0.0/16划分&#xff1b; 4、所有设备均可访问R4的环回&#x…...

NOTEPAD++编写abap

参考下面三个链接 Notepad ABAP代码高亮显示_notepad代码高亮颜色-CSDN博客 百度安全验证 ABAP Syntax Highlighting in Notepad Part 2 - SAP Community 最后XML文件看看你可以自己增加些新语法的高亮显示...

基于python的体育新闻数据可视化及分析

项目 &#xff1a;北京冬奥会体育新闻数据可视化及分析 摘 要 随着社会的不断进步与发展&#xff0c;新时代下的网络媒体获取的信息也更加庞大和繁杂&#xff0c;相比于传统信息来源更加难以分析和辨别&#xff0c;造成了新时代媒体从业者撰写新闻的难度。在此背景下&#xff…...

C# 精炼题18道题(类,三木运算,Switch,计算器)

1.数组元素和 2.数组元素乘积 3.数组元素平均数 4.数组中最大值 5.数组中的偶数 6.数组中的阶乘 7.数组反转 8.字符串反转 9.回文字符串 10.检查回文 11.最小最大值 12.找素数 13.字符串中的最长无重复字符串 14.字符串去重 15.数组中计算两数之和 16.数字到字符…...

vue2语法速通

首先&#xff0c;git clone下来的项目要npm install下载依赖&#xff0c;如果是vue项目&#xff0c;运行通常npm run serve或者npm run dev vue速通一下 使用vite创建项目&#xff08;较快&#xff09; npm create vite 配置文件 src/ ├── assets/ # 存放…...

LabVIEW图片识别逆向建模系统

本文介绍了一个基于LabVIEW的图片识别逆向建模系统的开发过程。系统利用LabVIEW的强大视觉处理功能&#xff0c;通过二维图片快速生成对应的三维模型&#xff0c;不仅降低了逆向建模的技术门槛&#xff0c;还大幅提升了建模效率。 ​ 项目背景 在传统的逆向建模过程中&#xf…...

idea隐藏无关文件

idea隐藏无关文件 如果你想隐藏某些特定类型的文件&#xff08;例如 .log 文件或 .tmp 文件&#xff09;&#xff0c;可以通过以下步骤设置&#xff1a; 打开设置 在菜单栏中选择 File > Settings&#xff08;Windows/Linux&#xff09;或 IntelliJ IDEA > Preference…...

Google C++ Style / 谷歌C++开源风格

文章目录 前言1. 头文件1.1 自给自足的头文件1.2 #define 防护符1.3 导入你的依赖1.4 前向声明1.5 内联函数1.6 #include 的路径及顺序 2. 作用域2.1 命名空间2.2 内部链接2.3 非成员函数、静态成员函数和全局函数2.4 局部变量2.5 静态和全局变量2.6 thread_local 变量 3. 类3.…...

猫眼Java开发面试题及参考答案(上)

详细介绍项目,像项目中如何用 Redis,用到 Redis 哪些数据类型,项目中遇到哪些问题,怎么解决的 在我参与的一个电商项目中,Redis 发挥了至关重要的作用。这个电商项目主要是为用户提供商品浏览、购物车管理、订单处理等一系列功能。 在项目中使用 Redis 主要是为了提升系统…...

CNN的各种知识点(五):平均精度均值(mean Average Precision, mAP)

平均精度均值&#xff08;mean Average Precision, mAP&#xff09; 1. 平均精度均值&#xff08;mean Average Precision, mAP&#xff09;概念&#xff1a;计算步骤&#xff1a;具体例子&#xff1a;重要说明&#xff1a;典型值范围&#xff1a; 总结&#xff1a; 1. 平均精度…...

8.原型模式(Prototype)

动机 在软件系统中&#xff0c;经常面临着某些结构复杂的对象的创建工作&#xff1b;由于需求的变化&#xff0c;这些对象经常面临着剧烈的变化&#xff0c;但是它们却拥有比较稳定一致的接口。 之前的工厂方法和抽象工厂将抽象基类和具体的实现分开。原型模式也差不多&#…...

DeepSeek-R1:开源机器人智能控制系统的革命性突破

目录 引言 一、DeepSeek-R1 的概述 1.1 什么是 DeepSeek-R1&#xff1f; 1.2 DeepSeek-R1 的定位 二、DeepSeek-R1 的核心特性 2.1 实时控制能力 2.2 多传感器融合 2.3 路径规划与导航 2.4 人工智能集成 2.5 开源与模块化设计 2.6 跨平台支持 三、DeepSeek-R1 的技术…...

网络安全学习 day5

状态检测和会话技术 状态检测以 “ 数据流量 ” 为单位来对报文进行检测和转发。即对一条流量的第一个报文进行包过滤规 则检查&#xff0c;并将判断结果作为这条流量的 “ 状态 ” 记录下来 。对于该条流量的后续报文&#xff0c;直接根据这个 “ 状态 ”来判断是否转发还是…...

[leetcode·回溯算法]回溯算法解题套路框架

本文参考labuladong算法笔记[回溯算法解题套路框架 | labuladong 的算法笔记] 本文解决几个问题&#xff1a; 回溯算法是什么&#xff1f;解决回溯算法相关的问题有什么技巧&#xff1f;如何学习回溯算法&#xff1f;回溯算法代码是否有规律可循&#xff1f; 其实回溯算法和我…...