1. 操作系统涉及IO操作的宏观理解
说明:
- 用户程序读取io设备中的数据时(如磁盘,网卡),先由操作系统内核读取到内存(缓存页),用户程序读取到的是缓存页中的内容。
- 用户程序往io设备写数据时,修改的先是缓存页中的内容,需要定时把缓存页中的内容刷新flush到磁盘,不同的刷新策略在断电时数据丢失的多少不一样。
- 操作系统中有一个称为VFS(vistual file system虚拟文件系统)的树形结构,对应着Linux系统中的一个个文件。(Linux下一切皆文件)。
- fd和inode说明(每当进程用
open()
函数打开一个文件,内核便会返回该文件的文件操作符(一个非负的整形值),此后所有对该文件的操作,都会以返回的fd文件操作符为参数。 -
fd = open(pathname, flags, mode) // 返回了该文件的fd rlen = read(fd, buf, count) // IO操作均需要传入该文件的fd值 wlen = write(fd, buf, count) status = close(fd)
- 每个文件系统会为存储于其上的所有文件(包括目录)维护一个i-node表,单个i-node包含以下信息:文件类型(file type),可以是常规文件、目录、套接字或FIFO,访问权限,文件锁列表(file locks),文件大小等
- 文件描述符可以理解为进程文件描述表这个表的索引,或者把文件描述表看做一个数组的话,文件描述符可以看做是数组的下标。当需要进行I/O操作的时候,会传入fd作为参数,先从进程文件描述符表查找该fd对应的那个条目,取出对应的那个已经打开的文件的句柄,根据文件句柄指向,去系统fd表中查找到该文件指向的inode,从而定位到该文件的真正位置,从而进行I/O操作。)
- 缓存页中的数据被称为脏数据(可能已经被应用程序修改)
- 当第二个进程操作同一个数据的时候,访问的是缓存页中的数据,不会从io中重新加载。
- 当访问的数据不同且数据不位于同一个缓存页的时候,将会从io中加载新的数据放入另一个缓存页
- 如何理解虚拟内存 - 知乎
2.linux操作文件系统常用命令
磁盘目录挂载
- df命令,查看目录挂载磁盘的情况,没有显示的目录默认挂在同/目录相同的磁盘
- df -h
- unmount命令,取消挂载
- mount命令 新增挂载
linux下常见文件类型
linux文件类型的软连接和硬链接
相同点:一方修改,另一方同步修改
不同点:
硬链接是指向相同的inode号(相当于两个变量指向同一块内存区域),软连接指向不同的inode号
创建硬链接时,内容引用数加1,创建软连接不加1.
创建硬链接
创建软连接(相当于一个快捷方式)
删除硬链接时,内容引用数减1,删除软连接时,指向的软连接爆红,表示不存在。
查看文件信息(inode号)
创建能挂载指定目录的磁盘空间
1.创建一个空的指定大小的磁盘文件
2.将其转换为块文件
3.将其格式化为ext2格式文件
4.将其挂载到指定目录(当程序访问/mnt/ooxx的时候,就会到/dev/loop0中去寻找)
where is 指令查找命令位置
whereis实用程序用于查找给定命令的二进制文件、源文件和手动文件
Linux中的Whereis命令,教你如何使用whereis命令及注意事项_Linux命令_云网牛站
改变根目录的位置
前提条件是该目录下有/bin/bash文件在,且执行该命令后会执行bash命令
查看当前进程的pid
$$是当前bash进程的pid 等同于 $BASHPID
显示当前进程加载了哪些文件
文件描述符中包含哪些东西?如何读取写入文件描述符
lsof -op $$(列出当前进程所打开的文件;)
重定向操作符的基本语法(每个命令都有标准012三个文件描述符)
head 命令()展示文件前十行
tail命令()展示文件后十行
使用管道展示文件第8行
父子进程通信问题
父进程变量能否被子进程获取
程序从磁盘加载的过程:
从main方法开始,通过pc寄存器使用一条条指令创建类,创建变量执行。其中包含着通过mmu将虚拟内存指向物理内存地址
应用程序 --操作系统内核--磁盘交互方式
数据从磁盘加载到内存不再经过cpu,而是通过dma协处理器
首先明确:
内存被分为一个个4K大小的页,每个页的初始内容为空
应用程序想要读磁盘数据时,发起in80读中断,通过操作系统内核调用read函数,读取磁盘,并将读取结果放入内存中的缓存页中,被填充的缓存页被标记为dirty,发起中断将结果返回给应用程序。
应用程序想要往磁盘写数据时,发起in80写中断,通过操作系统内核调用write函数,修改内存中缓存页中的数据,但不一定立即将更改的缓存页数据刷新到磁盘,而是取决于应用程序或者操作系统的刷盘策略,这样做的好处是减少与磁盘的交互次数,提高程序的执行效率,缺点是因为没有立刻刷写数据到磁盘,可能造成断电时数据丢失。
刷写磁盘的时机:(刷盘后清除dirty标记)
1 满足指定的刷盘策略时机,这种情况下在空间足够的情况下刷盘后数据仍然在内存缓存页中,刷盘后数据清除dirty标记,直到数被再次修改。
2.因为缓存页空间不足引起的数据淘汰(lru算法,将最近最少使用的数据刷写到磁盘),这种情况下缓存页数据刷盘后淘汰,磁盘空间向前移动,在末尾继续写入缓存页
刷盘策略查看
sysctl -a |grep dirty
修改默认的刷盘策略
示例:
strace命令(此处可以通过vi out.子进程号查看进程调用执行情况)
vi out.2561
网络io模型演进
bio--->nio----->多路复用器(select--->poll------->epoll------->netty)
以上网络io模型解决了什么问题
客户端与服务端连接后,以何种方式处理连接的读写事件、不断压榨cpu的过程、
bio 同步阻塞模型,基于stream,基于多线程,浪费线程资源
nio 同步非阻塞模型,基于buffer和channel,通过设置configureblocking(false),可以实现单线程或者多线程,通过遍历所有的fd状态
epoll与selector
epoll的过程解释:
1.服务器端创建serverSocket,发起3+1个调用(三个常规 socket bind listen,一个epoll特有 epoll_create{在内核中开辟一块空间}),对应关系看图2
2.如果有客户端连接,就在这块内核空间中通过epoll_ctl方法将该fd放入一个红黑树的数据结构中。
3.如果客户端有数据需要读取,在数据被读入内核后,epoll进行延申处理,对比红黑树的fds,将有状态的fds复制到双向链表中(epoll特有)
4.当执行到select方法时,epoll,select和poll的对应系统调用是不同的,看上图
5.当有多个可读channel时,还有可能造成读阻塞,解决办法1:接收和处理分离,异步消息队列处理。如tomcat8 9
解决办法2:使用线程池多线程处理,如redis处理io时的io threads