进程概念学习

学习目标

认识冯诺依曼体系结构

理解操作系统概念

深入理解进程概念,了解PCB

理解进程状态,掌握僵尸进程和孤儿进程

了解进程调度,进程优先级

理解进程竞争性、独立性、并行、并发

理解环境变量,熟悉常见环境变量及相关指令

理解虚拟地址空间

冯诺依曼体系结构

冯·诺依曼体系结构是一种计算机系统的基本组织结构,也被称为存储程序计算机

主要组成

输入单元:包括键盘, 鼠标,扫描仪, 写板等

存储器:将指令和数据都存储在计算机的存储器中,以二进制形式表示,这意味着计算机可以像执行数据操作一样执行指令操作,使得程序能够被存储、读取和修改,从而实现灵活的编程。

中央处理器(CPU):运算器用于执行算术和逻辑运算,控制器用于解码和执行存储器中的指令序列

输出单元:包括显示器,打印机等

注意:这里的存储器指的是内存(RAM),数据的传输都需要经过存储器(内存),提高操作系统的效率,降低了计算机成本

操作系统

操作系统( Operating System)包含了计算机基本的程序,是计算机的一种管理软件,负责管理计算机软件和硬件资源,并为应用程序提供运行环境,达到隐藏底层硬件的复杂性,为应用程序提供一个统一的、易于使用的环境的目的,提高系统的性能、可靠性和安全性。

1、组成
  • 内核(Kernel)

内核是操作系统的核心部分,它负责管理和控制计算机硬件资源,提供各种系统服务和功能。内核操作是操作系统最底层的软件层,直接与硬件交互,它控制和调度CPU、内存、设备驱动程序等,同时处理中断、系统调用和进程间通信等核心任务。执行关键的操作和管理任务,如进程管理、内存管理、文件管理、设备驱动程序等。内核提供了一个抽象层,使得应用程序可以通过系统调用来访问底层硬件和系统功能。

  • 其他程序

其他程序提供各种上层功能和服务,为用户提供各种实用工具、图形界面、网络服务,如函数库,shell程序等

2、计算机体系架构中的定位

承上启下

3、如何进行管理
  • 先描述,再组织

描述起来,用struct结构体

组织起来,用链表或其他高效的数据结构

4、系统调用

系统调用是内核提供给应用程序的接口,用于访问操作系统的各种功能和服务。应用程序通过调用系统调用来请求操作系统执行特定的操作,例如创建进程、读写文件、网络通信等。

  • 区分理解库函数

系统调用和函数库都是应用程序与操作系统之间的接口,但它们的作用和机制有所不同。系统调用提供了访问操作系统底层功能的接口,涉及到特权级切换和内核代码的执行;而函数库则是一组常用功能函数的集合,对系统调用的基础上再进行封装,以函数调用的方式提供给应用程序,不需要特权级切换和内核代码执行。

注意:不同平台操作系统的系统调用接口可能不一样,但一般用户操作接口是一样的,例如c语言等

进程

进程(process)是指在操作系统中运行的一个程序的实例。每个进程都是独立的、具有自己的内存空间和执行环境。进程是操作系统进行任务调度和资源管理的基本单位(实体)。

进程=内核结构+数据和代码

1、OS对进程的管理

1)描述进程
  • PCB(Process Control Block)

每个进程都有一个与之相关的数据结构,称为进程控制块(PCB)。PCB存储了进程的属性,包括进程的状态、优先级、程序计数器、寄存器值等,可以理解为是进程属性的集合。Linux操作系统下的PCB是: task_struct。

  • task_struct内容

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

进程状态: 进程可以处于不同的状态,如运行状态、阻塞状态等。操作系统根据进程的状态进行调度和管理。

优先级: 相对于其他进程的优先级。

程序计数器: 程序中即将被执行的下一条指令的地址。

内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针

上下文数据: 进程执行时处理器的寄存器中的数据

寄存器可以分为可见寄存器(Visible Registers)和不可见寄存器(Invisible Registers)

I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。

记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。

其他信息

2)组织进程

在Linux系统中,进程的task_struct结构体通过双向链表(通常称为进程链表或任务链表)连接起来,用于管理和组织进程。这个链表是内核中维护的数据结构,每个进程都有一个对应的task_struct结构体,通过链表连接起来形成一个进程链表。

2、常见系统调用

1)创建进程
  • ./程序名

运行相应的程序,产生进程

  • fork()

创建一个新的子进程,使得当前进程(父进程)复制自身,形成一个新的进程(子进程)

fork有两个返回值

一般来说父子进程代码共享,数据各自开辟空间(采用写时拷贝)

