【Linux 进程概念】

【Linux 进程概念】

  • 冯诺依曼体系结构
    • 冯诺依曼结构简要解释:
    • 你用QQ和朋友聊天时数据的流动过程
  • 操作系统(OperatorSystem)
    • 概念
    • 设计OS的目的
    • 定位
    • 操作系统的上下层都分别是什么
    • 如何理解“管理"
    • 总结
  • 进程
    • 基本概念
    • 描述进程-PCB
      • task_ struct内容
    • 组织进程
    • 查看进程
      • 通过目录查询
      • 通过指令查询
    • 通过系统调用获取进程的PID和PPID
    • 通过系统调用创建进程-fork初识
  • 进程状态
    • 看看Linux内核源代码怎么说
    • 进程状态查看
    • Z(zombie)-僵尸进程
      • 僵尸进程危害
    • 孤儿进程
  • 进程优先级
    • 基本概念
    • 查看系统进程
    • PRI and NI
    • PRI vs NI
    • 查看进程优先级的命令
    • 用top命令更改已存在进程的nice:
    • 其他概念
  • 环境变量
    • 基本概念
    • 常见环境变量
    • 查看环境变量方法
    • 测试PATH
    • 测试HOME
    • 测试SHELL
    • 和环境变量相关的命令
    • 环境变量的组织方式
    • 通过代码如何获取环境变量
    • 通过系统调用获取或设置环境变量
  • 程序地址空间

冯诺依曼体系结构

 我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系。
在这里插入图片描述

截至目前,我们所认识的计算机,都是有一个个的硬件组件组成:

  1. 输入单元:包括键盘, 鼠标,扫描仪, 写板等
  2. 中央处理器(CPU):含有运算器和控制器等
  3. 输出单元:显示器,打印机等

冯诺依曼结构简要解释:

 我们要知道计算机的发明是为了帮助人类更高效率的工作,所以我们应当了解其内部构成的原理:
计算机一般的信息需要人来输入,并且结果要呈现出来,所以就有了输入设备输出设备,输入进来的数据经过运算器的运算,再在控制器的控制下进行数据的迁移。

 但是CPU的运行速率是远远大于输入设备和输出设备的,根据木桶原理可知:要想整体提高效率,则需要提高前二者的效率,故引入内存,事先我们可以先将数据从输入设备读入到内存中,让CPU和内存进行读写操作以提高效率。

关于冯诺依曼,必须强调几点:

  1. 这里的存储器指的是内存
  2. 不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)
  3. 外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。
  4. 一句话,所有设备都只能直接和内存打交道。

你用QQ和朋友聊天时数据的流动过程

要使用QQ,首先要确保联网。当你向朋友发送消息时,你的电脑将键盘作为输入设备,显示器和网卡作为输出设备。而你的朋友的电脑则将网卡作为输入设备,显示器作为输出设备。

在这里插入图片描述
一开始,你通过键盘输入消息,该消息被加载到内存中。随后,你的显示器可以从内存中获取消息并显示在你的电脑上,让你能够看到发送的消息。

接着,CPU从内存中获取消息并对其进行封装,随后将其写回内存。此时,你的网卡可以从内存中获取已经封装好的消息,经过一系列处理后(网络处理细节略),消息通过网络传输。在此过程中,你的朋友的网卡从网络中接收到消息,并将其加载到内存中。

随后,你的朋友的CPU从内存中获取消息并对其进行解包操作,然后将解包后的消息写回内存。最后,你的朋友的显示器从内存中获取消息并显示在他的电脑上。

操作系统(OperatorSystem)

概念

 任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。

设计OS的目的

  1. 与硬件进行交互,管理所有的软硬件资源。
  2. 为用户程序(应用程序)提供一个良好的执行环境。

定位

 简单来说,操作系统就是一款进行软硬件资源管理的软件

操作系统的上下层都分别是什么

我们通过对上述的冯诺依曼结构的学习知道了计算机的最基本的组成部分都有什么。

