虚拟文件系统
一. 前言 基于上文介绍的文件系统的基本结构,本文将继续深入Linux文件系统的精髓所在:虚拟文件系统。操作文件的本质是将磁盘文件数据映射到进程中,上文的文件系统是如何存储文件数据,而从进程如何映射到该文件系统,中间还有一系列的过程,主要包括
进程发出文件操作命令,通过系统调用如sys_open、sys_read、sys_write调用相应内核函数
在内核中为进程打开的文件和系统文件创建数据结构进行维护
通过虚拟文件系统对各种不同的文件系统操作,如I/O设备、管道、进程间通信、网络等进行抽象并统一接口
实现虚拟文件系统和实际文件系统如ext4的挂载
提供文件系统和I/O设备层的设备驱动接口及加快读写效率的缓存
整体层次如上图所示,其实可以概括为文件系统层、通用快层和设备层,如下图所示。
文件系统层,包括虚拟文件系统和其他各种文件系统的具体实现。它为上层的应用程序,提供标准的文件访问接口;对下会通过通用块层,来存储和管理磁盘数据。
通用块层,包括块设备 I/O 队列和 I/O 调度器。它会对文件系统的 I/O 请求进行排队,再 ...
Linux 中的五种IO模型
IO模型的选择在Linux网络编程中十分重要,在Unix/Linux环境中主要提供了五种不同的IO模型,分别是阻塞式IO、非阻塞式IO、IO多路复用、信号驱动式IO和异步IO。
通常一个输入操作包含两个不同阶段:
等待数据准备好
从内核向进程复制数据
例如,对于一个网络套接字上的输入操作,第一步通常涉及到发生系统调用,用户态切换到内核态并等待数据从网络中到达,当所有等待分组到达时,数据被复制到内核中的某个缓冲区。第二步则是将数据从内核缓冲区复制到应用进程缓冲区。
磁盘文件的IO比较特殊,内核采用缓冲区cache加速磁盘IO请求。因而一旦请求的数据到达内核缓冲区cache,对磁盘的write()操作立即返回,而不用等待将数据写入磁盘后再返回(除非在打开文件时指定了O_SYNC标志)。与之相对应的read()操作将数据从内核缓冲区cache移动到用户的缓冲区中,如果请求的数据不在内核缓冲区cache中,内核会让进程休眠,同时执行对磁盘的读操作。所以实际上在磁盘IO中,等待阶段是不存在的,因为磁盘文件并不像网络IO那样,需要等待远程传输数据。
阻塞式I/O模型
...
Linux内核之epoll模型
同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的。
这里主要讨论Linux环境下的network IO。
一 概念说明在进行解释之前,首先要说明几个概念:
用户空间和内核空间
进程切换
进程的阻塞
文件描述符
缓存 I/O
用户空间与内核空间现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方)。操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。为了保证用户进程不能直接操作内核(kernel),保证内核的安全,操心系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间。
针对linux操作系统而言,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空间。
而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为用户空间。
进程切换为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起的某个进程的 ...
Linux内核网络UDP数据包发送(三)—IP协议层分析
1. 前言Linux内核网络 UDP 协议层通过调用 ip_send_skb 将 skb 交给 IP 协议层,本文通过分析内核 IP 协议层的关键函数来分享内核数据包发送在 IP 协议层的处理,并分享了监控IP层的方法。
2. ip_send_skbip_send_skb 函数定义在 net/ipv4/ip_output.c 中,非常简短。它只是调用ip_local_out,如果调用失败,就更新相应的错误计数:
1234567891011121314int ip_send_skb(struct net *net, struct sk_buff *skb){ int err; err = ip_local_out(skb); if (err) { if (err > 0) err = net_xmit_errno(err); if (err) IP_I ...
Linux内核网络udp数据包发送(一)
1.前言本文首先从宏观上概述了数据包发送的流程,然后分析了协议层注册进内核以及被套接字的过程,最后介绍了通过套接字发送网络数据的过程。
2.数据包发送宏观视角从宏观上看,一个数据包从用户程序到达硬件网卡的整个过程如下:
1、使用系统调用(如 sendto,sendmsg 等)写数据2、数据分段socket顶部,进入socket协议族(protocol family)系统3、协议族处理:数据跨越协议层,这一过程(在许多情况下)转变数据(数据)转换成数据包(packet)4、数据传输路由层,这会涉及路由缓存和ARP缓存的更新;如果目的MAC不在ARP缓存表中,将触发一次ARP广播来查找MAC地址5、穿过协议层,packet到达设备无关层(设备不可知层)6、使用XPS(如果启用)或散列函数选择发送坐标7、调用网卡驱动的发送函数8、数据传送到网卡的 qdisc(queue纪律,排队规则)9、qdisc会直接发送数据(如果可以),或者将其放到串行,然后触发NET_TX类型软中断(softirq)的时候再发送10、数据从qdisc传送给驱动程序11、驱动程序创建所需的DMA映射,刹车网卡从RAM读 ...
Linux内核网络udp数据包发送(二)-UDP协议层分析
1.前言本文分享了Linux内核网络数据包发送在UDP协议层的处理,主要分析了udp_sendmsg和udp_send_skb函数,并分享了UDP层的数据统计和监控以及套接字发送大小的调优。
2.udp_sendmsg这个函数定义在net / ipv4 / udp.c,函数很长,分段来看。
2.1 UDP插入UDP udp_sendmsg corking是一项优化技术,允许内核将多个数据累积成一体的数据报发送。在用户程序中有两种方法可以启用此选项:
使用 setsockopt 系统调用设置socket的 UDP_CORK 选项
程序调用 send,sendto 或 sendmsg 时,带 MSG_MORE 参数
udp_sendmsg 代码检查 up->pending 套接字socket当前是否已被塞住(corked),如果是,则直接跳到 do_append_data 进行数据追加(append)。
1234567891011121314151617181920212223int udp_sendmsg(struct kiocb *iocb, struct ...
Linux内核网络中断下半部处理
网卡接收和发过数据在 Linux 内核中的处理过程,我们先来回顾一下网卡接收和发送数据的过程,如 图1 所示:
图1 网卡接收和发送数据过程
如上图所示,当网卡接收到从网络中发送过来的数据后,网卡会向 CPU 发起一个硬件中断。当 CPU 接收到网卡的硬件中断后,便会调用网卡驱动向内核注册的中断处理服务,如 NS8390网卡驱动 会向内核注册 ei_interrupt 中断服务。
由于在处理硬件中断服务时会关闭硬件中断,所以在处理硬件中断服务的过程中,如果发生了其他的硬件中断,也不能得到有效的处理,从而导致硬件中断丢失的情况。
为了避免这种情况出现,Linux 内核把中断处理分为:中断上半部 和 中断下半部,上半部在关闭中断的情况下进行,而下半部在打开中断的情况下进行。
由于中断上半部在关闭中断的情况下进行,所以必须要快速完成,从而避免中断丢失的情况。而中断下半部处理是在打开中断的情况下进行的,所以可以慢慢进行。
一般来说,网卡驱动向内核注册的中断处理服务属于 中断上半部,如前面介绍的 NS8390网卡驱动 注册的 ei_interrupt 中断处理服务,而本文主要分析网卡 中断下 ...
Linux内核网络协议栈accept函数剖析
1、应用层——accept 函数该函数返回一个已建立连接的可用于数据通信的套接字。
12345#include <sys/socket.h> int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen); //返回:非负描述子——成功,-1——出错 /*参数sockfd是监听后的套接字,这个套接字用来监听一个端口,当有一个客户与服务器连接时,它使用一个与这个套接字关联的端口号, 比较特别的是:参数cliaddr和addrlen是一个结果参数,用来返回已连接客户的协议地址。如果对客户的地址不感兴趣,那么可以把这个值设置为NULL*/
2、BSD Socket 层——sock_accept 函数1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768/* * For accept, we attem ...
Linux内核网络协议栈bind函数使用
socket 函数并没有为套接字绑定本地地址和端口号,对于服务器端则必须显性绑定地址和端口号。bind 函数主要是服务器端使用,把一个本地协议地址赋予套接字。
1、应用层——bind 函数123#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);/*sockfd是由socket函数返回的套接口描述字,第二个参数是一个指向特定于协议的地址结构的指针,第三个参数是该地址结构的长度*/
bind 函数的功能则是将socket 套接字绑定指定的地址。
2、BSD Socket 层——sock_bind 函数同样是通过一个共同的入口函数 sys_socket
1234567891011121314151617181920212223242526272829303132/* * Bind a name to a socket. Nothing much to do here since it's * the protocol's ...
Linux内核网络协议栈套接字的绑定、监听、连接和断开
1、套接字的绑定创建完套接字服务器端会在应用层使用bind函数进行套接字的绑定,这时会产生系统调用,sys_bind内核函数进行套接字。
系统调用函数的具体实现
12345678910111213141516171819202122SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen){ struct socket *sock; struct sockaddr_storage address; int err, fput_needed; sock = sockfd_lookup_light(fd, &err, &fput_needed); if (sock) { err = move_addr_to_kernel(umyaddr, addrlen, (struct sockaddr *)&address); if (err >= 0) { err = security_socket_bind(sock, ...