int main()
{
    int ret = fork();
    if(ret < 0){
        perror("fork");
        return 1;
    }
    else if(ret == 0){ 
        printf("I am child : %d!, ret: %d\n", getpid(), ret);    }
    else{ 
        printf("I am father : %d!, ret: %d\n", getpid(), ret);    }
    sleep(1);
    return 0;
}
  • 指令

指令也是程序,也会产生进程,但不需要指定路径,因为环境变量PATH已经为指令定义了路径

几乎在命令行执行的指令都是bash进程的子进程

2)查看进程
  • ps axj/aux/-l

UID : 代表执行者的身份

PRI :代表这个进程可被执行的优先级,其值越小越早被执行

NI :代表这个进程的nice值

注意PRI和NI下面优先级会再次讲述

while : ; do ps axj | head -1 && ps axj | grep test | grep -v grep; sleep 2 ; done
    //使用该命令可以持续显示test进程
  • ls /proc

3)其他常见的进程系统调用

exec():用于在当前进程中执行一个新的程序,替换当前进程的内容。

wait()和waitpid():用于等待子进程的结束,并获取子进程的退出状态。

exit():用于进程的正常终止,同时会将退出状态传递给父进程。

getpid()和getppid():分别用于获取当前进程的进程PID和父进程的PID。

kill():向指定进程发送一个信号,可以用于终止进程或通知进程进行特定操作,如-9终止、-18发送sigstop、19发送sigcont

signal()和sigaction():用于注册和处理信号的处理函数,处理进程收到的特定信号。

sleep():使当前进程进入睡眠状态,暂停一定的时间。

pipe():创建一个管道,用于实现进程间的通信。

mmap():将一个文件映射到进程的内存空间,实现文件的内存映射。

3、进程的状态

在操作系统中,进程可以处于不同的状态,以反映其当前的执行情况和资源使用情况

1)操作系统下的进程状态

运行态(Running):进程正在执行或者已经准备好被执行

阻塞态(Blocked):进程由于等待某个事件(如等待输入、等待网络数据等)的发生而暂时停止执行,直到事件发生才能继续执行。

终止态(Terminated):进程执行结束或被终止,等待被操作系统回收资源。

挂起态(Suspended):内存资源不足时,暂时停止执行,并且不占用 CPU 资源,进程的状态和资源都会被保存(放回swap分区的磁盘),以便在解除挂起后能够继续执行

2)Linux操作系统下的进程状态

R运行状态(Running): 进程正在执行或者已经准备好被执行--->运行状态

S睡眠状态(Sleeping): 进程在等待事件完成(也叫可中断睡眠(interruptible sleep))----->阻塞状态

D磁盘休眠状态(Disk sleep):进程通常会等待IO的结束,等待的是磁盘资源(也叫不可中断睡眠状态(uninterruptible sleep))----->阻塞状态

T停止状态(Stopped): 可以通过发送 SIGSTOP 信号给进程来停止进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。----->阻塞状态/挂起

t追踪状态(Tracing stop):表示进程被其他进程跟踪,如遇到断点

死亡状态(Dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

  • Z僵尸进程(Zombies)

表示子进程已经执行结束,但其相关的进程描述符(Process Descriptor)仍然存在,以便父进程查询子进程的退出状态。当父进程查询到子进程的退出状态后,操作系统将释放该子进程所占用的系统资源。

bash对命令行程序进程的回收

危害

如果父进程没有及时处理子进程的退出状态(进程等待),会导致内存泄漏

  • 孤儿进程(Orphan)

当一个进程的父进程结束或意外终止时,该进程会成为孤儿进程,即失去了父进程。孤儿进程会被操作系统接管,并将其ppid设置为1(通常是init进程)。这样,孤儿进程将由init进程接管并负责清理资源,确保进程的正常退出。

在linux进程状态后会有+符号,表示前台进程,能给使用ctrl c来终止,而孤儿进程状态只有S属于后台进程

4、进程优先级

进程优先级是操作系统调度算法中用于确定进程执行顺序的重要指标。每个进程都有一个优先级值(Priority),该值表示了进程在竞争CPU资源时的优先级高低。操作系统根据进程的优先级来分配CPU时间,以确保高优先级进程能够更频繁地获得执行机会。

1)PRI和NI

PRI(Priority)是进程的优先级值,用于指示进程在竞争CPU资源时的相对重要性或紧急程度。PRI值越低,表示进程的优先级越高。在一些操作系统中,PRI值的范围通常是0到99,其中0表示最高优先级。

NI(Nice Value)是一个可调整的值,用于调整进程的优先级。较高的NI值表示进程更加"善"(nice)或低优先级,而较低的NI值表示进程较不友善或高优先级。在一些操作系统中,NI值的范围通常是-20到+19,其中-20表示最高优先级。