但是我们现在的计算机的硬件不止单单上述那几个:
在这里插入图片描述
在操作系统内部有多个管理模块,共同作用用于操作系统的正常运行。

  1. 内存管理:内存分配、内存共享、内存保护以及内存扩张等等。
  2. 驱动管理:对计算机设备驱动驱动程序的分类、更新、删除等操作。
  3. 文件管理:文件存储空间的管理、目录管理、文件操作管理以及文件保护等等。
  4. 进程管理:其工作主要是进程的调度。

并且我们可以吧计算机的底层硬件分为三个部分:网卡,硬盘和其他。
网卡用于将电脑与因特网链接,硬盘用于储存计算机的各种数据,而其他则包含键盘鼠标显示器等输入输出设备。


但是这里的操作系统是如何与底层的硬件进行交互的呢?
我们可以看到操作系统内有一个模块叫作驱动管理,其则是用于通过驱动让操作系统与硬件搭造一个桥梁。
因为要使硬件与系统可以交互,则必须通过接口,但是计算机的硬件厂家比较多,每个厂家都可能采用不同的协议,若因为每次更换不同厂家的硬件都要更改系统的接口,这样的代价是比较大的,所以系统则提供了一个标准的接口,使操作系统能够了解和控制硬件设备的功能,而无需了解硬件的细节或内部工作原理。驱动程序充当了操作系统与硬件之间的翻译器和中间人角色。

在这里插入图片描述

操作系统的上层是用户,用户通过操作系统来使用计算机。
但是出自于计算机的保护,用户不能直接访问操作系统。(直接访问的风险很大)
所以操作系统提供了各种用于访问的接口,即系统调用接口。

同时,这些接口对于我们普通的用户而言学习和使用的成本太高了,从而我们封装了系统的各种接口,构成用户调用接口层,再通过shell外壳或者部分指令来让用户来更高效率的来访问系统。
在这里插入图片描述

如何理解“管理"

要想学好操作系统,那么就必须正确理解到底什么是管理。

众所周知,我们在上学的时候,我们所扮演的角色是学生,一个学校还有校长,和辅导员。
在这里插入图片描述

很明显,这里的校长就是管理者,而我们学生则是被管理者,但是根据实际生活也知道,校长是一般不会直接来管理全校的所有学生的,所以,这里就出现了一个新角色———辅导员。
校长作为管理者来管理学生,校长实际上就是那个做决策的人,但是校长作出决策后并不需要自己来执行,而是让辅导员去执行,所以辅导员的主要任务就是执行管理者的决策,我们通常将其称为执行者。
在这里插入图片描述
虽然说校长是管理学生的,但是我们在学校一般情况下是看不到校长本人的,那么校长是如何做到在不看到我们的情况下对我们进行管理的呢?

举个例子,现在校长要求辅导员将计算机成绩排名前十的学生的各科资料以及平时表现记录拿过来,他将从这十名同学之中选出三名学生参加本次的编程大赛,当辅导员将资料拿来后校长选出三名学生说:“就这三个了,你找个老师对这三名学生进行一下强化训练,然后参加本次的编程大赛。”然后校长就什么也不管了。

在这一过程中,校长事实上并未亲眼见到这三名学生,却对他们进行了管理。他所依据的是什么?没错,他所依据的是数据。

实际上,学校对每个学生的各种信息进行了管理,包括基本信息、成绩信息以及健康信息等等。每一套信息都描述了一个学生,校长通过对这些信息的管理来实现对学生的管理。在C语言中,这一套信息被称为抽象结构体,在C++中则被称为面向对象。
在这里插入图片描述

当学生数量增多时,校长便可以将所有学生的信息组织起来。当然,组织的方式有很多种,比如链表、顺序表、树等等。每种组织方式都有其优势,因此就有了一门专门教导我们如何管理数据的课程,那就是数据结构。在这里,我们假设校长以双链表的形式将学生信息组织起来。
在这种情况下,校长对每个学生的管理实际上转变为对双链表的增删查改操作。当有新生时,直接向该双链表添加一个节点;而当学生毕业时,只需将其信息从双链表中移除即可。
在这里插入图片描述

概括来说:管理者对被管理者的管理实际上是先描述被管理者的各种信息,然后将这些描述信息根据某种数据结构组织起来。最后,管理者对被管理者的实际管理就是对这个数据结构的管理。

总结

计算机管理硬件

  1. 描述起来,用struct结构体
  2. 组织起来,用链表或其他高效的数据结构

