计算机:操作系统

进程和线程的区别

进程是资源分配调度的基本单位。操作系统为它分配了独立的内存空间

线程是cpu 调度 和 执行的最小单位,是进程内的执行单元。一个进程至少有一个线程,一个进程可以运行多个线程,这些线程共享同一块内存。

资源开销:

  • 进程:由于每个进程都有独立的内存空间创建和销毁进程的开销较大。进程间切换需要保存和恢复整个进程的状态,因此上下文切换的开销较高。
  • 线程:线程共享相同的内存空间创建和销毁线程的开销较小。线程间切换只需要保存和恢复少量的线程上下文,因此上下文切换的开销较小。

进程开销大:有独立地址空间、页表、资源,创建销毁都要重新分配 / 回收
线程开销小:共享进程资源,只需要独立栈和上下文,轻量得多

通信与同步:

  • 进程:由于进程间相互隔离,进程之间的通信需要使用一些特殊机制,如管道(半双工)、消息队列、共享内存等。
  • 线程:由于线程共享相同的内存空间,它们之间可以直接访问共享数据,线程间通信更加方便。

安全性:

  • 进程:由于进程间相互隔离,一个进程的崩溃不会直接影响其他进程的稳定性。
  • 线程:由于线程共享相同的内存空间,一个线程的错误可能会影响整个进程的稳定性。

并行和并发有什么区别

并行是指在同一时刻执行多个任务,这些任务可以同时进行. 例如,在多核 CPU 上,每个子任务都在不同的处理单元(如多个 CPU 核心)上执行,从而加速整体任务的完成。

并发是指在相同的时间段内(宏观上,同时)执行多个任务,但是微观上是交替执行的。例如,单核 CPU 上, 多个任务通过时间片轮转或事件驱动的方式,轮流占用处理器,从而实现在一个时间段内完成多个任务。它关注的是任务的逻辑上同时执行

并发提出的需求:
1、提高资源利用率,当任务因为I/O操作阻塞是,CPU可以切换到其他任务执行,充分利用处理器资源
2、提高用户体验

并行提出的需求:
1、计算速度更快,例如 图像处理 GPU

解释一下用户态和内核态

  1. 用户态和内核态的区别

用户态和内核态是操作系统为了 保护系统资源实现权限控制 而设计的两种不同的CPU 运行级别

  • 用户态:非特权模式,程序只能访问受限资源,无法直接操作硬件内核数据。所有普通应用程序都在此态运行。
  • 内核态:特权模式,程序可执行任何指令,直接访问硬件和内核核心。操作系统内核、驱动程序等在此态运行。
  1. 在什么场景下,会发生内核态和用户态的切换
  • 系统调用:当用户程序需要请求操作系统提供的服务时,会通过系统调用进入内核态。
  • 异常:当序执行过程中出现错误或异常情况时,CPU 会自动切换到内核态,以便操作系统能够处理这些异常
  • 中断:外部设备(如键盘、鼠标、磁盘等)产生的中断信号会使 CPU 从用户态切换到内核态。操作系统会处理这些中断,执行相应的中断处理程序,然后再将 CPU 切换回用户态。(中断信号->用户态到内核态—>中断处理)

中断是 CPU 执行过程中被紧急事件打断,转去处理中断服务程序,处理完再恢复执行的机制。

进程调度算法你了解多少

调度算法分为两类:非抢占式调度算法(一旦进程 / 线程开始运行,必须等它主动让出 CPU,别人才能上)、抢占式调度算法(由操作系统决定)

