【Linux】进程概念
文章目录
- 【Linux】进程概念
- 1、冯诺依曼体系结构
- 2、操作系统
- 2.1 概念
- 2.2 设计OS的目的
- 2.3 定位
- 2.4 管理
- 2.5 系统调用和库函数概念
- 3、进程
- 3.1 基本概念
- 3.2 描述进程—PCB
- 3.3 组织进程
- 3.4 查看进程
- 3.5 获取进程标示符
- 3.6 创建进程-fork初识
- 3.7 进程状态
- 3.7.1 进程状态查看
- 3.7.2 僵尸进程
- 3.8 孤儿进程
- 3.9 进程优先级
- 3.9.1 基本概念
- 3.9.2 查看系统进程
- 3.9.3 PRI & NI
- 3.9.4 其他概念
- 4、环境变量
- 4.1 基本概念
- 4.2 常见环境变量
- 4.3 查看环境变量
- 4.4 环境变量相关的命令
- 4.5 设置环境变量
- 4.6 环境变量的全局属性
- 5、程序地址空间
- 5.1 进程地址空间
1、冯诺依曼体系结构
截至目前,我们所认识的计算机,都是由一个个的硬件组件组成
- 输入单元:包括键盘, 鼠标,扫描仪, 写板等
- 中央处理器(CPU):含有运算器和控制器等
- 输出单元:显示器,打印机等
关于冯诺依曼,必须强调几点:
- 这里的存储器指的是内存
- 不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)
- 外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。
- 一句话,所有设备都只能直接和内存打交道。
2、操作系统
2.1 概念
操作系统是计算机系统中的核心软件,它提供了对计算机硬件资源的管理和控制,以及为用户和应用程序提供服务和接口。操作系统包括以下主要组成部分:
-
内核(Kernel):内核是操作系统的核心,它负责管理和控制计算机的硬件资源。内核包括以下主要功能:
- 进程管理:负责创建、调度、终止和管理进程(程序的执行实例)。
- 内存管理:管理计算机内存的分配和回收,以及虚拟内存的管理。
- 文件管理:提供对文件和文件系统的管理,包括文件的创建、读取、写入和删除等操作。
- 设备管理:管理计算机的输入输出设备,包括驱动程序的加载和控制。
- 网络管理:提供网络连接和通信的管理和控制。
-
其他程序:除了内核外,操作系统还包括其他程序,用于提供额外的功能和服务。这些程序可以包括:
- 函数库(Library):提供常用函数和工具,供应用程序调用和使用。
- Shell程序:提供与操作系统交互的命令行界面或图形用户界面。
- 系统工具和实用程序:例如任务管理器、文件浏览器等,用于辅助系统管理和操作。
- 驱动程序(Driver):用于与特定硬件设备进行通信和控制。
操作系统的主要目标是为用户和应用程序提供一个方便、高效、安全和可靠的计算环境。通过内核和其他程序的协同工作,操作系统实现了对计算机资源的统一管理,提供了一系列的功能和服务,使得用户和应用程序能够方便地与计算机进行交互和利用。
2.2 设计OS的目的
设计操作系统的主要目的可以总结为以下两点:
-
与硬件交互,管理所有的软硬件资源:操作系统作为计算机系统的核心软件,负责与计算机硬件进行交互,并管理和控制所有的软硬件资源。它提供了对处理器、内存、存储设备、输入输出设备等硬件资源的管理和分配,以确保它们能够有效地被应用程序和用户所利用。操作系统通过提供统一的接口和抽象层,隐藏底层硬件的复杂性,使得应用程序和用户可以方便地访问和使用这些资源。
-
为用户程序(应用程序)提供一个良好的执行环境:操作系统为用户程序提供了一个良好的执行环境,使得应用程序可以在其上运行并实现预期的功能。操作系统通过提供进程管理、内存管理、文件系统、网络通信等功能,为应用程序提供了运行所需的基本服务和接口。操作系统还负责处理和调度多个应用程序的执行,以确保它们能够按照预期顺序和优先级运行,并合理地利用计算机资源。
通过这两个目的,操作系统实现了对计算机系统的有效管理和资源分配,为用户和应用程序提供了一个高效、安全和可靠的执行环境。操作系统的设计考虑了提高系统性能、资源利用率、用户体验和系统安全性等方面的因素,以满足不同应用和用户的需求。
2.3 定位
在整个计算机软硬件架构中,操作系统的定位是一款纯正的“搞管理”的软件。操作系统作为计算机系统的核心软件,主要负责管理和控制计算机的硬件资源,以及为用户程序和应用程序提供一个良好的执行环境。
操作系统的主要任务是管理和协调计算机系统中的各种资源,包括处理器、内存、存储设备、输入输出设备等。它负责分配和调度处理器的时间片,管理内存的分配和回收,处理文件系统的访问和操作,以及控制和协调各种输入输出设备的使用。操作系统还提供了诸如进程管理、线程管理、文件管理、网络管理等功能,以确保系统的稳定运行和资源的有效利用。
操作系统的设计目标是提供一个高效、安全、可靠、用户友好的计算环境。它通过对底层硬件资源的抽象和管理,为应用程序和用户提供了一个统一的接口和执行环境。操作系统还负责处理和解决各种系统级的问题和异常情况,以确保系统的稳定性和可靠性。
总的来说,操作系统定位为一款专注于管理和控制计算机资源、提供良好执行环境的软件。它扮演着整个计算机系统的管理者和协调者的角色,为应用程序和用户提供了一个方便、高效、安全的计算环境。
2.4 管理
在操作系统的上下文中,"管理"指的是对计算机系统中的各种资源进行控制、分配、调度和协调的过程。类比于组织管理中的概念,操作系统管理的对象是计算机系统中的硬件资源和软件执行环境。
举几个管理的例子来说明:
-
进程管理:操作系统管理计算机系统中的进程(程序的执行实例)。它负责创建新的进程,分配处理器时间片,调度进程的执行顺序,以及提供进程间的通信和同步机制。
-
内存管理:操作系统管理计算机系统的内存资源。它负责内存的分配和回收,以及虚拟内存的管理和页面置换算法的实现。
-
文件管理:操作系统管理计算机系统中的文件和文件系统。它负责文件的创建、读取、写入和删除等操作,以及文件的组织、存储和访问控制。
-
设备管理:操作系统管理计算机系统的输入输出设备。它负责设备的初始化和驱动程序的加载,控制设备的访问和操作,以及处理设备的中断和异常。
-
资源分配与调度:操作系统管理计算机系统中的各种资源,如处理器、内存、存储设备等。它根据各个资源的使用情况和优先级,进行资源的分配和调度,以满足应用程序的需求,优化资源利用和系统性能。
在组织被管理对象的方面,操作系统将这些资源进行组织和抽象,以提供统一的接口和执行环境。例如,操作系统通过进程控制块(PCB)来管理进程,通过内存分页和虚拟地址空间来管理内存,通过文件控制块(FCB)和目录结构来管理文件系统等。通过这样的组织和抽象,操作系统能够对这些资源进行有效的管理和控制,并为应用程序和用户提供统一的访问方式和接口。
2.5 系统调用和库函数概念
在开发角度,操作系统提供了一组接口,允许上层应用程序与底层操作系统进行交互和利用系统资源。这些接口可以分为两类:系统调用和库函数。
-
系统调用(System Calls):系统调用是操作系统提供给用户程序的一组接口,允许用户程序通过操作系统的中断机制请求操作系统执行特定的功能。系统调用提供了访问底层操作系统功能的方式,例如创建进程、读写文件、分配内存等。系统调用通常提供了底层的、直接操作硬件和系统资源的能力,但使用系统调用需要较高的编程技能和对系统底层的了解。
-
库函数(Library Functions):库函数是在系统调用的基础上进一步封装和抽象的函数集合。库函数通常由开发者创建,以便简化对底层系统调用的使用,提供更高层次的功能和接口。开发者可以将一组相关的系统调用封装为库函数,以提供更方便、更易用的接口供上层应用程序使用。库函数可以实现一系列常用的功能,如字符串操作、网络通信、图形界面等,并通过隐藏底层的系统调用细节,使开发者能够更加便捷地进行二次开发。
通过系统调用和库函数,开发者可以利用操作系统提供的功能和资源,快速开发应用程序。系统调用提供了直接访问底层操作系统功能的接口,而库函数在系统调用的基础上提供了更高层次的抽象和封装,使开发者能够更加方便地使用和组合这些功能。这种分层的设计可以提高开发效率、降低开发难度,并促进更高层次的应用程序开发。
3、进程
3.1 基本概念
基本概念:
-
程序:程序是一组按照特定顺序组织的指令集合,用于完成特定任务或实现特定功能。程序本身只是一组静态的代码,需要被加载到内存中并由计算机的中央处理器(CPU)执行,才能成为一个实际的运行程序。
-
执行实例:执行实例是指程序在计算机上运行的一个具体实例。当程序被加载到内存中,并由CPU按照指令顺序逐条执行时,就形成了一个程序的执行实例。执行实例是程序的动态运行状态,它在计算机上占用资源并完成任务。
-
内核:内核是操作系统的核心部分,是操作系统的最底层软件。它担当着管理和分配系统资源的实体,包括分配CPU时间片,管理内存的分配和回收,处理设备的输入输出等。内核负责控制和协调系统中的各种资源,使得计算机系统能够高效稳定地运行。
课本概念与内核观点:
课本概念中提到的"程序的一个执行实例"和"正在执行的程序"可以理解为程序被加载到内存中,CPU开始执行它,并形成了一个具体的运行实例,这个实例就是一个正在执行的程序。
内核观点中强调的"担当分配系统资源的实体"指的是操作系统的核心组件——内核。内核负责管理和分配系统资源,它根据程序的需求分配CPU时间,确保程序能够得到执行;管理内存,为程序分配所需的内存空间;协调各种输入输出设备,使得程序能够与外部设备进行交互等。内核在操作系统中扮演着至关重要的角色,确保了系统的稳定性和资源的高效利用。
3.2 描述进程—PCB
进程控制块(PCB),也称为任务控制块(TCB)或进程描述符(Process Descriptor),是操作系统中用于管理和控制进程的数据结构。它是存储了进程的所有相关信息的数据块,可以看作是进程属性的集合。
PCB包含了进程在操作系统中的所有信息,用于跟踪和管理进程的状态、资源分配和执行情况。每个进程都有一个对应的唯一的PCB,操作系统通过访问和操作PCB来管理进程。
PCB通常包含以下信息:
-
进程标识符(PID):唯一标识进程的数字或标识符。
-
程序计数器(Program Counter):指示下一条要执行的指令的地址。
-
寄存器(Registers):保存进程的寄存器状态,包括通用寄存器、指令指针等。
-
进程状态(Process State):记录进程的当前状态,如运行、就绪、阻塞等。
-
优先级(Priority):进程的优先级,用于调度和分配CPU时间。
-
内存管理信息:包括进程的内存分配情况、虚拟地址空间等。
-
文件描述符表(File Descriptor Table):记录进程打开的文件和设备的信息。
-
资源分配信息:记录进程分配到的资源,如CPU时间片、内存空间等。
-
父子进程关系(Parent-Child Relationship):记录进程的父进程和子进程的关系。
-
等待状态信息(Waiting State Information):记录进程等待的事件或资源。
PCB在操作系统中起着至关重要的作用,它存储了操作系统管理进程所需的所有信息,包括进程的上下文、状态和资源分配情况。通过访问和操作PCB,操作系统可以对进程进行调度、资源管理和状态转换等操作,确保进程能够有序、安全地执行。在Linux操作系统中,PCB的具体实现是通过task_struct结构体来表示和管理进程信息。
对task_struct的内容进行以下分类:
-
标识符:描述进程的唯一标识符,用于区别其他进程。
-
状态:包括任务的状态信息,如运行、就绪、阻塞等;以及退出代码和退出信号等与进程结束相关的信息。
-
优先级:表示进程相对于其他进程的优先级。
-
程序计数器(Program Counter):指示下一条将要被执行的指令的地址。
-
内存指针:包括指向程序代码和进程相关数据的指针,以及指向与其他进程共享的内存块的指针。
-
上下文数据:保存进程执行时处理器寄存器中的数据,包括通用寄存器、指令指针等。上下文数据可以用来在进程之间进行切换时保存和恢复进程的执行状态。
-
I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和进程使用的文件列表等与I/O操作相关的信息。
-
记账信息:可能包括处理器时间总和、使用的时钟数总和、时间限制、记账号等与进程执行和资源使用统计相关的信息。
-
其他信息:task_struct结构体可能还包含其他与进程管理和控制相关的信息,如进程的父子关系、信号处理器等。
需要注意的是,具体的task_struct结构体的字段和实现方式可能因不同的操作系统和内核版本而有所差异。上述分类是基于一般情况下的常见字段进行的,并不包括所有可能的字段。
3.3 组织进程
内核中的所有运行中的进程都以task_struct结构体的链表形式组织在内核中。
在内核源代码中,每个进程都有一个对应的task_struct结构体,用于存储进程的所有相关信息。这些task_struct结构体通过指针链接在一起,形成一个链表,称为进程列表(Process List)或进程表(Process Table)。
进程列表是内核用于管理和跟踪所有进程的主要数据结构之一。它可以通过访问一个特定的指针,如init_task指针(在Linux中)或current指针(在某些其他系统中),找到链表中的第一个进程,然后通过遍历链表来访问和操作其他进程的task_struct结构体。
通过进程列表,内核可以高效地遍历和管理所有的进程。它可以根据进程的状态和优先级进行调度、分配CPU时间,以及处理进程的创建、销毁和切换等操作。进程列表的组织方式可以根据具体的操作系统和内核版本而有所不同,但通常会采用链表或其他形式的数据结构来表示。
需要注意的是,进程列表是在内核中维护的,用户无法直接访问和修改它。用户可以通过系统调用和相关接口来与内核交互,以实现对进程的创建、管理和控制。
3.4 查看进程
可以通过在Linux系统中访问/proc文件系统来查看进程的信息。/proc文件系统是一种特殊的文件系统,提供了对内核数据结构的访问接口,包括对进程信息的访问。
在/proc文件系统中,每个运行的进程都有一个对应的目录,目录的名称是进程的PID(进程标识符)。进入该进程的目录,可以查看和访问与该进程相关的各种信息。
以下是一些常见的/proc文件系统中进程相关的信息文件:
-
/proc//status:包含有关进程状态、命令行参数、运行时间等的信息。
-
/proc//cmdline:包含进程的完整命令行参数。
-
/proc//stat:包含有关进程的状态和统计信息,如进程状态、父进程ID、CPU使用情况等。
-
/proc//cwd:表示进程当前的工作目录。
-
/proc//fd:是一个目录,包含进程打开的文件描述符的符号链接。
通过访问这些文件,可以获取有关进程的各种信息。可以使用命令行工具如cat
、less
等,或者在编程中使用相关的文件操作函数来读取这些文件并解析其中的信息。
需要注意的是,/proc文件系统中的信息是实时的,并随着进程的状态和活动进行更新。每个进程的信息都可以在对应的目录中找到,通过读取这些文件可以获得进程的详细信息。
3.5 获取进程标示符
可以通过系统调用来获取当前进程的进程标识符(PID)和父进程标识符(PPID)。在Linux系统中,常用的系统调用是getpid()
和getppid()
。
- getpid():该系统调用用于获取当前进程的进程标识符(PID)。它返回当前进程的PID,可以通过调用该函数获取当前进程的PID值。
示例代码(C语言):
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid = getpid();
printf("My PID is: %d\n", pid);
return 0;
}
- getppid():该系统调用用于获取当前进程的父进程标识符(PPID)。它返回当前进程的父进程的PID值。
示例代码(C语言):
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t ppid = getppid();
printf("My parent's PID is: %d\n", ppid);
return 0;
}
这些系统调用可以在程序中调用,以获取当前进程的PID和PPID。请注意,系统调用的具体语法和用法可能因编程语言和操作系统而有所不同。以上示例代码仅为演示目的,实际使用时应根据编程语言和操作系统的要求进行适当的调用和处理。
3.6 创建进程-fork初识
fork
是一个系统调用,用于创建一个新的进程,这个新的进程称为子进程。子进程是父进程的副本,它继承了父进程的代码段、数据段和堆栈等内容。
通过运行man fork
命令可以查看fork
系统调用的详细说明。在Linux中,fork
系统调用的函数原型如下:
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
fork
系统调用的返回值有两个:
-
在父进程中,
fork
返回子进程的进程标识符(PID),这个PID大于0。 -
在子进程中,
fork
返回0。
如果fork
系统调用失败,则返回一个负数,表示创建新进程失败。
当fork
调用成功后,父进程和子进程会拥有相同的代码和程序状态,但它们各自拥有独立的内存空间。为了实现这种独立性,采用了写时拷贝(copy-on-write)技术。在初始阶段,父进程和子进程共享相同的内存页。只有当其中一个进程尝试修改这些页时,才会进行实际的复制。
需要注意的是,父进程和子进程在fork
之后继续执行代码的位置是不同的。在父进程中,fork
返回子进程的PID,父进程可以根据返回值判断自己是父进程。而在子进程中,fork
返回0,子进程可以根据返回值判断自己是子进程。父子进程可以根据返回值的不同来执行不同的代码逻辑。
通过使用fork
系统调用,可以创建多个并行执行的进程,从而实现并发执行和任务分配。
3.7 进程状态
在Linux内核源代码中,有一个名为"task_state_array"的数组,它用于表示进程的状态。这个数组是一个奇怪的“位图”,用于表示进程睡眠的原因。在这个数组中,进程的状态用数字表示,通过简单的位测试可以检查各个状态的组合。
以下是task_state_array数组中各个状态的含义及对应的表示:
0: R (running) - 进程正在运行
1: S (sleeping) - 进程正在睡眠
2: D (disk sleep) - 进程在进行磁盘访问时进入睡眠状态
4: T (stopped) - 进程被停止
8: t (tracing stop) - 进程处于被跟踪的停止状态
16: X (dead) - 进程已经终止
32: Z (zombie) - 进程成为僵尸进程
通过这个数组,可以根据进程的状态值查找对应的状态描述。每个状态都用一个字符表示,并在括号内提供了对应的描述信息。
需要注意的是,这个数组只是表示进程状态的一种方式,在内核源代码中可能还有其他的定义和表示方式。具体的进程状态定义和使用可能会因不同的内核版本和架构而有所不同。因此,查看特定版本的Linux内核源代码可以获得更准确和详细的进程状态定义和描述。
3.7.1 进程状态查看
ps aux
和ps axj
是两个常用的Linux命令,用于查看进程状态和进程信息。
-
ps aux
命令:ps
:用于报告当前进程的状态。aux
:是ps
命令的选项之一,表示显示所有用户的进程信息,包括僵尸进程。
执行
ps aux
命令会列出当前系统中所有进程的详细信息,包括进程ID(PID)、父进程ID(PPID)、CPU使用情况、内存占用等等。 -
ps axj
命令:ps
:用于报告当前进程的状态。axj
:是ps
命令的选项之一,表示显示所有进程及其父子关系的进程树,包括僵尸进程。
执行
ps axj
命令会以进程树的形式显示当前系统中所有进程及其父子关系。它会显示进程的PID、PPID、状态(STAT)、命令行参数等信息。
这些命令可以查看当前系统中运行的进程信息,并检查是否存在僵尸进程(以Z标识)。通过观察进程的状态和其他信息,可以更好地了解系统中的进程活动和资源使用情况。
3.7.2 僵尸进程
Z(zombie)-僵尸进程
- 僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲)
- 没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
- 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
- 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
僵尸进程的存在会带来以下一些危害和问题:
-
内存资源浪费:僵尸进程的存在意味着进程表中会保存其退出状态和其他信息,这需要占用一定的内存资源。如果父进程不及时回收僵尸进程,就会导致内存资源被浪费。
-
进程表满:如果大量的僵尸进程积累在进程表中而没有被回收,会导致进程表中的空间被占满。当进程表已满时,系统将无法创建新的进程,从而影响系统的正常运行。
-
内存泄漏:僵尸进程所占用的内存空间无法被释放,这被视为一种内存泄漏。虽然每个僵尸进程本身所占用的内存较小,但当数量增加时,内存泄漏问题可能会变得更加严重。
为了避免这些问题,父进程应该及时回收子进程的资源,避免产生大量的僵尸进程。父进程可以通过以下方法来处理僵尸进程:
-
使用
wait()
或waitpid()
等系统调用等待子进程的退出状态,并回收其资源。 -
使用
SIGCHLD
信号捕获子进程的退出状态,并在信号处理函数中调用相应的系统调用来回收子进程。 -
设置
SIGCHLD
信号为忽略或使用特定的处理方式,使操作系统自动回收僵尸进程。
以上方法都可以使父进程及时处理子进程的退出状态,避免产生僵尸进程和相关的资源浪费问题。
值得注意的是,僵尸进程并不会消耗CPU资源或造成其他直接的运行时问题,但过多的僵尸进程可能会对系统的稳定性和性能产生负面影响。因此,及时处理僵尸进程是良好的编程和系统管理实践。
3.8 孤儿进程
当父进程提前退出,而子进程尚未退出或成为僵尸进程时,子进程就会成为孤儿进程。孤儿进程没有父进程来回收它们的资源,这可能导致资源泄漏和其他问题。
在类Unix系统中,例如Linux,孤儿进程会被1号进程(也称为init进程)领养。init进程是系统启动时由内核创建的一个特殊进程,它会周期性地调用wait()
或waitpid()
等系统调用来回收孤儿进程的资源,防止它们变成僵尸进程。
init进程(或称为systemd进程)具有PID为1,是整个进程树的根。当父进程退出时,内核会将孤儿进程的父进程设置为init进程。这样,init进程就负责回收孤儿进程,并保证它们的资源得到正确释放。
通过init进程的回收,孤儿进程可以避免成为僵尸进程,并且释放其占用的系统资源。这种方式确保了系统中的孤儿进程不会长时间存在,从而维护了系统的稳定性和性能。
总结起来,当父进程提前退出时,子进程会成为孤儿进程,然后被1号init进程领养和回收,避免产生僵尸进程和资源泄漏。
3.9 进程优先级
3.9.1 基本概念
进程的优先级是指CPU资源分配的先后顺序,即决定了进程在多任务环境中获取CPU执行时间的权利。进程的优先级高低决定了它相对于其他进程的执行顺序和执行权重。
在Linux系统中,进程的优先级通常用一个数值来表示,被称为"nice"值。较低的nice值表示较高的优先级,而较高的nice值表示较低的优先级。nice值的范围通常是-20到+19,其中-20表示最高优先级,+19表示最低优先级。
进程优先级的设置和调整可以通过系统调用(如nice()
)来实现。较高优先级的进程将在调度时更频繁地获得CPU时间片,从而具有更高的执行优先权。通过适当地调整进程优先级,可以改善系统的性能和响应能力。
除了进程的优先级,Linux系统还提供了CPU绑定(CPU affinity)的功能。通过将进程绑定到特定的CPU上运行,可以实现将不重要的进程分配给特定的CPU,从而改善系统的整体性能。这种方式可以避免不必要的进程切换和资源竞争,提高系统的效率。
需要注意的是,进程优先级的调整需要谨慎使用。不合理的设置可能会导致系统资源不均衡、优先级倾斜等问题。在进行进程优先级调整时,应仔细评估系统的需求和资源分配情况,确保调整能够达到预期的性能改善效果。
3.9.2 查看系统进程
在linux或者unix系统中,用ps –l
命令则会类似输出以下几个内容:
我们很容易注意到其中的几个重要信息,有下:
- UID : 代表执行者的身份
- PID : 代表这个进程的代号
- PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
- PRI :代表这个进程可被执行的优先级,其值越小越早被执行
- NI :代表这个进程的nice值
3.9.3 PRI & NI
在Linux系统中,进程的优先级由PRI(Priority)值表示,PRI值越小表示优先级越高。而nice值(NI)是一个表示优先级修正的数值,范围为-20至19。当进程有一个较低的nice值(通常为负值),它的PRI值将会减小,从而获得较高的优先级,会更快地被CPU调度执行。
进程的优先级调整可以通过调整nice值来实现。可以使用系统调用(如nice()
或renice
命令)来为进程设置或修改nice值。通过调整nice值,进程的优先级会相应地增加或减小,从而影响进程在多任务环境中的调度顺序。
需要注意的是,nice值的范围是-20至19,其中-20表示最高优先级,19表示最低优先级。如果nice值为负数,则进程的优先级会提高,而如果nice值为正数,则优先级会降低。进程的默认nice值通常为0。
调整进程优先级是一种调优手段,可以用来平衡系统中不同进程的资源使用。然而,过度调整进程优先级可能会导致资源竞争和不稳定性问题,因此应该谨慎使用,并根据系统的实际情况进行调整。
进程的nice值和进程的优先级并不是完全相同的概念,但nice值的调整确实会影响进程的优先级变化。
进程的优先级是由PRI(Priority)值表示的,PRI值越小表示优先级越高。而nice值(NI)是一种修正因子,它会影响进程的PRI值,从而影响进程的优先级。
nice值可以看作是对进程优先级的修正数据。通过增加或减小nice值,可以调整进程的优先级,使其相对于其他进程具有更高或更低的优先级。较低的nice值会导致PRI值减小,从而提高进程的优先级;而较高的nice值会导致PRI值增加,降低进程的优先级。
因此,调整nice值可以被视为一种修正进程优先级的手段。nice值的调整可以使进程在多任务环境中得到更合适的调度顺序,以满足系统性能和资源分配的需求。
需要注意的是,虽然nice值和进程优先级不是严格对应的概念,但它们之间存在关联,并且通过调整nice值可以实现对进程优先级的影响。这种关联性和调整机制使得nice值成为调整进程优先级的一种常用方式。
3.9.4 其他概念
- 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级
- 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
- 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
- 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发
4、环境变量
4.1 基本概念
环境变量是在操作系统中用来指定操作系统运行环境的一些参数或配置信息。它们是全局性的,可在系统中的各个进程中访问和使用。
环境变量通常用于存储系统级别的配置信息、路径设置、库的搜索路径、语言设置、临时文件位置等。它们可以在不同的操作系统和应用程序之间共享和传递参数。通过设置和读取环境变量,可以在不修改源代码或重新编译的情况下,对系统行为和运行环境进行灵活的配置。
在编写C/C++代码时,环境变量可以用于指定库文件的搜索路径,例如LD_LIBRARY_PATH
环境变量可以用来指定动态库的搜索路径,使得编译器和链接器能够正确地找到所需的库文件。这样,在链接过程中,即使不知道库文件的具体位置,也可以成功链接并生成可执行程序。
环境变量的值通常是以键值对的形式存在,例如PATH=/usr/local/bin:/usr/bin:/bin
。可以通过操作系统提供的相关命令或API来设置和获取环境变量的值。
需要注意的是,环境变量是全局的,它们对整个系统和所有的进程都起作用。在使用环境变量时,需要小心管理和处理,确保它们的设置符合系统的要求,并避免潜在的安全问题。
4.2 常见环境变量
PATH
、HOME
和SHELL
是常见的环境变量,在Linux系统中具有特定的含义和用途。
-
PATH
:PATH
环境变量指定了系统在执行命令时搜索可执行文件的路径列表。当您在终端或命令行中输入一个命令时,系统会根据PATH
变量中定义的路径顺序依次搜索可执行文件,直到找到匹配的命令。如果命令文件位于PATH
中的某个路径下,您就可以直接使用该命令而不需要指定完整的路径。 -
HOME
:HOME
环境变量指定了当前用户的主工作目录(home directory)。当用户登录到Linux系统时,默认的工作目录就是HOME
目录。该目录通常包含用户的个人文件和配置信息。用户可以通过~
符号来表示自己的HOME
目录,例如~/Documents
表示用户的文档目录。 -
SHELL
:SHELL
环境变量指定了当前正在使用的Shell程序。Shell是一种命令行解释器,它接受用户的命令,并将其传递给操作系统执行。SHELL
变量的值通常是Shell程序的路径,例如/bin/bash
表示当前使用的是Bash Shell。通过SHELL
变量,可以知道当前Shell的类型和位置。
这些环境变量在系统中具有全局特性,并且对所有的进程都可见。它们可以通过在终端中使用echo $变量名
来查看其值,也可以通过修改相应的配置文件(如.bashrc
、/etc/profile
等)来设置或修改其值。
通过使用这些环境变量,可以方便地配置和定制系统的行为,以及为用户提供个性化的工作环境。
4.3 查看环境变量
在终端中,使用echo
命令加上环境变量的名称,例如:
echo $PATH
echo $HOME
echo $SHELL
这将打印出相应环境变量的值。
还可以使用printenv
命令或env
命令来查看所有环境变量的列表及其对应的值,如:
printenv
env
这将显示当前系统中所有的环境变量及其取值。
注意,环境变量是区分大小写的,所以请确保在查看环境变量时使用正确的大小写。
4.4 环境变量相关的命令
- echo: 显示某个环境变量值
- export: 设置一个新的环境变量
- env: 显示所有环境变量
- unset: 清除环境变量
- set: 显示本地定义的shell变量和环境变量
4.5 设置环境变量
Linux/Unix:
- 获取环境变量:
#include <stdlib.h> int main() { char* value = getenv("ENV_VARIABLE_NAME"); // 使用获取到的环境变量值进行操作 return 0; }
- 设置环境变量:
#include <stdlib.h> int main() { setenv("ENV_VARIABLE_NAME", "value", 1); // 设置环境变量名为ENV_VARIABLE_NAME,值为value return 0; }
4.6 环境变量的全局属性
环境变量通常具有全局属性,并且可以被子进程继承。
当一个进程创建子进程时,子进程会继承父进程的环境变量。这意味着子进程会获得与父进程相同的环境变量,并且可以在其执行过程中访问和使用这些变量。
继承环境变量的机制使得在父进程中设置的环境变量可以方便地传递给子进程,使得它们可以共享相同的配置信息和参数。这在实际开发中非常有用,特别是在构建复杂的应用程序或系统时。
需要注意的是,尽管子进程继承了父进程的环境变量,但子进程也可以对其进行修改或添加新的环境变量。子进程可以使用系统调用或编程语言提供的接口来修改环境变量的值,以满足其自身的需求。
总之,环境变量具有全局属性,并且在父子进程之间可以被继承和共享,这为进程间的信息传递和配置提供了一种便捷的机制。
5、程序地址空间
程序地址空间是指一个正在执行的程序在内存中所占用的地址范围。
在计算机中,每个正在运行的程序都有自己的地址空间,用于存储程序的代码、数据和堆栈等。程序地址空间通常是虚拟的,即程序认为自己拥有整个内存空间,而实际上,操作系统负责将程序的地址空间映射到物理内存上。
程序地址空间通常被分为几个不同的区域,包括:
- 代码段(Text Segment):用于存储程序的可执行指令(机器码)。
- 数据段(Data Segment):用于存储程序的全局变量和静态变量。
- 堆(Heap):用于动态分配的内存,如通过
malloc
或new
分配的内存。 - 栈(Stack):用于存储函数调用和局部变量等的内存区域。
每个区域在程序地址空间中都有自己的起始地址和大小。这些区域的大小和位置可以在程序加载和执行的过程中进行动态调整。
程序地址空间的划分和管理由操作系统负责。操作系统为每个正在运行的程序分配一块地址空间,并进行地址映射,以便程序可以访问所需的内存区域。操作系统还负责内存保护和权限管理,以确保不同程序之间的内存隔离和安全性。
程序地址空间的概念对于理解程序的内存管理、虚拟内存和进程间通信等方面非常重要。不同的操作系统和体系结构可能有不同的程序地址空间布局和管理方式。
5.1 进程地址空间
进程地址空间和程序地址空间是相关但不完全相同的概念。
程序地址空间是指一个程序在内存中所占用的地址范围,它是程序的虚拟地址空间,包含了程序的代码、数据和其他相关的存储区域。程序地址空间是编译链接时确定的,它定义了程序在内存中的布局和组织方式。
进程地址空间是指一个正在运行的进程在内存中所占用的地址范围。每个运行的进程都有自己独立的进程地址空间,用于存储进程的代码、数据、堆栈和其他相关信息。进程地址空间是在运行时由操作系统动态分配和管理的。
从关系上来说,程序地址空间是进程地址空间的一部分。当一个程序被加载到内存并运行时,操作系统为该程序分配一个进程地址空间,并将程序的代码、数据等加载到进程地址空间的相应区域中。因此,进程地址空间是程序地址空间在运行时的映射。
进程地址空间还包含了额外的区域,如堆和栈,用于动态分配内存和管理函数调用。这些区域在程序地址空间中可能没有明确的定义。
总结起来,程序地址空间是编译链接时定义的虚拟地址空间,而进程地址空间是程序在运行时实际占用的内存空间。进程地址空间是程序地址空间的运行时映射。