进程

基本概念

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

描述进程-PCB

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

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

task_ struct内容

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

组织进程

 在 Linux 内核中,进程(也称为任务)以一个双向链表的形式组织在一起。这个链表的每个节点都是一个 task_struct 结构体,包含了表示一个进程所需的所有信息。

这个链表通常被称为进程链表或任务链表,其中每个节点都指向下一个节点和上一个节点,从而形成了一个双向链表的结构。这种双向链表的结构有助于高效地遍历进程列表以及进行进程管理操作,比如创建、销毁和调度进程等。

并且由于 Linux 是一个多任务操作系统,因此这个链表中可以包含大量的进程,每个进程都有自己的 task_struct 结构体。这些结构体通过链表连接在一起,构成了操作系统内核中进程管理的基础数据结构之一。

同时当操作系统启动时,会创建一个初始的进程(通常是 init 进程),然后根据需要动态地创建和销毁其他进程。这些进程都会被加入到进程链表中,依靠这个链表,操作系统能够有效地管理系统中运行的所有进程。

在这里插入图片描述
  进程=内核task_struct结构体+程序的代码和数据

查看进程

通过目录查询

Linux系统中万物皆文件,所以进程也一定是以文件的形式存在于系统中的。

我们可以在根目录找到下面这个路径,其里面就是存放的进程的相关信息。
在这里插入图片描述
这些数字其实是某一进程的PID,对应文件夹当中记录着对应进程的各种信息。我们若想查看PID为1的进程的进程信息,则查看名字为1的文件夹即可。
在这里插入图片描述

通过指令查询

我们也可以通过指令来查询系统的进程信息,下面来了解以下几个指令:

  1. ps:ps 命令是最基本和常用的进程查询命令。它可以显示当前终端下的进程信息。简单地键入 ps 就会显示当前用户的所有进程。
  2. ps aux:ps aux 命令显示了所有用户的所有进程的详细信息,包括进程的用户、PID、CPU 使用情况、内存使用情况等。
  3. top:top 命令提供了一个实时的系统进程监视功能,显示当前系统中运行的所有进程的动态信息。只需键入 top,你就会看到类似任务管理器的实时进程信息。

使用ps -aux 来查询
在这里插入图片描述
使用 top 来查询
在这里插入图片描述

因为直接使用ps来查询的话,打印的东西比较繁杂,通常我们搭配grep来使用。

ps aux |head -1 && ps aux | grep myprocess |grep -v grep

在这里插入图片描述
让我们来逐步来理解这段语句:

  1. ps aux | head -1:这部分命令首先执行 ps aux 命令,它会列出当前系统上所有进程的详细信息。然后**通过管道 **| 将输出传递给 head -1 命令,head -1 的作用是只显示输出的第一行。这样做是为了显示 ps aux 命令输出的表头 其中包含了各列的名称,比如 USER、PID、%CPU、%MEM 等。
  2. ps aux | grep myprocess | grep -v grep: 这部分命令使用 ps aux 命令列出所有进程的详细信息,并将输出通过管道 | 传递给 grep myprocess这个命令会过滤出包含 “myprocess” 字符串的行。接着,又将过滤出的结果再次通过管道传递给 grep -v grep,这个命令的作用是去除包含 “grep” 字符串的行, 这是因为当你用 grep 命令查找进程时,grep 命令本身也会出现在进程列表中,我们不希望将它包括在结果中。 最终,输出的结果将显示出符合条件的进程信息。

在这里插入图片描述

通过系统调用获取进程的PID和PPID

我们在上文说过:

标示符: 描述本进程的唯一标示符,用来区别其他进程。

那我们来了解一下 PID(进程标识符)PPID(父进程标识符)

  1. PID(进程标识符):PID 是一个唯一的整数,用于标识在 Linux 中运行的每个进程。当操作系统启动一个新的进程时,会为该进程分配一个唯一的 PID。PID 通常从 1 开始递增,直到达到操作系统所支持的最大 PID 值(通常是 32767 或更高)。

  2. PPID(父进程标识符):PPID 是指创建当前进程的进程的 PID。在 Linux 中,每个进程都有一个父进程,除了初始进程(通常是 PID 1)。当父进程创建一个新的子进程时,子进程的 PPID 就会被设置为父进程的 PID。PPID 用于建立进程之间的父子关系。通过 PPID,可以追溯到进程的创建链条,即该进程是如何被创建的。