非抢占式

  • 先来先服务:按照请求的顺序进行调度。这种调度方式简单,但是能导致较长作业阻塞较短作业。(适合CPU繁忙型作业的系统,不适用I/O繁忙型作业系统)
    短作业等待时间可能很长,不利于短作业

  • 最短作业优先:优先选择估计运行时间最短的进程(该进程完后,从就绪队列中)来运行。但是如果一直有短作业到来,那么长作业永远得不到调度,造成长作业 “饥饿” 现象。
    长作业不利
    抢占式

  • 最短剩余时间优先:** 基于最短作业优先改进,按剩余运行时间的顺序进行调度。当一个新的作业到达时,其整个运行时间与当前进程的剩余时间作比较。如果新的进程需要的时间更少,则挂起当前进程,运行新的进程。否则新的进程等待。
    最短优先改进,依旧长作业不利

  • 时间片轮转:为每个进程分配一个时间片,进程轮流执行,时间片用完后切换到下一个进程。
    时间片长度很关键:时间片太短,导致过多的进程上下文切换,降低CPU效率;时间片太长,可能引起对短作业进程的响应时间变长。

  • 优先级调度:为每个进程分配一个优先级,按优先级进行调度。(可以分别设定抢占式和非抢占式)为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。

  • 多级反馈队列调度:时间片轮转调度算法和优先级调度算法的结合。将进程分为不同的优先级队列,每个队列有自己的调度算法。
    「多级」表示有多个队列,每个队列优先级从高到低,同时优先级越高时间片越短。
    「反馈」表示如果有新的进程加入优先级高的队列时,立刻停止当前正在运行的进程,转而去运行优先级高的队列;

    兼顾了长短作业,同时有较好的响应时间(用户能够快速得到反馈)

  • 高响应比调度算法
    高响应比优先调度算法主要是权衡了短作业和长作业。
    每次进行进程调度时,先计算「响应比优先级」,然后把「响应比优先级」最高的进程投入运行。
    优先权 = 等待时间 + 要求服务时间 要求服务时间 \text{优先权} = \frac{\text{等待时间} + \text{要求服务时间}}{\text{要求服务时间}}优先权=要求服务时间等待时间+要求服务时间
    从上面的公式,可以发现:

    1. 如果两个进程的「等待时间」相同时,「要求的服务时间」越短,「响应比」就越高,这样短作业的进程容易被选中运行;
    2. 如果两个进程「要求的服务时间」相同时,「等待时间」越长,「响应比」就越高,这就兼顾到了长作业进程,因为进程的响应比可以随时间等倍的增加而提高,当其等待时间足够长时,其响应比便可以升到很高,从而获得运行的机会;

进程间有哪些通信方式

每个进程的用户地址空间都是独立的,一般而言是不能互相访问的,但内核空间是每个进程都共享的,所以进程之间要通信必须通过内核。

管道分为匿名管道和命名管道

  1. (匿名)管道:是一种半双工的通信方式,数据只能单向流动而且只能在具有父子关系的进程间使用(特殊文件,只在内存中,不存在文件系统中,父子共享文件描述符,所以可以使用)。

  2. 命名管道:也是半双工的通信方式,但是它允许在不相关的进程间通信(会创建一个管道类型的文件)。

  3. 消息队列:允许进程发送和接收消息,而消息队列是保存在内核的消息链表,可以设定优先级。缺点:存在于内核之中,消息大小有限制,通信过程中存在用户态与内核态之间切换,并且数据拷贝开销(用户->内核,内核->用户)。

  4. 共享内存共享内存的机制,就是拿出一块虚拟地址空间来,映射到相同的物理内存中。这样这个进程写入的东西,另外一个进程马上就能看到了,都不需要拷贝来拷贝去,传来传去,大大提高了进程间通信的速度。共享内存是最快的进程通信方式。(解决用户态到内核态的开销,只是,建立共享内存需要内核态建立)
    用了共享内存通信方式,带来新的问题,那就是如果多个进程同时修改同一个共享内存,很有可能就冲突了

  5. 信号量:是一个整型计数器,可以用来控制多个进程对共享资源的访问,主要用于实现进程间的互斥与同步。防止一个进程正在访问共享资源时,其他进程也访问该资源。

  6. 信号:是一种异步通信机制,用于发送通知到进程,告知其发生了某种事件(例如,Ctrl+C 组合键、除0错误)。进程可以忽略、捕获该信号或执行默认操作。

  7. Socket 套接字:最初用于网络通信,但也可用于同一台主机上的进程间通信(如Unix Domain Socket)。

次要:

  1. 互斥锁:一种信号量,用于保护共享数据结构,防止多个进程同时访问。

  2. 条件变量:与互斥锁配合使用,用于进程间的同步,等待某些条件成立。

解释一下进程同步和互斥,以及如何实现进程同步和互斥

进程同步:解决的是“协作”问题。它是指多个并发进程之间为了完成共同的任务,需要协调它们的执行顺序。例如,进程A负责生产数据,进程B负责打印数据,那么B必须在A之后执行。同步就是用来保证这种“前驱后继”关系的机制。