通过增加或减少NI值,可以相应地增加或减少进程的优先级。这种方式可以用于对进程进行动态的优先级调整,以满足不同的系统负载、用户需求或资源分配策略。注意每次修改时,PRI都会先恢复到默认值

2)查看优先级指令
  • top指令

在top界面中,可以看到各个进程的相关信息,包括PID(进程ID)、USER(进程所有者)、PR(优先级)、NI(Nice值)等。

  • PR列显示的是进程的实际优先级值,较低的数字表示较高的优先级。

  • NI列显示的是进程的Nice值,较低的数字表示较高的优先级。

  • 修改NI:按r–>输入进程PID–>输入nice值

请注意,top命令会实时刷新进程信息,因此可以随时观察到进程的优先级变化。同时,top命令还提供了其他功能,如查看CPU、内存、系统负载等信息,可根据需要进行探索和使用。

3)CFS (Completely Fair Scheduler) 调度器

CFS 调度器的设计目标是实现公平的进程调度,即在多任务环境下,尽可能地使每个进程获得相等的 CPU 时间片。为了实现这个目标,CFS 调度器采用了一种基于红黑树的进程队列数据结构,其中每个进程的调度优先级是通过动态计算的虚拟运行时间来确定的。较短虚拟运行时间的进程被认为是更需要 CPU 时间的,因此它们的调度优先级更高。

CFS 调度器使用最小化进程的虚拟运行时间作为调度策略,这意味着每个进程在单位时间内都能获得相等的运行时间。通过不断调整进程的调度优先级和时间片大小,CFS 调度器可以在尽量保持公平性的同时,充分利用系统资源,提供良好的响应性和可扩展性。

5、进程属性

1)竞争性

进程之间可能存在资源竞争的情况。多个进程可以竞争共享的资源,如CPU时间(时间片)、内存、磁盘等。竞争性是操作系统中一个重要的概念,操作系统需要通过调度算法资源管理机制来协调和控制进程之间的资源竞争,确保公平性和效率

2)独立性

每个进程在逻辑上都是独立的实体,它们拥有自己的内存空间、寄存器、文件描述符等。进程之间的执行互不干扰,一个进程的错误或异常不会直接影响其他进程的正常运行。

3)并行

当多个进程在多个处理器上同时执行时,它们可以以并行的方式执行,提高系统的处理能力和响应性。

4)并发

指多个进程按照某种交替方式执行的能力。即使在单个处理器上,多个进程也可以通过操作系统的调度机制轮流执行,从而实现同时运行的效果。并发性使得多个任务能够共享计算机资源,并在同一时间内进行交替执行

  • 交替方式实现原理

调度器会根据调度算法和优先级规则(抢占),决定进程获得CPU的执行时间(时间片),让多个进程在一段时间内得到推进,在交替执行中,PCB会保存上下文数据,以便继续执行时能够恢复数据

环境变量

环境变量(environment variables)是一种用于存储系统和用户相关信息的动态变量,如系统路径、默认编辑器、语言设置等。它们提供了一种简单而灵活的方式来配置系统和应用程序的行为。

当进程启动时,它会继承父进程的环境变量,并可以添加、修改或删除自己的环境变量。具有全局特性。

1、常见的环境变量

PATH:指定可执行文件的搜索路径。

HOME:指定当前用户的主目录路径。大多数应用程序将在这个路径下查找用户的配置文件。

USER:指定当前登录用户的用户名。

SHELL:指定当前用户使用的默认shell程序。通常是/bin/bash

PWD:指定当前工作目录的路径。

TMPTEMP:指定临时文件的存储路径。

LD_LIBRARY_PATH:指定动态链接库的搜索路径。系统会在这些路径中查找要加载的动态链接库文件。

DISPLAY:指定图形界面的显示设备。

HISTSIZE:指定记录历史指令行数。

2、相关的指令

env:显示当前系统的所有环境变量及其值。

export:设置一个环境变量,使其在当前会话中可用。

  • 例如:export VARNAME=value

unset:取消设置一个环境变量,使其不再可用。

  • 例如:unset VARNAME

echo:显示指定环境变量的值。

  • 例如:echo $VARNAME,显示名为VARNAME的环境变量的值。

set:显示当前系统中已经设置的所有环境变量和其他一些系统变量。

3、内存结构

每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以’\0’结尾的环境字符串

4、获取环境变量