我们可以通过系统函数的调用来得到这两个标识符,可以通过 man 来查询一下
在这里插入图片描述

我们可以通过getpid() 和 getppid() 来分别获得:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include <sys/types.h>
  4 int main ()
  5 {
  6   while(1) 
  7   {
  8     printf("I am a process my ppid is %d , my pid is %d \n",getppid(),getpid());
  9     sleep(1);
 10   }
 11 } 

在这里插入图片描述
这里使用sleep函数让代码打印完之后休息1s.

当运行该代码生成的可执行程序后,便可循环打印该进程的PID和PPID。
在这里插入图片描述

我们可以通过ps命令查看该进程的信息,即可发现通过ps命令得到的进程的PID和PPID与使用系统调用函数getpidgetppid所获取的值相同。
在这里插入图片描述

通过系统调用创建进程-fork初识

先man一下fork

[qq@iZ0jl65jmm6w9evbwz2zuoZ 3_19]$ man fork

在这里插入图片描述

fork() 函数是 Unix 和类 Unix 系统中的一个系统调用,用于创建一个新的进程,这个新进程称为子进程,它是调用 fork() 的进程(父进程)的一个复制。

当调用 fork() 函数时,操作系统会复制当前进程的所有内存内容(包括代码、数据、堆栈等) 到新的进程空间中,并在新的进程空间中执行相同的程序。这样,原始进程和新创建的进程会成为两个完全独立的进程,它们共享相同的代码和数据,但拥有不同的内存地址空间、进程 ID(PID)和父进程 ID(PPID)。

其中,pid_t 是一个整数类型,表示进程 ID,fork() 函数返回两次在父进程中返回新创建的子进程的 PID,而在子进程中返回 0。这样,通过判断 fork() 的返回值,可以知道当前是在父进程还是子进程中执行。

我们可以通过下面这个代码来理解这个过程

在这里插入图片描述

在运行这段代码的时候我们可以监控这个进程:
在这里插入图片描述

在这里插入图片描述
当然运行该代码后,我们可以通过以下监控脚本,每隔一秒对该进程的信息进行检测。

while :; do ps axj | head -1 && ps axj | grep proc | grep -v grep;echo "######################";sleep 1;done

在这里插入图片描述

进程状态

在这里插入图片描述

看看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 * const task_state_array[] = {
 "R (running)", /* 0 */
 "S (sleeping)", /* 1 */
 "D (disk sleep)", /* 2 */
 "T (stopped)", /* 4 */
 "t (tracing stop)", /* 8 */
 "X (dead)", /* 16 */
 "Z (zombie)", /* 32 */
 };
  • R (running): 表示进程正在运行。
  • S (sleeping): 表示进程正在睡眠状态。
  • D (disk sleep): 表示进程正在等待磁盘 I/O 操作完成的睡眠状态。
  • T (stopped): 表示进程已停止。
  • t (tracing stop): 表示进程因为正在被跟踪而停止。
  • X (dead): 表示进程已经终止。
  • Z (zombie): 表示进程成为僵尸进程,即已经终止但其父进程尚未收回其资源。

注意:进程的当前状态是保存到自己的进程控制块(PCB)当中的,在Linux操作系统当中也就是保存在task_struct当中的。

进程状态查看

在Linux操作系统当中我们可以通过 ps aux 或 ps axj 命令查看进程的状态。

[qq@iZ0jl65jmm6w9evbwz2zuoZ 3_20]$ ps ajx

在这里插入图片描述

[qq@iZ0jl65jmm6w9evbwz2zuoZ 3_20]$ ps aux

在这里插入图片描述

二者的区别在于 -j 会显示作业控制相关的信息,而 -u 会显示与用户相关的信息。


运行状态-R

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

所有处于运行状态,即可被调度的进程,都被放到运行队列当中,当操作系统需要切换进程运行时,就直接在运行队列中选取进程运行。

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

比如一个sleep函数,如下图:
在这里插入图片描述

