操作系统知识点
I/O操作
I/O(Input/Output,输入/输出)是计算机系统中用于数据传输的机制,指的是在计算机和外部设备(如键盘、显示器、磁盘)之间,或在计算机内部组件(如内存和CPU)之间的数据传输过程。I/O操作包括输入操作(数据从外部设备读取到计算机)和输出操作(数据从计算机传输到外部设备)。
- 输入(Input):指将数据从外部设备(如键盘、鼠标、传感器等)传输到计算机内部(如内存、CPU)。例如,用户通过键盘输入字符到程序中。
- 输出(Output):指将数据从计算机内部传输到外部设备(如显示器、打印机、磁盘等)。例如,将处理后的数据输出到屏幕上显示。
I/O操作的分类
同步I/O(Synchronous I/O):
- I/O操作的调用者在操作完成前会被阻塞,等待数据传输完成后才能继续执行。适合一些简单的应用场景,但可能导致CPU等待I/O操作,造成资源浪费。
- 例如,读取文件时,程序会等待直到所有数据读取完成。
异步I/O(Asynchronous I/O)
- 调用者发起I/O请求后立即返回,不等待操作完成。I/O操作完成后,系统会通过回调或事件通知调用者。适合需要高并发处理的应用程序,可以更好地利用系统资源。
- 例如,网络服务器在接收请求时使用异步I/O来处理大量的并发连接。
网络I/O阻塞原因
网络/O会被阻塞是因为在进行网络数据传输时,操作系统在等待数据的发送或接收完成之前 会将进程挂起,直到数据传输完成后才恢复进程执行。
阻塞主要原因:
- 等待数据到达或发送完成:当进程尝试从网络套接字中读取数据时,如果数据尚未到达,操作系统会使进程进入阻塞状态,直到数据到达为止。同样,当数据未能立即发送出去时,发送操作也可能被阻塞,等待缓冲区有空闲空间。
- 系统资源有限:当系统资源(如网络缓冲区、连接数等)被占满时,进一步的I/O请求可能会被阻塞,等待资源释放后才能继续。
- 默认的阻塞行为:大多数网络API(如
recv
、send
、accept
等)在默认情况下都是阻塞的,即调用这些API时,如果条件不满足,会使调用者等待,直到I/O操作完成。
网络I/O阻塞的常见场景
连接的阻塞:
accept()
阻塞:服务器在调用accept()
时,会阻塞在这个调用上,直到有新的客户端连接请求到来。此时,服务器进程会等待客户端的连接,无法继续执行其他操作。connect()
阻塞:客户端在调用connect()
尝试连接服务器时,如果连接不能立即建立(如服务器响应慢或网络不通畅),connect()
会阻塞,直到连接成功或超时。
数据传输的阻塞:
recv()
和read()
阻塞:当使用这些函数从套接字中读取数据时,如果缓冲区中没有数据(如对方尚未发送数据),进程会被阻塞,直到数据到达。send()
和write()
阻塞:当缓冲区已满(如网络拥塞或对方接收缓慢),数据无法立即写入时,send()
或write()
会阻塞,等待缓冲区有空闲空间时再发送数据。
线程
线程是CPU调度的基本单位,属于进程,一个进程可以包含多个线程。线程共享进程的内存空间和资源(如文件句柄、数据段),但每个线程有自己独立的栈和寄存器。
线程上下文
线程上下文涉及下面三个部分:
- CPU寄存器:如程序计数器(PC)、栈指针(SP)、通用寄存器等,这些值被保存,以便下次切换回当前线程时能够恢复。
- 程序状态字(PSW):包括CPU的状态信息,比如中断使能、优先级等。
- 线程的内核栈和用户栈:这两个栈分别用于存储内核和用户空间的函数调用信息。
TCB(线程控制块)
TCB(Thread Control Block)是操作系统用来管理线程的一个数据结构,它包含了与线程执行相关的所有必要信息,确保线程能够在系统中被正确调度和执行。每个线程都有一个对应的TCB,操作系统通过TCB来管理和调度线程的执行。
TCB中的关键内容:
- 线程状态:就绪、运行、阻塞、终止等。操作系统通过线程状态来判断是否可以被执行或需要被挂起。
- 程序计数器:程序计数器保留线程当前执行指令的地址,标识线程的执行位置。线程切换时,程序计数器的值需要被保存和恢复。
- 寄存器内容:线程的寄存器值(如通用寄存器、栈指针等)需要在上下文切换时保存和恢复。它们包含了线程执行期间的局部变量、函数调用的返回地址等信息。
- 线程栈指针(Stack Pointer,SP):每个线程都有一个栈,用于存储函数调用信息(如局部变量、返回地址等)。栈指针指向当前栈顶,线程切换时,栈指针需要被保存和恢复
- 线程优先级:操作系统通常根据线程的优先级来决定哪个线程应该被调度执行。
- 线程标识符:每个线程都有一个唯一的标识符(线程ID),操作系统通过它来区分不同的线程。
- 线程的调度信息:包括线程的调度策略、时间片(如果采用时间片轮转调度)、期望的调度器等。这个信息帮助操作系统决定何时将线程调度到CPU上执行。
管道
什么是进程间通信?
每个进程各自有不同的地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程A把数据从用户空间拷到内核缓冲区,进程B再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信。
管道定义
- 管道是一种最基本的进程间的通信机制。把一个进程连接到另一个进程的一个数据流称为一个"管道",通常是用作把一个进程的输出通过管道连接到另一个进程的输入。
- 管道本质上是内核的一块缓存,内核维护了一块缓冲区与管道文件相关联,对管道文件的操作,被内核转换成对这块缓冲区内存的操作。
举例:
在shell
中执行命令,经常会将上一个命令的输出组为下一个命令的输入,由多个命令配合完成一件事情。而这就是通过管道来实现。|
这个竖线就是管道符号
|
|
ls
命令(其实也是一个进程)会把当前目录中的文件都列出来- 但它不会直接输出,而是把要输出到屏幕上的数据通过管道输出到
grep
这个进程中,作为**grep
这个进程的输入** - 然后这个进程对输入的信息进行筛选(grep的作用),把存在string的信息的字符串(以行为单位)打印在屏幕上。
参考:一文让你明白,什么是管道
进程
进程是资源分配的基本单位,每个进程都有自己独立的内存空间(代码段,数据段,堆栈等),可以看作是一个正在运行的程序实例。进程之间是相互独立的。
进程的状态
- 就绪状态:进程已经分配到必要的资源,具备执行条件,等待CPU调度程序分配处理器时间。一旦CPU空闲并选择该进程,就会从就绪状态进入运行状态。
- 运行状态:进程正在使用CPU执行指令。
- 阻塞状态:进程因为等待某种事件(如I/O操作完成、等待某个信号等)而暂时停止运行。处于阻塞状态的进程不能被CPU调度,必须等到所需事件发生后,才能转为就绪状态。
- 新建状态:进程正在创建但尚未进入就绪队列。操作系统为新进程分配资源(如内存),当装备就绪后,进程会进入就绪状态。
- 终止状态:进程执行完毕或因某种原因被终止,进入终止状态。操作系统会回收该进程所占用的资源。
进程状态转换的流程
进程在其生命周期中可能经历多次状态转换,常见的状态转换包括:
- 就绪->运行:当CPU调度到某个就绪进程时,该进程从就绪状态转变为运行状态。
- 运行->阻塞:如果进程需要等待I/O操作或某个事件(如等待文件输入),会从运行状态转为阻塞状态,放弃CPU的使用。
- 运行->就绪:操作系统采用时间片轮转调度时,如果进程的时间片用完但未完成任务,会回到就绪状态,等待下次调度。
- 阻塞->就绪:当阻塞进程等待的事件(如I/O完成)发生后,会从阻塞状态回到就绪状态。
僵尸进程与孤儿进程
- 僵尸进程(Zombie Process):进程已经终止,但其父进程未对其进行回收(调用wait函数)。僵尸进程占用系统的进程表项,但不再消耗其他资源。操作系统会等待其父进程来获取它的终止状态信息,清除僵尸进程。
- 孤儿进程(Orphan Process):父进程提前终止,子进程继续运行,成为孤儿进程。操作系统会将孤儿进程托管给
init
进程(Linux系统中的PID为1的进程),由init
进程来收养并清理这些孤儿进程。
面试题
线程切换
操作系统在进行线程切换时需要进行哪些动作?
主要动作如下:
- 保存当前线程的上下文(如CPU寄存器、栈指针)到线程控制块(TCB)中。
- 更新当前线程的状态(如就绪、阻塞等)。
- 选择下一个要执行的线程,更新调度信息。
- 恢复下一个线程的上下文,恢复CPU寄存器、栈指针、程序计数器等信息,确保线程能够从上次中断的地方继续执行,开始执行其任务。
在操作系统中,线程切换是指将CPU的控制权从一个线程转移到另一个线程的过程。与进程切换相比,线程切换开销较小,因为同一个进程内的线程共享相同的地址空间和资源。
进程间的通信
进程之间的通信方式有哪些?
- 管道:管道是一种单向通信方式,用于在父进程和子进程之间或者同一主机上的不同进程之间传递数据。它可以是匿名的,也可以是命名的。
- 命名管道:与匿名管道类似,但具有一个在文件系统中有名的路径,运行不相关的进程之间进行通信。
- 消息队列:消息队列允许一个进程向另一个进程发送信息,消息在队列中按顺序存储,并且接收方可以按需接收。
- 共享内存:共享内存允许多个进程访问同一块内存区域,从而实现快速的数据交换。但需要注意同步问题,以避免竞态条件和数据一致性问题。
- 信号量:信号量是一种同步原语,用于管理对共享资源的访问。它可以用于实现进程间的互斥访问和同步操作。
- 信号:信号是一种异步的通信方式,用于通知目标进程发生了某个事件。信号常用于进程之间发送终端或终止命令。
- 套接字:套接字允许在网络上的不同主机上的进程进行通信,是实现网络通信的基础。
- 文件:进程可以通过读写文件来进行通信,这种方式常用于进程之间的间接通信,例如使用临时文件或者共享文件。
参考:面试鸭