内核缓冲区与用户缓冲区
转自:https://blog.csdn.net/DLUTBruceZhang/article/details/9050467
https://www.cnblogs.com/yc3110/p/10440613.html
1.用户与操作系统关系
在用户空间中的进程要通过系统调用才能访问系统资源。系统资源包括:
上面所说的这些系统资源,在用户进程中是无法被直接访问的,只能通过操作系统来访问,所以也把操作系统提供的这些功能成为:“系统调用”。
2.用户进程缓冲区
首先,用户进程通过系统调用访问系统资源的时候,需要切换到内核态,而这对应一些特殊的堆栈和内存环境,必须在系统调用前建立好。而在系统调用结束后,cpu会从核心模式切回到用户模式,而堆栈又必须恢复成用户进程的上下文。而这种切换就会有大量的耗时。
一些程序在读取文件时,会先申请一块内存数组,称为buffer,然后每次调用read,读取设定字节长度的数据,写入buffer。(用较小的次数填满buffer)。之后的程序都是从buffer中获取数据,当buffer使用完后,在进行下一次调用,填充buffer。【缓冲区就可以理解为内存数组,在当前进程中开辟一段虚拟内存空间,并且映射到物理内存中,实际上存储在物理内存中】
用户缓冲区的目的是为了减少系统调用次数,从而降低操作系统在用户态与核心态切换所耗费的时间。除了在进程中设计缓冲区,内核也有自己的缓冲区。
3.内核缓冲区
- 当一个用户进程要从磁盘读取数据时,内核一般不直接读磁盘,而是将内核缓冲区中的数据复制到进程缓冲区中。【同步IO非阻塞的话,此时recvfrom只阻塞于数据从内核复制到用户缓冲区这个过程】
- 但若是内核缓冲区中没有数据,内核会把对数据块的请求,加入到请求队列,然后把进程挂起【这里说的应该是同步阻塞IO】,为其它进程提供服务。【同步IO非阻塞的话recvfrom获取的状态是无数据准备好,需要通过轮询来判断是否准备好,但是这个准备好只是存在于了内核缓冲区中,还没有复制到用户缓冲区中】
- 等到数据已经读取到内核缓冲区时,把内核缓冲区中的数据读取到用户进程中,才会通知进程,当然不同的io模型,在调度和使用内核缓冲区的方式上有所不同。
read是把数据从内核缓冲区复制到进程缓冲区。write是把进程缓冲区复制到内核缓冲区。当然,write并不一定导致内核的写动作(不等价于写入磁盘),比如os可能会把内核缓冲区的数据积累到一定量后,再一次写入。这也就是为什么断电有时会导致数据丢失。所以说内核缓冲区,是为了在OS级别,提高磁盘IO效率,优化磁盘写操作。
【个人认为,read和write都是系统调用函数,因为要访问内核缓冲区或者写入内核缓冲区,需要进入到内核态。但是因为有buffer的存在,如果这里有数据,就从这里读,而不产生系统调用,节约时间。】
对比阻塞和非阻塞:
- 在阻塞io中,直到数据从内核缓冲区拷贝到用户缓冲区才通知用户进程调用完成并唤醒;
- 而非阻塞,在轮训得知数据准备好后,数据还是在内核缓冲区中,等你去读取,这也就是说数据准备好,并不代表已经读好可以使用。当然也不代表一定能读。【只是来到内核缓冲区了,还没有拷贝到用户缓冲区】
应用内核缓冲区的主要思想就是一次读入大量的数据放在缓冲区,需要的时候从缓冲区取得数据:
- 提高了磁盘的I/O效率;
- 优化了磁盘的写操作;
- 需要及时的将缓冲数据写到磁盘。
4.为什么有需要将数据来回从内核到用户缓冲区替换呢?
用户进程是运行在用户空间的,不能直接操作内核缓冲区的数据。 用户进程进行系统调用的时候,会由用户态切换到内核态,待内核处理完之后再返回用户态。
- 应用缓冲技术能很明显的提高系统效率。内核与外围设备的数据交换,内核与用户空间的数据交换都是比较费时的,使用缓冲区就是为了优化这些费时的操作。比如read本来从内核读取数据时是比较费时的,所以一次取出一块,以避免多次陷入内核。【这里说明了本人之前的理解是正确的。】