这里的sleep会是程序在运行的时候,睡眠100s,即我们可以查看到进程的浅度睡眠状态S

在这里插入图片描述

而处于浅度睡眠状态的进程是可以被杀掉的,我们可以使用kill命令将该进程杀掉。

在这里插入图片描述

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

例如,某一进程要求对磁盘进行写入操作,那么在磁盘进行写入期间,该进程就处于深度睡眠状态,是不会被杀掉的,因为该进程需要等待磁盘的回复(是否写入成功)以做出相应的应答。(磁盘休眠状态)

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

例如,我们对一个进程发送SIGSTOP信号,该进程就进入到了暂停状态。
在这里插入图片描述
我们再对该进程发送SIGCONT信号,该进程就继续运行了。

在这里插入图片描述

注意: 使用kill命令可以列出当前系统所支持的信号集。

[qq@iZ0jl65jmm6w9evbwz2zuoZ 3_20]$ kill -l

在这里插入图片描述

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

首先,僵尸状态的存在是必要的,因为进程被创建的目的就是完成某项任务,那么当任务完成的时候,调用方是应该知道任务的完成情况的,所以必须存在僵尸状态,使得调用方得知任务的完成情况,以便进行相应的后续操作。
例如,我们写代码时都在主函数最后返回0。

在这里插入图片描述

实际上这个0就是返回给操作系统的,告诉操作系统代码顺利执行结束。在Linux操作系统当中,我们可以通过使用echo $?命令获取最近一次进程退出时的退出码。
在这里插入图片描述
程退出的信息(例如退出码),是暂时被保存在其进程控制块当中的,在Linux操作系统中也就是保存在该进程的task_struct当中。

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

Z(zombie)-僵尸进程

僵尸进程就是我们刚刚所提到的处于僵尸状态的进程。
退出的信息未被读取,相关的数据未被释放,该进程一直在等待其退出信息被读取,故而该进程是僵尸进程。

我们来一个创建维持30秒的僵死进程例子:

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

int main()
{
    pid_t id = fork();
    if (id < 0) {
        perror("fork");
        return 1;
    }
    else if (id > 0) { //parent
        printf("parent[%d] is sleeping...\n", getpid());
        sleep(30);
    }
    else {
        printf("child[%d] is begin Z...\n", getpid());
        sleep(5);
        exit(EXIT_SUCCESS);
    }
    return 0;
}

在这里插入图片描述
子进程退出,一直等待父进程来读取退出信息,但是父进程因为一直还在运行,无法读取,所有此时的子进程就是一个僵尸进程(Z).

僵尸进程危害

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

孤儿进程

我们思考这样一个问题:
 父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢?

在Linux当中的进程关系大多数是父子关系,若子进程先退出而父进程没有对子进程的退出信息进行读取,那么我们称该进程为僵尸进程。但若是父进程先退出,那么将来子进程进入僵尸状态时就没有父进程对其进行处理,此时该子进程就称之为孤儿进程。

若是一直不处理孤儿进程的退出信息,那么孤儿进程就会一直占用资源,此时就会造成内存泄漏。因此,当出现孤儿进程的时候,孤儿进程会被1号init进程领养,此后当孤儿进程进入僵尸状态时就由int进程进行处理回收。

我们通过下面这段代码来理解这个过程:

 #include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
 
int main()
 {
    pid_t id = fork();
    if(id < 0){
        perror("fork");
        return 1;
    }
    else if(id == 0){//child
        printf("I am child, pid : %d\n", getpid());
        sleep(10);
    }else{//parent
        printf("I am parent, pid: %d\n", getpid());
        sleep(3);
        exit(0);
    }
    return 0;
 }

在这里插入图片描述
父进程退出后,子进程被1号进程领养

进程优先级

基本概念

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

查看系统进程

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

[qq@iZ0jl65jmm6w9evbwz2zuoZ 3_20]$ ps -l

在这里插入图片描述
我们先了解一下下面这几个重要信息:

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

PRI and NI

  • PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小
    进程的优先级别越高
  • 那NI,就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值,nice其取值范围是-20至19,一共40个级别。
  • PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice
  • 这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行
  • 所以,调整进程优先级,在Linux下,就是调整进程nice值