进程互斥:解决的是“竞争”问题。当多个进程需要访问同一个共享资源(比如打印机、共享内存中的变量)时,互斥确保同一时刻只允许一个进程进入临界区(访问共享资源的代码段)访问该资源。这就好比电话亭,一个人进去使用了,其他人就必须在外面排队等待,保证了数据的一致性和正确性。

解决进程同步和互斥的问题有很多种方法:

互斥锁(Mutex):互斥锁是一种同步机制,用于实现互斥。每个共享资源都关联一个互斥锁,进程在访问该资源前需要先获取互斥锁,使用完后释放锁。只有获得锁的进程才能访问共享资源。

信号量:信号量是一个整型计数器,它表示系统中某种资源的数量。它会配合两个原子操作(PV操作)来使用:

  • P 操作:申请资源。它会检查信号量的值,如果值大于0(资源可用),则将其减1并继续执行;如果值等于0(资源不足),则进程被阻塞,进入等待队列。
  • V 操作:释放资源。它将信号量的值加1,如果此时有进程在等待队列中,则唤醒其中一个。

通过将信号量初始化为1,我们可以用它来实现互斥(即互斥锁的功能)。通过将信号量初始化为0,我们可以用它来实现同步(例如,确保进程B在P操作上等待,直到进程A执行V操作后才被唤醒)。

条件变量:与互斥锁配合,允许进程在某个条件不满足时阻塞等待,并在条件满足时被唤醒。


一些概念:

  • 临界区:访问共享资源的代码段。进入这个区域前需要先获取锁,退出临界区后释放该锁。这确保同一时间只有一个进程可以进入临界区。
  • 条件变量:条件变量用于在进程之间传递信息,以便它们在特定条件下等待或唤醒。通常与互斥锁一起使用,以确保等待和唤醒的操作在正确的时机执行。

互斥解决竞争问题,同步解决协作问题,而互斥是同步的特例,互斥锁属于同步机制。

什么是死锁,如何预防死锁?

死锁是系统中多个进程因争夺资源而造成的一种僵局。当每个进程都持有一定的资源并等待其他进程释放它们所需的资源时,如果这些资源都被其他进程占有且不释放,就导致了死锁。

死锁只有同时满足以下四个条件才会发生:

  • 互斥条件:同一时刻一个进程占用了某个资源时,其他进程无法同时占用该资源。
  • 请求保持条件:一个线程因为请求资源而阻塞的时候,不会释放自己的资源。
  • 不可剥夺条件:资源不能被强制性地从一个进程中剥夺,只能由持有者自愿释放。
  • 循环等待条件:多个进程之间形成一个循环等待资源的链,每个进程都在等待下一个进程释放所占有的资源。

避免死锁:通过破坏死锁的四个必要条件之一来预防死锁。

  • 破坏互斥条件:将资源改为共享方式(如只读文件),但有些资源天生互斥(如打印机),难以实现。
  • 破坏请求保持条件:采用一次性分配,即进程在运行前一次性申请所有需要的资源,若资源不足则等待,但会导致资源利用率低和饥饿。
  • 破坏不可剥夺条件:允许抢占,例如进程申请新资源失败时,系统强制释放其已占有的资源(如CPU寄存器),但实现复杂且可能造成数据不一致。
  • 破坏循环等待资源有序分配法,给所有资源编号,进程必须按编号递增顺序申请资源,这样就不会形成循环链。

检测死锁:通过检测系统中的资源分配情况来判断是否存在死锁。例如,可以使用资源分配图或银行家算法进行检测。

解除死锁:例如,可以通过抢占资源、终止某些进程等方式来解除死锁。

检测死锁:
资源分配图:用有向图表示进程和资源的关系,如果图中存在环路且环路中的资源均只被一个进程占有,则可能发生死锁。
死锁检测算法:类似于银行家算法,通过查找可以完成的进程来简化图,若最终所有节点无法被消除,则存在死锁。
检测时机:可定时触发,或当CPU利用率骤降时触发。

银行家算法:1、每个进程事先声明最大资源需求。2、系统在分配资源时,计算如果满足此次请求,是否还存在一个安全序列(即所有进程都能按某个顺序完成)。3、只有处于安全状态时才分配资源,否则让进程等待。

