学懂IO必备的操作系统知识(一)

系列文章目录

学懂IO必备的操作系统知识(一)
学懂IO必备的操作系统知识(二)
学懂IO必备的TCP、socket知识(三)


  很多非计算机专业的学生学习java编程时,一上来就接触各种IO:BIO、NIO、AIO,同步io、阻塞io,可能直接傻掉了,what‘s the hell!

接下来我们就从操作系统层面一点点的解开IO的面纱,让你一睹真容,其实它就是个普普通通的“人”。


计算机主要组成:运算器、控制器、存储器、输入设备和输出设备。

IO指的是input、output,数据的输入输出,一般涉及到磁盘的io、网络的io(网卡的io)。

一、磁盘io

我们先讲磁盘的io,一般来说程序是没有办法直接访问操作磁盘的,直接io(不经过内核缓冲区,直接操作磁盘数据)除外。其他形式的磁盘io,都需要经过内核kernel,内核就是操作系统的程序,只是它的权利很大而已。

内核

kernel 包含了虚拟文件系统、fd表、of表、inode表、pagecache 等。

操作系统

每种操作系统都是有自己的虚拟文件系统的。Windows对应的是各种盘下的文件夹、文件等;linux的是通过虚拟目录树组成的,在linux的世界中一切皆文件,所有的文件操作,都是通过fd定位资源和状态的,不管读写文件还是进行网络通信。

linux中涉及的文件类型如下:
ll 命令 第一列:第一个字符代表类型,第二列:硬链接的数量
  -:普通文件(可执行、图片、文件)
 d:目录
 b:块设备–可以前后随意读取,硬盘
 c:字符设备–不可以前后随意读取,键盘
 s:socket
 p:pipeline (管道)
 l:link 链接(弱连接、强链接)
 [eventpoll]:
 … …
 
 rw前的-,就是文件类型
 在这里插入图片描述

理解具体情况,需要了解由内核维护的3个数据结构:

  • 进程级文件描述符表(file descriptor table)
  • 系统级打开文件表(open file table)
  • 文件系统i-node表(i-node table)

这3个数据结构之间的关系如下图所示:
在这里插入图片描述

文件描述符表

系统为每个进程维护一份文件描述符表,该表的每一个条目都记录了记录了打开文件描述体(open file table)指针,通过of找到访问的方式、文件偏移量、i-node对象指针等。表中每个条目为一个fd-文件描述符,是供程序读写使用。
该表的每一个条目都记录了单个文件描述符的相关信息,包括:

  • 控制标志(flags),目前内核仅定义了一个,即close-on-exec
  • 打开文件描述体指针

fd其实就是一个数字,linux默认是有0,1、2的,再增加就是从3开始

  • 0:标准输入(system.in())
  • 1:标准输出(system.out(),输出到屏幕)
  • 2:标准错误输出(system.error())

exec 8 < demo.txt,在当前进程创建一个fd,可以在/proc/$$/fd 下看到8, 两个 $代码当前进程号,可以替换为其他的id号

read a 0<& 8 :d 读取8中的内容到a,echo a可以看到8里面的内容。

exec 8 <> /dev/tcp/www.baidu.com/80 :这样就跟百度取的一个连接,通过8这个fd进行读写,如图:
在这里插入图片描述

打开文件列表

内核对所有打开的文件维护一个系统级别的打开文件描述表(open file description table)。表中的条目称为打开文件描述体(open file description),存储了与一个打开的文件相关的全部信息,包括:

  • 文件偏移量(file offset),调用read()和write()更新,调用lseek()直接修改
  • 访问模式(status flags),由open()调用设置,例如:只读、只写或读写等
  • i-node对象指针(v-node ptr),指向一个inode元素,从而关联物理文件
i-node表

在linux系统中,文件使用inode号来描述,inode存储了文件的很多元信息。每个文件系统会为存储于其上的所有文件(包括目录)维护一个i-node表

单个i-node包含以下信息:

  • 文件类型(file type),可以是常规文件、目录、套接字或FIFO
  • 文件的字节数
  • 文件拥有者的User ID
  • 文件的Group ID
  • 文件的读、写、执行权限
  • 文件的时间戳,共有三个:ctime指inode上一次变动的时间,mtime指文件内容上一次变动的时间,atime指文件上一次打开的时间。
    链接数,即有多少文件名指向这个inode
  • 文件数据block的位置

i-node存储在磁盘设备上,内核在内存中维护了一个副本,这里的i-node表为后者。副本除了原有信息,还包括:引用计数(从打开文件描述体)、所在设备号以及一些临时属性,

页缓存pagecache

pagecache是内核实现的,以4K为缓存单位,在64位系统上为8k,这里会有脏页的产生。pagecache是为了提高程序与磁盘的交互,减少用户态到内核态的切换而产生的,只要增加了一层缓存,就会影响到数据的一致性,当新建一个缓存页或者修改了缓存页时,还没有刷盘,这个时候它就是脏页(与磁盘数据不一致),如果计算机突然断电会导致,脏页中的数据丢失。
多个程序操作一个文件时,只会在pagecache加载一次,共享缓冲页。
pagecache存在内存淘汰策略,淘汰的都是正常的落盘的页。
刷屏策略:内核通过占比阈值、时间阈值刷盘。