PRI vs NI

  • 需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进
    程的优先级变化。
  • 可以理解nice值是进程优先级的修正修正数据

查看进程优先级的命令

我们可以使用ps -al命令查看该进程优先级的信息。

[qq@iZ0jl65jmm6w9evbwz2zuoZ 3_20]$ ps -al

在这里插入图片描述

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

top命令就相当于Windows操作系统中的任务管理器,它能够动态实时的显示系统当中进程的资源占用情况。

那怎么通过top来修改呢?

进入top后按“r”–>输入进程PID–>输入nice值

在这里插入图片描述

使用top命令后按“r”键,会要求你输入待调整nice值的进程的PID。

在这里插入图片描述

输入进程PID并回车后,会要求你输入调整后的nice值

在这里插入图片描述

输入nice值后按“q”即可退出,如果我们这里输入的nice值为10,那么此时我们再用ps命令查看进程的优先级信息,即可发现进程的NI变成了10,PRI变成了90(80+NI)。

在这里插入图片描述
注意: 若是想将NI值调为负值,也就是将进程的优先级调高,需要使用sudo命令提升权限。

通过renice命令更改进程的nice值
使用renice命令,后面跟上更改后的nice值和进程的PID即可。
在这里插入图片描述

其他概念

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

环境变量

基本概念

环境变量(environment variables) 一般是指在操作系统中用来指定操作系统运行环境的一些参数

例如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但
是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。

环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性

常见环境变量

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

查看环境变量方法

  echo $NAME //NAME:你的环境变量名称

在这里插入图片描述

测试PATH

我们之前提过,在Linux中,万物皆文件,我们每一条执行的语句都是一个可执行文件。
比如我们使用的 ls ,为什么在我们使用 ls 的时候就不用带上路径呢?

这时我们可以使用which来搜索一下ls的路径
在这里插入图片描述
我们看到ls的路径是 /usr/bin/ls 而我们刚刚查到的PATH不正好包含了这个路径,故而执行ls的时候不用带上路径,而对于我们的myprocess文件,我们也可以吧其路径加入到PATH下,以后就可以直接执行myprocess

我们有两种方式:

方式一:将可执行程序拷贝到环境变量PATH的某一路径下。

[qq@iZ0jl65jmm6w9evbwz2zuoZ 3_20]$ sudo cp myprocess /usr/bin/

在这里插入图片描述
这里需要sudo提权一下。

这样我们就可以直接运行myprocess了。

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

[qq@iZ0jl65jmm6w9evbwz2zuoZ 3_20]$ export PATH=$PATH:/home/qq/bt111/Linux/3_20

将可执行程序所在的目录导入到环境变量PATH当中后,位于该目录下的可执行程序也就可以在不带路径的情况下执行了。

在这里插入图片描述

测试HOME

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

普通用户示例:在这里插入图片描述
超级用户示例:
在这里插入图片描述

测试SHELL

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

而该命令行解释器实际上是系统当中的一条命令,当这个命令运行起来变成进程后就可以为我们进行命令行解释

和环境变量相关的命令

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

env:显示所有的环境变量: 在这里插入图片描述
set:显示本地定义的shell变量和环境变量。
在这里插入图片描述

部分环境变量说明: 在这里插入图片描述

环境变量的组织方式

在系统当中,环境变量的组织方式如下:
在这里插入图片描述每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以’\0’结尾的环境字符串

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

让我们重新来认识一下 main() 函数,其实main是有三个参数的。
在这里插入图片描述

  • argc(ArgumentCount):这是一个整数,表示在命令行中传递给程序的参数数量,包括程序名。argc至少为1,因为程序名本身也算一个参数。
  • argv [ ](ArgumentVector):这是一个指向字符指针数组的指针,它包含了命令行参数的实际值
  • envp [ ] (EnvironmentPointer):这个参数是可选的,在一些操作系统中可能不存在。它是一个指向环境变量的指针数组。

在Linux操作系统下,编写以下代码,生成可执行程序并运行。
在这里插入图片描述
结果如下:

在这里插入图片描述


