用户进程缓冲区和内核缓冲区
缓冲区的目的,是为了减少频繁的系统IO调用。大家都知道,系统调用需要保存之前的进程数据和状态等信息,而结束调用之后回来还需要恢复之前的信息,为了减少这种损耗时间、也损耗性能的系统调用,于是出现了缓冲区。
有了缓冲区,操作系统使用read函数把数据从内核缓冲区复制到进程缓冲区,write把数据从进程缓冲区复制到内核缓冲区中。等待缓冲区达到一定数量的时候,再进行IO的调用,提升性能。至于什么时候读取和存储则由内核来决定,用户程序不需要关心。
在linux系统中,系统内核也有个缓冲区叫做内核缓冲区。每个进程有自己独立的缓冲区,叫做进程缓冲区。
因此,用户程序的IO读写程序,大多数情况下,并没有进行实际的IO操作,而是在读写自己的进程缓冲区。
1. 用户进程和系统进程
用户进程:运行在操作系统上的进程,都运行在用户空间
系统空间:操作系统运行的空间
这是一个计算机系统运行时的简化模型,我们把所有运行在操作系统上的进程成为用户进程,它们都运行在用户空间(可以看到用户空间有很多进程)。把操作系统运行的空间成为系统空间。
2. 内核态和用户态(kernel mode和user mode)
(1)内核态可以访问系统资源,比如:
ps:上面所说的这些系统资源,在用户进程中是无法被直接访问的,只能通过操作系统来访问,所以也把操作系统提供的这些功能成为:“系统调用”。
比如下图,展示一个用户通过shell控制计算机所经过的数据流向:文件读写和终端控制,都是通过内核进行的。
提供这些限制的基础就是cpu提供的内核态和用户态。比如intel x86 CPU有四种不同的执行级别0-3,linux只使用了其中的0级和3级分别来表示内核态和用户态。
在用户态,不仅仅是系统资源了,就是别的进程的内存对于你来说,都是“透明的”(并不是没办法访问,否则游戏作弊器怎么实现?)
一、用户进程缓冲区
前面提到,用户进程通过系统调用访问系统资源的时候,需要切换到内核态,而这对应一些特殊的堆栈和内存环境,必须在系统调用前建立好。而在系统调用结束后,cpu会从核心模式切回到用户模式,而堆栈又必须恢复成用户进程的上下文。而这种切换就会有大量的耗时。
你看一些程序在读取文件时,会先申请一块内存数组,称为buffer,然后每次调用read,读取设定字节长度的数据,写入buffer。(用较小的次数填满buffer)。之后的程序都是从buffer中获取数据,当buffer使用完后,在进行下一次调用,填充buffer。
所以说:用户缓冲区的目的是为了减少系统调用次数,从而降低操作系统在用户态与核心态切换所耗费的时间。
二、内核缓冲区
除了在进程中设计缓冲区,内核也有自己的缓冲区。
当一个用户进程要从磁盘读取数据时,内核一般不直接读磁盘,而是将内核缓冲区中的数据复制到进程缓冲区中。
但若是内核缓冲区中没有数据,内核会把对数据块的请求,加入到请求队列,然后把进程挂起,为其它进程提供服务。
等到数据已经读取到内核缓冲区时,把内核缓冲区中的数据读取到用户进程中,才会通知进程,当然不同的io模型,在调度和使用内核缓冲区的方式上有所不同,下一小结介绍。
你可以认为,read是把数据从内核缓冲区复制到进程缓冲区。write是把进程缓冲区复制到内核缓冲区。
当然,write并不一定导致内核的写动作,比如os可能会把内核缓冲区的数据积累到一定量后,再一次写入。这也就是为什么断电有时会导致数据丢失。
所以说内核缓冲区,是为了在OS级别,提高磁盘IO效率,优化磁盘写操作。
缓冲区和缓存
还有一部分人把缓冲区和缓存混淆,后来我明白这也是因为翻译导致的把两种东西进行混淆。缓冲区的英文是buffer,而缓存的应为是cache。
CPU缓存(Cache Memory)是位于CPU与内存之间的临时存储器,因为cpu的计算速度要比内存的读写速度快很多,而把这些可能会被重复访问到的数据存储于cpu缓存中,就会提高读取速度。可以说缓存是cpu和内存之间的临时存储器。
也就是说,buffer是因为减少调用次数,集中调用,提高系统性能。而cache是将读取过的数据保存起来,重新读取时若命中(找到需要的数据)就不要去读硬盘了,若没有命中就读硬盘。