操作系统
什么是操作系统
操作系统(Operating System,简称 OS)是一种控制和管理计算机硬件和软件资源的系统软件。它负责管理和分配计算机系统的资源,如处理器、内存、存储设备、输入输出设备、网络等,同时提供各种服务和接口,使应用程序能够方便地访问系统资源。
操作系统的组成
内核(Kernel):内核是操作系统的核心组成部分,它是操作系统和计算机硬件之间的接口。内核负责管理系统资源,如处理器、内存、设备驱动程序等,同时也提供各种系统调用供应用程序使用。
文件系统(File System):文件系统是一种管理和组织计算机存储设备上数据的机制。它负责对文件进行创建、删除、修改、读取等操作,并保证数据的安全性和可靠性。
进程管理(Process Management):进程管理是操作系统对正在运行的程序进行管理的机制。它负责管理进程的创建、调度、挂起、恢复和终止等操作,并保证多个进程之间的资源共享和互斥。
内存管理(Memory Management):内存管理是操作系统对计算机内存进行管理的机制。它负责管理内存的分配、回收、虚拟内存等操作,并保证系统和应用程序的内存使用效率和稳定性。
设备驱动程序(Device Drivers):设备驱动程序是操作系统中用来管理和控制硬件设备的软件程序。它负责与硬件设备进行通信,接收和发送数据,控制设备的运行状态等。
用户界面(User Interface):用户界面是操作系统和用户之间的接口。它提供了各种交互方式,如命令行界面、图形用户界面等,让用户可以方便地与计算机进行交互。
系统调用
系统调用(System Call)是操作系统提供给应用程序用于访问操作系统的服务和资源的一组函数接口。常见的系统调用包括:文件操作(如 open、read、write、close 等)、进程管理(如 fork、exec、waitpid 等)、网络操作(如 socket、connect、send、recv 等)、信号处理(如 signal、sigaction 等)等。系统调用的具体实现与操作系统的具体实现相关,不同的操作系统会提供不同的系统调用接口。
系统调用通常是通过软中断或系统调用指令(如 x86 中的 int 指令)触发的,当应用程序需要调用一个系统调用时,它会将参数传递给内核,内核会执行相应的操作并返回结果给应用程序。由于系统调用涉及到内核态和用户态的切换,所以相对于普通函数调用,系统调用的开销较大。
用户态和内核态
用户态(User Mode)和内核态(Kernel Mode)是操作系统运行过程中的两种不同的执行模式,它们的区别在于对计算机系统资源的访问权限不同。
用户态是指应用程序所在的环境空间中,只可访问自己的内存空间和CPU非特权的指令集。不能直接访问操作系统内核或者特权指令集,若是要访问操作系统的服务和资源,需要通过系统调用来实现
内核态是指操作系统内核所处的执行环境,内核态下有权限访问和处理操作系统的所有资源,包括 CPU 的特权指令集、内存和所有硬件设备等。在内核态下运行的代码通常是操作系统内核的部分或全部,例如设备驱动程序、进程管理、文件系统等。
用户态和内核态之间的切换是通过系统调用、中断、异常等方式实现的。当应用程序需要访问操作系统提供的服务时,它会触发系统调用,从用户态切换到内核态,操作系统会执行相应的操作并返回结果给应用程序,之后再从内核态切换回用户态。因为用户态和内核态之间的切换需要进行状态的保存和恢复,所以切换的开销比较大,应尽量避免过多的切换操作。
常见的阻塞进程的系统调用函数
read():读取文件或者其他的输入设备时,如果没有可用的数据,会将进程挂起等待数据到来,直到数据到来或者出现错误才会返回。
write():写入数据到输出设备时,如果设备缓冲区已满,就会将进程挂起等待设备空闲,直到写入的数据全部被写入或者出现错误才会返回。
accept():接受一个网络连接请求时,如果没有请求到来,会将进程挂起等待请求到来,直到请求到来或者出现错误才会返回。
recv()/send():在进行网络编程时,当需要接收或发送数据时,如果没有数据可用或发送缓冲区已满,进程将被阻塞,直到有数据可用或者缓冲区有空闲位置。
select():用于在多个文件描述符上等待事件,如果没有任何一个文件描述符上有事件发生,就会将进程挂起等待,直到有事件发生或者出现错误才会返回。
poll():与select()类似,用于在多个文件描述符上等待事件,如果没有任何一个文件描述符上有事件发生,就会将进程挂起等待,直到有事件发生或者出现错误才会返回。
epoll_wait():也是用于在多个文件描述符上等待事件,但是相对于select()和poll()来说,在处理大量的文件描述符时,效率更高。
sleep():让进程挂起一段时间,等待指定时间后再恢复运行。
wait():等待子进程退出时,如果子进程没有退出,就会将进程挂起等待子进程退出,直到子进程退出或者出现错误才会返回。