通过/etc/sysctl.conf  调整 pagecache相关的参数。具体每项意思这里就不赘述了
vm.dirty_background_ratio = 10
vm.dirty_background_bytes = 0
vm.dirty_ratio = 20
vm.dirty_bytes = 0
vm.dirty_writeback_centisecs = 500
vm.dirty_expire_centisecs = 3000

pcstate -p $$:查看当前进程的pagecache (怎么添加pcstate后续会加)

如何用代码查看pagecache ,确实是刷盘的?
首先改写/etc/sysctl.conf 配置项,再编写一个java程序,往一个文件demo.txt 死循环一直往里面写数据,写的过程中可以通过pcstate 查看pagecache的具体情况,这个时候可以通过正常关闭虚拟机、直接关掉虚拟机来测试pagecache,正常关闭pagecache是会刷盘的,直接关掉的pagecache脏页会丢失未刷盘。重启虚拟机就可以看到demo.txt 大小的变化。

硬盘分区

根据自己需要自定义分区,下面是个例子3个分区:
1:200M (bios启动这个分区kernel在里面,boot 也挂载这个,会覆盖下面的boot挂载)
2:swap:2G
3:/、root、boot、etc…

重定向

重定向不是命令,是机制

$$ 当前base的进程id,== $BASEPID
/proc 内核映射的文件
/proc/pid/fd/ == lsof -p pid 查看指定进程打开的fd
ls ./ 1> ls.out :ls 输出的内容,重定向到ls.out中
cat 0< ls.out 1>cat.out :cat 的输入从ls.out获取,输出到cat.out (0<等于<;1>等于>)
read a:读取键盘输入,回车结束,
**echo $a:**打印上面键盘输入的值。
read a 0< cat.out :指定read的输入流为cat.out ,但是read换行结束,因此a 只有cat.out的第一行。
在这里插入图片描述
ls ./ /dd 1> std.out 2>error.out :ls的标准输出到std.out中,标准错误输出到error.out 中
ls ./ /dd 1> std.out 2>&1 :ls的标准输出、标准错误都指向到了std.out

进程间的变量是隔离的,除非使用export导出
{ echo “ad”; echo “123”; }:多条指令的代码块,注意空格和分号

管道

|管道左侧的输出作为右侧的输入
head all.log : 读取头的十行,head -2 all.log 读取头的前两行
tail all.log : 读取尾的十行,tail-2 all.log 读取尾的两行
head -10 all.log |tail -1 :只获取第10行

如果命令含有了管道|,管道左右会启动子进程执行命令;

ps:$$ 优先级 > 管道优先级 > $BASEPID 优先级 ,影响进程间变量的读取

int 0x80 :int cpu 指令,0x80是16进制数字128,数字对应的是终端描述符

涉及命令

ifconfig:informationConfig 查看IP地址
df -h:disk file 磁盘空间占用情况
mount:挂载系统外的文件。 /dev/hda1 /boot :将 /dev/hda1 挂在 /boot 之下。可以增加其他配置项
umount:卸除目前挂在Linux目录中的文件系统。
stat:以文字的格式来显示inode的内容。查看 testfile 文件的inode内容内容,命令: stat testfile
ln:ln demo.txt target.txt ,demo.txt映射到target.txt,是硬链接,两个文件的inode号是一样的;ln -s demo.txt target.txt ,demo.txt映射到target.txt,是弱链接两个文件的inode号是不一样的;
whereis bash:查看bash路径
echo $$ :可以打印出当前进程号
chroot:把根目录换成指定的目的目录。例:chroot /mnt/ls
lsof:(list open files)是一个列出当前系统打开文件fd的工具,默认是所有进程的,lsof -p 8341 指定进程
pstree:查看进程的父子关系
ps -ef | grep java :ps=process根据关键字筛选进程
command > /dev/null :/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃

二、 整体流程图

读取数据流程图1:
在这里插入图片描述
程序有缓冲区、内核有缓冲区、硬盘有缓冲区。
执行程序编译后的字节码文件时,会把字节码对应成cpu的命令,cpu根据指令保护现场切换到内核态,调取kernel的方法,cpu再通过内核的调度从硬盘读取数据(中间通过fd找到打开文件记录,再到inode 记录)),每次的数据读取既可以都通过cpu的寄存器来传递,也可以通过DMA 就可以读写磁盘,直接从磁盘缓存区写入到内存pagecache,不必一直依赖cpu。
写数据:程序缓冲区>kernel pagecache >硬盘缓冲区 >硬盘
读数据:硬盘>硬盘缓冲区 >kernel pagecache >程序缓冲区

读取数据流程图2:
在这里插入图片描述
在图一的过程中还涉及图2的一个细节流程,如果kernel pagecache没有想要的数据,做缺页处理,app1进程保护现场挂起,cpu交给dma去处理从磁盘往kernel写数据的事儿,等数据准备完毕了,dma发起一个中断,告诉cpu数据准备完毕,你可以继续干活了,cpu就可以恢复现场,程序继续运行。

后续更精彩… …

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值