死锁解除:
终止进程:一次性终止所有死锁进程,或逐个终止直到死锁解除。选择终止的进程通常考虑优先级、运行时间、已使用资源等因素。
资源抢占:从某些进程中强制剥夺资源给其他进程,但需要回滚被剥夺的进程到安全状态,以防止数据不一致。
系统重启:简单粗暴,但代价高。


介绍一下几种典型的锁

  • 互斥锁
    定义: 互斥锁是最基本的锁,确保同一时刻只有一个线程能访问被保护的临界区。

    工作原理: 每个共享资源都关联一个互斥锁,线程在进入临界区前必须获取锁,若锁已被其他线程持有,则当前线程会被阻塞(释放CPU,进入睡眠状态),由操作系统在锁释放时唤醒它。这个过程涉及两次上下文切换,开销较大。

    适应场景: 适合保护执行时间较长的临界区代码(被锁住的代码)(否则上下文切换比执行时间更长,得不偿失)。

  • 自旋锁
    定义:自旋锁采用忙等待机制,线程在获取锁失败时会持续循环检查锁状态,而不释放CPU。
    工作原理: 当锁被其他线程持有时,线程会忙等待,即不断循环检查锁是否可用,不会主动释放CPU。它在用户态完成锁的获取,避免了上下文切换,但会一直占用CPU。
    适应场景:自旋锁适合保护那些执行时间非常短的临界区代码(被锁住的代码),避免cpu被无效占有(比如只修改一个变量。在多核系统中,如果锁持有时间很短,自旋锁效率更高。)


其他的锁都是基于这两个锁的

  • 读写锁
    定义:读写锁将共享资源的访问分为读和写两种模式。读锁是共享的,允许多个线程同时读;写锁是排他的,只允许一个线程写。

    工作原理:当没有写锁时,多个读锁可以同时获得;写锁必须等待所有读锁和写锁释放后才能获得。读写锁可以用互斥锁和条件变量实现,也可用自旋锁实现。
    适应场景:读操作远多于写操作的场景,能显著提升并发性能。

  • 悲观锁
    定义:悲观锁认为多线程同时修改共享资源的概率比较高,所以访问共享资源时候要上锁,确保数据安全。
    典型实现:互斥锁、自旋锁、读写锁都是悲观锁

  • 乐观锁
    定义:乐观锁认为多线程同时修改共享资源的概率比较低,因此不加锁直接操作,但在更新时会检查数据是否被其他线程修改过,若冲突则重试或放弃。

    典型实现: Git、在线文档编辑 冲突概率极低的场景,可避免加锁开销,但冲突重试成本较高。


讲一讲你理解的虚拟内存

虚拟内存是一种内存管理技术,它为每个进程提供一个独立的、连续的虚拟地址空间,它不是真实存在的,而是通过映射与实际物理地址空间对应,这样就可以使每个进程看起来都有自己独立的连续地址空间,并允许程序访问比物理内存 更大的地址空间,每个程序都可以认为它拥有足够的内存来运行。

需要虚拟内存的原因:

  • 内存扩展:程序可以使用的内存空间可以超过物理内存的容量。当物理内存不足时,操作系统会把暂时不用的数据页换出到磁盘,需要时再换入,从而让程序“感觉”拥有巨大的内存。

  • 内存隔离与保护:每个进程运行在独立的虚拟地址空间中,一个进程无法直接访问另一个进程的内存,这防止了恶意或错误的程序破坏其他进程的数据。(同时,页表项中可以设置读写权限,实现代码段只读、数据段可写等保护机制。)

  • 简化内存管理:对程序员而言,他们只需要关心连续的虚拟地址,而不用操心物理内存的碎片化、分配和回收等复杂问题。操作系统可以更灵活地管理物理内存,例如将不连续的物理页组合成连续的虚拟空间。

  • 内存映射文件:虚拟内存还可以用于将文件映射到内存中,这使得文件的读取和写入可以像访问内存一样高效。(通过页表)

内存映射文件 = 把磁盘上的文件,直接映射到进程的虚拟地址空间。
以后读写文件,不用 read/write,直接用指针访问内存就行

  • 物理内存管理:虚拟内存允许操作系统动态地将数据和程序的部分加载到物理内存中,以满足当前正在运行的进程的需求。当物理内存不足时,操作系统可以将不常用的数据或程序暂时移到硬盘上,从而释放内存,以便其他进程使用。(包含在内存拓展之中)

线程同步的方式