main函数的第二个参数是一个字符指针数组,数组当中的第一个字符指针存储的是可执行程序的位置,其余字符指针存储的是所给的若干选项,最后一个字符指针为空,而main函数的第一个参数代表的就是字符指针数组当中的有效元素个数。
在这里插入图片描述
main函数的第三个参数接收的实际上就是环境变量表,我们可以通过main函数的第三个参数来获取系统的环境变量。
例如,编写以下代码,生成可执行程序并运行。
在这里插入图片描述
运行结果就是各个环境变量的值:
在这里插入图片描述
除了使用main函数的第三个参数来获取环境变量以外,我们还可以通过第三方变量environ来获取
在这里插入图片描述
结果如下:
在这里插入图片描述
注意: libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时要用extern进行声明。

通过系统调用获取或设置环境变量

除了通过main函数的第三个参数和第三方变量environ来获取环境变量外,我们还可以通过系统调用getenv函数来获取环境变量。
getenv函数可以根据所给环境变量名,在环境变量表当中进行搜索,并返回一个指向相应值的字符串指针。

例如,使用getenv函数获取环境变量PATH的值。
在这里插入图片描述
在这里插入图片描述

程序地址空间

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/474244.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

序列化与反序列化介绍

文章目录 一、序列化与反序列化二、PHP反序列化漏洞成因三、JAVA反序列化 一、序列化与反序列化 在PHP语言开发层面上基本都是围绕着serialize()&#xff0c;unserialize()这两个函数。serialize()函数序列化对象后&#xff0c;可以很方便的将它传递给其他需要它的地方&#x…

由浅到深认识Java语言(9):Eclipse IDE简介

该文章Github地址&#xff1a;https://github.com/AntonyCheng/java-notes 在此介绍一下作者开源的SpringBoot项目初始化模板&#xff08;Github仓库地址&#xff1a;https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址&#xff1a;https://blog.c…

【蓝桥杯入门记录】继电器、蜂鸣器及原理图分析

一、继电器、继电器概述 &#xff08;1&#xff09;蜂鸣器原理 蜂鸣器的发声原理由振动装置和谐振装置组成&#xff0c;而蜂鸣器又分为无源他激型与有源自激型&#xff0c;蜂鸣器的发声原理为: 1、无源他激型蜂鸣器的工作发声原理是&#xff1a;方波信号输入谐振装置转换为声…

稀碎从零算法笔记Day23-LeetCode:二叉树的最大深度

题型&#xff1a;链表、二叉树的遍历 链接&#xff1a;104. 二叉树的最大深度 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 题目描述 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上…

ES集群不识别节点SSL证书的问题处理

问题描述 在启动ES服务并试图加入其他节点上已启动的集群时&#xff0c;出现报错(原文是一大段话&#xff0c;我按语义拆成了几段)&#xff1a; [2024-03-19T16:32:02,844][WARN ][o.e.c.s.DiagnosticTrustManager] [node-2-master] failed to establish trust with server a…

高压线下垂钓很危险!高压线下防垂钓智能语音警示杆:科技守护生命

初春时节&#xff0c;气温逐渐回升&#xff0c;在这阳光明媚的日子里&#xff0c;大批“捕鱼达人”纷纷开始行动&#xff0c;河边、池塘、水库……不放过任何一个垂钓点&#xff0c;甚至在高压线下&#xff0c;依旧自信甩杆&#xff0c;殊不知高压线下垂钓&#xff0c;轻则伤、…

聚类算法之层次聚类(Hierarchical Clustering)

注意&#xff1a;本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 &#xff08;[www.aideeplearning.cn]&#xff09; 层次聚类是一种非常独特和强大的聚类方法&#xff0c;与众多其他的聚类技术相比&#xff0c;它不仅为数据集提供了一个划分&#xff0c;还给出了…

鸿蒙APP应用开发教程—超详细的项目结构说明

1. 新建项目 打开DevEco Studio, 选择 Create Project: 1.1 选择模版 Create Project - Choose Template 1.2 配置项目 Create Project - Configure Project 如果使用的是 DevEco 3.X 版本, 可以根据 Compile SDK版本选择不同的模式, 比如: 3.0.0(API 8)及更早 - 仅支持 …

【数据结构】堆和树详解堆和二叉树的实现堆的top-k问题

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;数据结构_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.树概念及结构 1.1 树的概念 2.2 树的相关概念 1.3 树的表示 1.4 树在实际中的运用 2.二叉树的概念及结构 2.1 二叉树的概念…

