该分析sys_io_submit函数了,这个函数有点复杂,但是条理很清晰,先说一句就是提交异步io,具体怎么提交呢?我们知道,对于异步io,一次性可以提交多个请求,那么可以想象的就是在sys_io_submit中会把我们用户程序的多个请求分解成一个一个的请求,依次提交,这是很合理的假设,内核实际上也是这么做的,刚才的建立异步io的阶段只是建立了一个可以让异步io表演的大的环境,现在的提交请求和将来的读取数据便是大戏了,准备好了吗?马上开演!

asmlinkage long sys_io_submit(aio_context_t ctx_id, long nr, 
struct iocb __user * __user *iocbpp) 

struct kioctx *ctx; 
long ret = 0; 
int i; 
if (unlikely(nr < 0)) 
return -EINVAL; 
if (unlikely(!access_ok(VERIFY_READ, iocbpp, (nr*sizeof(*iocbpp))))) 
return -EFAULT; 
ctx = lookup_ioctx(ctx_id);//这里查找我们刚才建立的kioctx 
if (unlikely(!ctx)) { 
… 
for (i=0; i<nr; i++) {//这个循环实质上分解了用户请求 
struct iocb __user *user_iocb; 
struct iocb tmp; 
if (unlikely(__get_user(user_iocb, iocbpp + i))) { 
ret = -EFAULT; 
break; 

if (unlikely(copy_from_user(&tmp, user_iocb, sizeof(tmp))))

… 
ret = io_submit_one(ctx, user_iocb, &tmp);//一次提交一个,直到全部提交完毕 
if (ret) 
break; 

put_ioctx(ctx); 
return i ? i : ret; 
}

上面的函数提到了查找kioctx结构,这个函数猜也能猜到怎么实现的,就是在进程的mm中的ioctx_list中寻找id和sunmit参数的id相 同的kioctx,说了半天,到底什么是kioctx,它到底有什么用?它实际上正如前面说的那样,是一个大舞台,提供了一个工作体,提供了一个运行队列 等等,所有属于这个大舞台的请求必须纳入它的管理范畴,它的字段决定了谁应该被调度,以及调度后应该做甚。下面来看看完成实际工作的io_submit_one函数:

1476 int fastcall io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, 
struct iocb *iocb) 

struct kiocb *req; 
struct file *file; 
ssize_t ret; 
/* enforce forwards compatibility on users */ 
if (unlikely(iocb->aio_reserved1 || iocb->aio_reserved2 || 
… 
/* prevent overflows */ 
if (unlikely( 
(iocb->aio_buf != (unsigned long)iocb->aio_buf) || 
(iocb->aio_nbytes != (size_t)iocb->aio_nbytes) || 
((ssize_t)iocb->aio_nbytes < 0) 
)) { 
… 
file = fget(iocb->aio_fildes); 
req = aio_get_req(ctx); /* returns with 2 references to req */ 
if (unlikely(!req)) { 
… 
req->ki_filp = file; 
ret = put_user(req->ki_key, &user_iocb->aio_key); 
if (unlikely(ret)) { 
dprintk("EFAULT: aio_key/n"); 
goto out_put_req; 

req->ki_obj.user = user_iocb; 
req->ki_user_data = iocb->aio_data; 
req->ki_pos = iocb->aio_offset; 
req->ki_buf = (char __user *)(unsigned long)iocb->aio_buf; 
req->ki_left = req->ki_nbytes = iocb->aio_nbytes; 
req->ki_opcode = iocb->aio_lio_opcode; 
init_waitqueue_func_entry(&req->ki_wait, aio_wake_function);//注册唤醒时的回调函数 
INIT_LIST_HEAD(&req->ki_wait.task_list); 
req->ki_retried = 0; 
ret = aio_setup_iocb(req);//设置retry回调函数 
if (ret) 
goto out_put_req; 
spin_lock_irq(&ctx->ctx_lock); 
aio_run_iocb(req);//首先先执行一次,没准一次就能成功,要不怕做无用功,内核真是精打细算啊 
if (!list_empty(&ctx->run_list)) { 
/* drain the run list */ 
while (__aio_run_iocbs(ctx))//如果当前异步上下文的运行队列有请求,执行之! 


spin_unlock_irq(&ctx->ctx_lock); 
aio_put_req(req); /* drop extra ref to req */ 
return 0; 
… 

我们来看看aio_run_iocb函数,实际上__aio_run_iocbs(ctx)最终也是要调用aio_run_iocb函数的: 
static ssize_t aio_run_iocb(struct kiocb *iocb) 

struct kioctx *ctx = iocb->ki_ctx; 
ssize_t (*retry)(struct kiocb *); 
ssize_t ret; 
if (iocb->ki_retried++ > 1024*1024) { 
… 

if (!(iocb->ki_retried & 0xff)) { 

if (!(retry = iocb->ki_retry)) { 
… 

kiocbClearKicked(iocb); 
iocb->ki_run_list.next = iocb->ki_run_list.prev = NULL; 
spin_unlock_irq(&ctx->ctx_lock); 
/* Quit retrying if the i/o has been cancelled */ 
if (kiocbIsCancelled(iocb)) { 
… 

BUG_ON(current->io_wait != NULL); 
current->io_wait = &iocb->ki_wait;//以下3行很重要,如果当前请求未果,则可能睡眠在io_wait,当被唤醒的时候执行aio_wake_function 
ret = retry(iocb); 
current->io_wait = NULL; 
if (ret != -EIOCBRETRY && ret != -EIOCBQUEUED) { 
… 


注意aio_wake_function是唤醒回调函数,这个函数本质上也是执行将上下文挂入工作队列的任务,为什么呢?为何不让它直接把任务完成呢?因为它可能在中断上下文中,这又很多限制,比如不能睡眠,于是乎就把任务挂入一个工作队列,这样就有了进程的上下文,一切变得明朗!到这基本上就完事了,不用用户进程操心了,到了实在闲来无事的时候来取数据吧!如果实在觉得这篇文章到此意尤未尽,那么请看《linux工作队列和异步io 》下面将要进行的就是取数据了。一会回来,更精彩!

本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1274055

linux内核分析--异步io(二)相关推荐

  1. linux内核分析--异步io(一)

    linux2.6的内核增加了异步io,这个改动可以体现内核架构的重要性,连同epoll的内核实现,提升了io性能.碰巧的是,这两个特性都源自于同 一个本源,那就是睡眠队列的唤醒函数中增加了回调函数,这 ...

  2. Linux内核实验孟宁,《linux内核分析》实验二:时间片轮转多道程序运行原理

    一.概述 本文通过分析一个简单的时间片轮转多道程序的内核 mykernel,来理解操作系统是如何工作的. mykernel 是孟宁老师的一个开源项目,借助 Linux 内核部分源代码模拟存储程序计算机 ...

  3. 《Linux内核分析》(二)——从一个简单Linux内核分析进程切换原理

    转载:https://blog.csdn.net/FIELDOFFIER/article/details/44280717 <Linux内核分析>MOOC课程http://mooc.stu ...

  4. 操作系统与存储:解析Linux内核全新异步IO引擎io_uring设计与实现

    作者:draculaqian,腾讯后台开发工程师 引言 存储场景中,我们对性能的要求非常高.在存储引擎底层的IO技术选型时,可能会有如下讨论关于IO的讨论. http://davmac.org/dav ...

  5. Linux内核分析 - 网络[十二]:UDP模块 - 收发

    内核版本:2.6.34 UDP报文接收        UDP报文的接收可以分为两个部分:协议栈收到udp报文,插入相应队列中:用户调用recvfrom()或recv()系统调用从队列中取出报文,这里的 ...

  6. Linux内核分析 - 网络[十二]:UDP模块 - socket

    内核版本:2.6.34 这部分内容在于说明socket创建后如何被内核协议栈访问到,只关注两个问题:sock何时插入内核表的,sock如何被内核访问的.对于核心的sock的插入.查找函数都给出了流程图 ...

  7. Linux内核分析 笔记二 操作系统是如何工作的 ——by王玥

    一.知识要点 1.计算机是如何工作的?(总结)--三个法宝 存储程序计算机工作模型,计算机系统最最基础性的逻辑结构: 函数调用堆栈,高级语言得以运行的基础,只有机器语言和汇编语言的时候堆栈机制对于计算 ...

  8. linux内核分析(转自某位大哥网上的笔记)

    启动 当PC启动时,Intel系列的CPU首先进入的是实模式,并开始执行位于地址0xFFFF0处的代码,也就是ROM-BIOS起始位置的代码.BIOS先进行一系列的系统自检,然后初始化位于地址0的中断 ...

  9. LINUX内核分析第二周学习总结——操作系统是如何工作的

    LINUX内核分析第二周学习总结--操作系统是如何工作的 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course ...

最新文章

  1. create-react-native-app
  2. CORS跨域实现思路及相关解决方案
  3. 安全测试的目的,发现哪些问题
  4. Oracle建立约束、删除约束
  5. SharePoint 大局观(4)——从开发人员角度
  6. iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码...
  7. Unity3D实践3:BOSS血条
  8. Go新手上路(时不时更新)
  9. robotframe处理日志中文问题
  10. Matlab 图例 位置的不同命令
  11. matlab中全局变量的作用域,在simulink中使用全局变量的方法
  12. 一分钟了解”matlab对数函数log“
  13. 3DES加密,苹果、Java 、安卓 平台一致的加密工具
  14. 进入故障恢复控制台从此不需要密码
  15. 医院病案管理系统MRMS源码 病案管理 医院源码
  16. kali在高清屏幕下如何放大字体与图标
  17. Win10点击PowerShell显示找不到文件路径
  18. Constrained Joint CRF for Simultaneous FAUR and FLD
  19. 《微信小程序》初识微信小程序
  20. Ambari 自定义服务集成 | quicklinks 快速链接不显示的排查方案

热门文章

  1. javascript 未来新方法的介绍
  2. hdu3729(二分图)
  3. 不可错过的MSDN TV —— IronPython: Python on the .NET Framework (中)
  4. Stringlifier:Adobe 开源日志清理和凭据暴露检测工具
  5. 成都睿铂 | 落差区域无人机倾斜摄影航线的规划要点
  6. 好程序员web前端教程:字符串
  7. JavaSE复习(二)集合
  8. m_Orchestrate learning system---三十一、模板和需求的关系
  9. jQuery图片水平滑动延迟加载动画
  10. Swift 中的过滤器