线程同步机制是指在多线程编程中,为了保证线程之间的互不干扰,协调它们之间的执行顺序。常见的线程同步机制有以下几种:

  1. 互斥锁每个共享资源都关联一个互斥锁,线程在访问该资源前需要先获取互斥锁,使用完后释放锁。只有获得锁的线程才能访问共享资源。可以回答:适合场景
  2. 条件变量:条件变量用于线程间通信,允许一个线程等待某个条件满足,而其他线程可以发出信号通知等待线程。通常与互斥锁一起使用。
  3. 读写锁:读写锁将共享资源的访问分为读和写两种模式。读锁是共享的,允许多个线程同时读;写锁是排他的,只允许一个线程写。
  4. 信号量:是一个整型计数器,支持两个原子操作P、V操作,用于控制多个线程对共享资源进行访问的工具。

常见的(内存)页面置换算法

页面置换算法是: 当出现缺页异常,需调入新页面而内存已满时,选择被置换的物理页面。也就是说选择一个物理页面换出到磁盘,然后把需要访问的页面换入到物理页。

算法的目标是,尽可能减少页面的换入换出的次数。(页面的换入和换出,由于磁盘速度很慢,I/O 会阻塞进程,效率降低)

常见页面置换算法有最佳置换算法(OPT)、先进先出(FIFO)、最近最久未使用算法(LRU)、时钟算法(Clock)等。

  • 最佳置换算法:置换在「未来」最长时间不访问的页面。但是实际系统中无法实现,因为程序访问页面时是动态的,我们是无法预知每个页面在「下一次」访问前的等待时间。
  • 先进先出(FIFO)置换算法:也就是最先进入内存的页面最先被置换出去(内存驻留时间最长的页面)。
  • 最近最久未使用置换算法(LRU):发生缺页时,选择最长时间未被使用的页面进行置换。(性能比FIFO高)
  • 时钟算法(CLOCK):Clock 算法的核心思想是通过使用一个指针 (称为时钟指针) 在环形链表上遍历检查页面是否被访问过,当需要进行页面置换时,Clock 算法从时钟指针的位置开始遍历环形链表。如果当前页面的访问位为 0,表示该页面最久未被访问,可以选择进行置换。将访问位设置为 1,继续遍历下一个页面。如果当前页面的访问位为 1,表示该页面最近被访问过,它仍然处于活跃状态。将访问位设置为 0,并继续遍历下一个页面如果遍历过程中找到一个访问位为 0 的页面,那么选择该页面进行置换。
  • 最不常用(LFU):当发生缺页时,考虑页面的访问频率,淘汰访问次数最少的页面。

熟悉哪些Linux命令

  1. 文件操作
  • ls:列出当前目录内容。
  • cd:进入指定目录。
  • pwd:打印当前路径。
  • cp:复制文件或目录到另一个文件、目录。
    cp 源文件 目标文件
    目标文件不存在:复制源文件并改为目标文件名
    目标文件存在且是目录:复制到目标文件夹下
    目标文件存在且不是目录,会覆盖该目标文件(名不变),保留源
  • mv:移动或重命名文件。
    mv 源文件名 目标文件
    目标文件不存在:直接改名为目标文件
    目标文件存在且是目录:移动到目标文件夹下,
    目标文件存在且不是目录:会覆盖该目标文件(名不变),删除源。
  • rm:删除文件或目录。
  • touch:创建空文件或更新文件时间戳。(不能创建目录)
  • file:查看文件的描述信息
  • where(非系统标配,等价于which -a 列出所有路径)which($PATH中可执行路径) 查找命令的执行路径
  • whereis查找执行路径,以及相关二进制文件
  1. 文件内容查看
  • cat:查看文件内容。
  • head:查看文件的前几行。
    head --lines=数量 文件名
  • tail:查看文件的后几行,常用于查看日志文件。
    tail – lines=数量 文件名
  • less:翻页方向 上下均可
  • more翻页方向 向下
  • grep查找文件、内容符合条件的部分
  1. 文件编辑
  • vivim:强大的文本编辑器
  • nano:文本编辑器(易上手,有快捷键提示)
  1. 权限管理
  • chmod:更改文件或目录的访问权限。
  • chown:更改文件或目录的所有者和 / 或所属组。
  1. 磁盘管理
  • df:查看磁盘空间使用情况。
  1. 网络管理
  • ifconfigip addr:查看和配置网络接口。
  • ping:测试网络连接。
  • netstat:查看网络状态和统计信息。
  • ssh:安全远程登录。
  1. 进程管理
  • ps:查看当前运行的进程。(静态, ps aux 可以查看所有进程以及详细信息)
  • top:实时查看当前运行的进程(所有且详细)
  • kill:杀掉某进程。
  1. 软件包管理(根据 Linux 发行版不同,命令可能有所不同)
  • apt-get(Debian/Ubuntu):安装、更新和删除软件。

  • echo打印

