从C语言系统调用函数read和write到Java阻塞IO

一 背景

作为一名Java代码搬运工,我们经常通过java.io包对进行操作IO。由于JVM跨平台的优点,能够帮开发者屏蔽底层细节,使我们不需要直接与操作系统打交道,更加专注业务开发。缺点也很明显,开发者对底层往往一脸懵逼。最近楼主重读了操作系统一书,对底层原理打破沙锅问到底。

二 用户程序读写原理

在Linux操作系统中,内存划分为用户空间内核空间,在4G内存的32位X86系统中,两者的内存占比是3:1。JVM想读取磁盘中的文件时候,可以直接读取?答案是否定的。硬件设备通常不能与用户空间交互,JVM属于用户进程,驻守在用户空间,所以JVM没法直接读取磁盘中的数据。正确的读流程是:1)用户进程底层使用C语言的read方法进行系统调用; 2)内核向磁盘控制器发送命令,要求其从磁盘读取数据; 3)磁盘控制器通过直接内存读取(DMA),无须CPU协助即可把数据写入内核缓冲区; 4)内核把数据从内核的临时换冲区拷贝到用户空间的缓冲区,如图所示。整个IO过程总结起来就是两次拷贝
在这里插入图片描述

读如此,写亦然,JVM向磁盘中写数据跟读数据流程相反而已。这里不再累述。

三 系统调用函数read和write的API 详解

1)输入函数read

   Int n_read = read(int fd, char * buf, int n)

参数fd是文件描述符,从命令行读取数据时,设置为0;buf为读缓冲区;n为每次读取到缓冲区的字节数。
当返回值n_read为-1表示读取失败,大于0表示读到缓存区中的字节数。

2)输出函数write

Int n_written = read(int fd, char * buf, int n)

参数fd是文件描述符,输出到命令行时,设置为1;buf为写缓冲区;n为每次写入缓冲区的字节数。
当返回值n_written为-1表示写入失败,大于0表示成功写入字节数。

通过API我们可以发现,虽然两次拷贝降低IO的读写速率,但是我们可以通过提高每次读出或者写入的数据的字节数,有效提高IO的读写速率。

四 IO模型

C语言系统调用函数read和write是同步阻塞的,即代码执行到read或write方法时候,必须执行完当前代码,才能执行下一步。以读数据为例,read(int fd, char * buf, int n)如果用户空间的buf缓冲区没有读取到n个字节,程序就会一直卡在read这个方法,不会往下执行。正因为如此,所以java.io中读写的方法也是同步阻塞的。

五 系统调用代码

root@ubuntu18:~/c_workspace/io#vim read_write_app.c

//在文件中写入如下代码:


```c
#include <unistd.h>

#define MAXSIZE 1024

int main(void){
  char buf[MAXSIZE];
  int n;

  while((n = read(0, buf, MAXSIZE)))
     write(1, buf, n);

  return 0;
}

```bash
#编译成二进制可执行文件
root@ubuntu18:~/c_workspace/io# gcc read_write_app.c -o read_write_app
root@ubuntu18:~/c_workspace/io# ls
read_write_app  read_write_app.c

#与用户交互

第一个hello world!是用户在shell上输入的,第二个是程序将结果写到shell上。

五 参考文献

1) Brian W. Kernighan,Dennis M.Ritchie著,《C语言程序设计》,第二版本,机械工业出版社
2) Andrew S·Tanenbaum 著,《现代操作系统》,第三版本,机械工业出版社

原创文章 32 获赞 12 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_35469756/article/details/103624523
今日推荐