1)命令行第三个参数
int main(int argc, char *argv[],char *env[])//main函数其实有三个命令行参数,分别对应命令行的数量,存储命令行内容,其中argv[0]是程序本身的名称,环境表
{
    int i = 0;
    for(; env[i]; i++){   //环境表的末尾是NULL
        printf("%s\n", env[i]);
    }
    return 0;
}
2)通过第三方变量environ获取
int main(int argc, char *argv[])
{
    extern char **environ;    //char *[]    extern声明外部变量、函数
    int i = 0;
    for(; environ[i]; i++){
        printf("%s\n", environ[i]);    }
    return 0;
}
3)通过系统调用获取或设置环境变量

putenv()

getenv()

常用getenv和putenv函数来访问特定的环境变量

int main()
{
    printf("%s\n", getenv("PATH"));    return 0;
}

5、全局特性

环境变量的全局属性指的是在整个操作系统中都可访问和使用的属性。可以被子进程继承下去,这意味着无论在哪个终端窗口、哪个用户登录会话中,都可以访问和使用相同的环境变量。

注意

bash是命令行进程,指令是其子进程,但一些指令仍可以执行普通变量的操作,其原因是一些指令属于内建命名,如ls,cd等

程序地址空间

程序地址空间是指一个进程在内存中所占据的地址范围。每个运行的程序都有自己的地址空间,用于存储程序的代码、数据和堆栈等信息。

虚拟地址空间为访问内存增加了软硬件层,对访问过程进行审核,拦截非法访问,维护内存安全,实现linux内存管理和进程管理的解耦,以统一的方式编译和加载所有的可执行程序,分配地址,简化了进程的设计

1、程序地址空间划分

代码区(Text Segment):存储程序的指令代码。这部分区域通常是只读的,包含了可执行文件中的机器指令。

数据区(Data Segment):存储程序的全局变量和静态变量等数据。这部分区域可以分为初始化数据区(Initialized Data Segment)和未初始化数据区(Uninitialized Data Segment)。

  • 初始化数据区:存储已经初始化的全局变量和静态变量,其初始值在编译时确定。

  • 未初始化数据区:存储未初始化的全局变量和静态变量,其初始值默认为0。

堆栈区(Stack Segment):存储程序的函数调用和局部变量等信息。每个函数调用都会在堆栈中创建一个栈帧,用于存储函数的参数、局部变量和返回地址等。堆栈区是按照后进先出(LIFO)的顺序进行操作。

注意:

堆区向上增长,栈区向下增长,堆栈相对而生

函数内定义static变量,本质是编译器把该变量放进全局数据区

划分是通过start、end变量

程序地址空间不是物理内存空间,而是虚拟地址空间,这也解释了上面pid_t ret=fork()有不同值,实现了父子进程独享数据

2、页表

页表是一种树形的数据结构,用于在虚拟内存系统中将虚拟地址映射到物理地址。

每个进程都有自己的页表,用于管理其虚拟内存和物理内存之间的映射。

1)相关概念

页框(Page Frame):页框是物理内存中的固定大小的块(4KB),通常是以页面(Page)为单位进行划分。每个页框对应一个物理地址,用于存储数据或指令。

页帧(Page Frame Number):页帧是页表中用来表示物理页框的索引。

页表权限(Page Table Entry Permissions):页表中的每个页表项(Page Table Entry)包含了关于对应虚拟页的权限信息,如读、写、执行等。这些权限控制了对虚拟页的访问权限,保护了内存的安全性。

页目录(Page Directory):页目录下多级页表中均存储了指向下一级页表的指针。页目录中的每个表项指向一个页表,而每个页表负责一定范围的虚拟地址映射。通过多级的页目录和页表结构,可以根据虚拟地址的高位逐级查找页表项,最终找到对应的物理页框。

2)作用

通过页表,操作系统可以实现虚拟内存的抽象,可以按需创建,节省了地址空间,使内存管理和进程管理解耦,保护物理内存的安全,并优化进程的使用和管理。

3、进程—>虚拟地址空间—>物理内存

生成虚拟地址:进程通过指令或程序中的变量访问虚拟地址。虚拟地址由虚拟地址空间中的偏移量组成。

页表查询:内存管理单元(Memory Management Unit,MMU)根据虚拟地址中的页号查询进程的页表。

物理地址计算:通过页表查询,MMU获取了虚拟页对应的物理页框号。然后,将页框号与虚拟地址的偏移量组合,计算出物理地址。

访问物理内存:使用计算得到的物理地址,进程可以直接访问物理内存,读取或写入数据。

注意

在程序编译时,生成的地址是相对于进程的虚拟地址空间的偏移量,也就是相对于进程自己的地址空间的起始地址的差值。这个偏移量会在程序加载到内存并执行时,经过地址转换,映射到实际的物理地址。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值