Linux中如何查看一个进程,如何杀死一个进程,如何查看某个端口有没有被占用

1. 查看进程

ps命令查看当前运行的进程,比如ps aux可以列出所有进程及其详细信息,但它显示的是静态的列表。top 可以实时查看当前运行的进程的详细信息

2. 杀死进程

首先用pstop命令找到进程的 PID(进程 ID)。

然后用kill命令加上进程 ID 来结束进程,例如kill -9 PID"-9"是强制杀死进程的信号。默认的kill PID等价于kill -15让进程自己清理资源后退出。

3. 查看端口占用

使用lsof -i:端口号可以查看占用特定端口的进程(没有输出表示端口空闲)。或者用netstat -tulnp | grep 端口号,这会显示监听在该端口的服务及其进程 ID。

注意:|是匿名管道,将前一个进程的信息给后一个进程信息,需要两者有父子关系,半双工通信。

说一下 select、poll、epoll

I/O 多路复用,允许单个线程同时监控多个文件描述符(FD)。当某个 文件描述符 就绪(可读、可写或异常)时,系统调用会返回,通知应用程序进行后续处理。

解决的痛点:

  • 对比阻塞 I/O:避免了为每个连接创建一个线程的巨大资源开销(内存、上下文切换)。
  • 对比非阻塞轮询:避免了用户态不断发起系统调用去检查状态,极大降低了 CPU 消耗。

selectpollepoll等系统调用是用来实现I/O 多路复用

  • selectselect是一个最早的 I/O 多路复用实现方案,它可以监视多个文件描述符的可读、可写和错误状态。查询时需要将所有文件描述符(FD)集合从用户态拷贝到内核态,由内核进行线性轮询
    缺点:

    1. 数量限制:使用固定大小的位数组,文件描述符通常最大为1024
    2. 效率低下:每次调用都要全量拷贝 文件描述符 集合(用户态到内核态,内核态到用户态),(O(n) )遍历开销随 文件描述符 数量增加而线性增长。
    3. 重复设置:每次调用返回后,原有的 文件描述符 集合会被修改(就绪的为1),下次调用需重新初始化(需要监控的设置为1)。
  • pollpollselect的一种改进,避免了select中文件描述符数量有限的问题。(改用了基于链表的pollfd结构)
    缺点:依然存在用户态到内核态的全量拷贝内核态的线性轮询,对于大量的文件描述符性能依然不够高效。

  • epollepoll是 Linux 特有的 I/O 多路复用机制,相较于selectpollepoll使用事件通知的方式,只有在文件描述符就绪时内核才会通知应用程序,而且不需要应用程序轮询
    无重复拷贝:文件描述符 注册后常驻内核,只有在发生变化时才操作,减少了数据拷贝开销。

这个操作是:主动将这个就绪的 fd 添加到内核维护的“就绪链表”中


总结

select是最早的 I/O 多路复用技术,但受到文件描述符数量和效率方面的限制。poll克服了文件描述符数量的限制,但仍然存在一定的效率问题。epoll是一种高效的 I/O 多路复用技术,尤其适用于高并发场景,但它仅在 Linux 平台上可用。一般来说,epoll的效率要比selectpoll高,但是对于活动连接较多的时候,由于回调函数触发的很频繁,其效率不一定比selectpoll高。所以epoll在连接数量很多,但活动连接较小的情况性能体现的比较明显。

文件描述符(File Descriptor,简称FD)是操作系统(尤其是类Unix系统如Linux、macOS)中用于表示打开的文件、网络连接、管道等I/O资源的抽象概念。它是一个非负整数,类似于一个“索引”,进程通过它来访问和管理这些资源。
本质:文件描述符是一个小的非负整数,由内核在进程打开文件时分配。进程后续的读写操作都通过这个整数进行,内核根据它找到对应的文件对象。