力扣389周赛复盘

字符串及其反转中是否存在同一子字符串 class Solution {public boolean isSubstringPresent(String s) {StringBuilder sb new StringBuilder(s);String reverse sb.reverse().toString(); for (int i 0; i < s.length() - 2; i) { // 修改循环终止条件为 <&#xf…

matlab实现对全球不规则投影数据的投影转换

前几个专栏我们讨论了几个不规则的投影转换问题&#xff0c;有需要的可以阅读以下文章&#xff1a; matlab实现对极地投影数据的投影转换_matlab极地投影-CSDN博客 联合matlab和Arcgis进行netcdf格式的雪覆盖数据的重新投影栅格-CSDN博客 这次遇到的问题是一个墨卡托投影的数据…

【JavaWeb】Spring非阻塞通信 - Spring Reactive之WebFlux的使用

【JavaWeb】Spring非阻塞通信 - Spring Reactive之WebFlux的使用 文章目录 【JavaWeb】Spring非阻塞通信 - Spring Reactive之WebFlux的使用参考资料一、初识WebFlux1、什么是函数式编程1&#xff09;面向对象编程思维 VS 函数式编程思维&#xff08;封装、继承和多态描述事物间…

vue3新功能-Teleport

1.teleport 在组件内的任何位置渲染内容 将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去。 例:将组件dialog添加到body下面 <teleport to"body"> <el- dialog --> </teleport> 2.fragments 多个根元素外层不需要…

2024年了,还能学自动化吗?

大家都说2024年软件测试行业会卷的更厉害&#xff0c;简单的功能测试不再是入门的标准&#xff0c;那么2024年是否可以从自动化测试这块冲一把呢&#xff1f; 我们先来看看过去的一年自动化测试在测试行业中的发展分析&#xff1a; 01 市场需求增长 随着技术的进步和企业对软件…

爬虫入门系列-HTML基础语法

&#x1f308;个人主页&#xff1a;会编辑的果子君 &#x1f4ab;个人格言:“成为自己未来的主人~” HTML基础语法 bs4解析比较简单&#xff0c;但是呢&#xff0c;首先你需要了解一丢丢的html知识&#xff0c;然后再去使用bs4去提取&#xff0c;逻辑和编写难度就会非常简…

消息队列常见的两种消费模式

一、点对点模式 点对点模式&#xff1a;生产者发送消息到消息队列&#xff0c;消费者从消息队列中接收、处理消息&#xff0c;消息被消费后&#xff0c;就不在消息队列中了。每个消息只能由一个消费者接收和处理。如果有多个消费者监听同一个队列&#xff0c;消息将被发送到其…

刷题DAY29 | LeetCode 491-递增子序列 46-全排列 47-全排列 II

491 递增子序列&#xff08;medium&#xff09; 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 数组中可能含有重复元素&#xff0c;如出现两个整数相等&#xff0c;也…

流畅的 Python 第二版(GPT 重译)(五)

第九章. 装饰器和闭包 有人对将这个功能命名为“装饰器”的选择提出了一些抱怨。主要的抱怨是该名称与其在 GoF 书中的用法不一致。 名称 decorator 可能更多地归因于其在编译器领域的用法—语法树被遍历并注释。 PEP 318—函数和方法的装饰器 函数装饰器让我们在源代码中“标记…

外包干了14天,技术退步明显。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;2019年我通过校招踏入了成都一家软件公司&#xff0c;开始了我的职业生涯。那时的我&#xff0c;满怀热血和憧憬&#xff0c;期待着在这个行业中闯出一片天地。然而&#xff0c;随着时间的推移&#xff0c;我发现自己逐渐陷入…

NVIDIA Chat with RTX教程使用以及CUDA和CUDNN

基本环境安装&#xff1a;CUDA12.1CUDNNcudnn-windows-x86_64-8.9.7.29_cuda12-archive 1、CUDA下载 CUDA官方安装教程: https://docs.nvidia.com/cuda/cuda-installation-guide-microsoft-windows/index.html CUDA Toolkit的下载: CUDA Toolkit 12.1 Downloads | NVIDIA Dev…