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

简介:

linux2.6的内核增加了异步io,这个改动可以体现内核架构的重要性,连同epoll的内核实现,提升了io性能。碰巧的是,这两个特性都源自于同 一个本源,那就是睡眠队列的唤醒函数中增加了回调函数,这就可以让用户实现自己的唤醒策略,结果是异步io和epoll都用到了唤醒回调函数,只是实现不 同,本文先讨论异步io,下一篇文章讨论epoll。 
本人文笔不甚好,前面的话我自己都感觉不知所云,还是代码可以说明一切问题(在此小声说一句,哪位志同道合的要想研究内核,千万别买国产的内核分析之类的书,全是垃圾!还是自己牢牢实实读代码的好!) 
先说一下睡眠队列,它被定义为:

typedef struct __wait_queue wait_queue_t
struct __wait_queue { 
unsigned int flags
#define WQ_FLAG_EXCLUSIVE 0x01 
void *private
wait_queue_func_t func
struct list_head task_list; 
}; 
typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int sync, void *key); 
这个func就是前面所说的回调函数,至于怎么实现,就要看你的需求了,如果一个进程位于一个睡眠队列__wait_queue_head中,当它被唤醒的时候,过程是这样的:调用wake_up 
#define wake_up(x__wake_up(xTASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1, NULL
void fastcall __wake_up(wait_queue_head_t *q, unsigned int mode
int nr_exclusive, void *key

unsigned long flags
spin_lock_irqsave(&q->lockflags); 
__wake_up_common(q, mode, nr_exclusive, 0, key); 
spin_unlock_irqrestore(&q->lockflags); 

这里调用了__wake_up_common 3183 static void __wake_up_common(wait_queue_head_t *q, unsigned int mode
int nr_exclusive, int sync, void *key

struct list_head *tmp, *next
list_for_each_safe(tmpnext, &q->task_list) { 
wait_queue_t *curr; 
unsigned flags
curr = list_entry(tmpwait_queue_t, task_list); 
flags = curr->flags
if (curr->func(curr, modesynckey) && 
(flags & WQ_FLAG_EXCLUSIVE) && 
!--nr_exclusive) 
break; 


第3193行就是唤醒函数的调用,在进程因为种种苦衷必须休息一下的时候,调用 
__add_wait_queue_tail 
static inline void __add_wait_queue_tail(wait_queue_head_t *head
wait_queue_t *new

list_add_tail(&new->task_list, &head->task_list); 

其中的参数new就是一个睡眠体,他的唤醒函数就是上面说的可以自定义的唤醒函数。现在整体过程已经了解了,总结起来一句话,该休息时就睡眠,该工作时被唤醒,拒绝soho,拒绝杂乱! 
现在看看怎么实现异步io,所谓异步io,就是不同步的io---废话!此时的多么惭愧,一句很容易理解的话硬是不知道该怎么说,是这样的:异步io和非阻塞io是根本不同的,非阻塞的意思是当阻塞的时候返回,但是实际上什么也没有做,仅仅得到了一个信息:我现在很忙。言外之意就是:你等会再来。实际上你得一直询问:你还忙吗?直到数据到位了,你再询问,才能真正返回你要的结果,整个过程都是你自己单枪匹马在劳作,别人只给你信息,并不会帮你;而异步io不是这样,异步io当遇到阻塞时也是直接返回,这点和非阻塞io一致,但是它在返回前要交待一下自己的要求,为什么呢?自己的要求给谁交待呢?为何要交待呢?难道要将任务转手,自己的事情推给别人不成,就像我们公司的某某人,某某现在正在写博客的人!是的,正是这样,进程要把请求交待给内核,让内核帮它,其实并不一定非得交待给内核,按照posix的异步io的语义,只要不是它自己干都行,随便交待给一个别的可运行实体都是可以的。 
以上就是二者的区别,从内核的发展来看,Linux内核越来越往posix靠拢。说的不少了,还是分析代码吧: 
异步io涉及几个系统调用: 
io_setup 
io_submit 
io_getevents 
io_destroy 
大家可以自己在网上的man手册里查看以上函数的用法。以上函数对应4个系统调用: 
sys_io_setup 
sys_io_submit 
sys_io_getevents sys_io_destroy 
从分析sys_io_setup开始,一直到sys_io_getevents结束,整个架构就了解了。 
asmlinkage long sys_io_setup(unsigned nr_events, aio_context_t __user *ctxp) 

struct kioctx *ioctx = NULL
unsigned long ctx
long ret; 
ret = get_user(ctx, ctxp); 
if (unlikely(ret)) 
goto out
ret = -EINVAL
if (unlikely(ctx || nr_events == 0)) { 
… 

ioctx = ioctx_alloc(nr_events); 
ret = PTR_ERR(ioctx); 
if (!IS_ERR(ioctx)) { 
ret = put_user(ioctx->user_id, ctxp); 
if (!ret) 
return 0; 
get_ioctx(ioctx); /* io_destroy() expects us to hold a ref */ 
io_destroy(ioctx); 

out
return ret; 

ioctx = ioctx_alloc(nr_events)是这个函数的重点,它分配了一个kioctx结构并且初始化了一些字段 
static struct kioctx *ioctx_alloc(unsigned nr_events) 

struct mm_struct *mm; 
struct kioctx *ctx
/* Prevent overflows */ 
if ((nr_events > (0x10000000U / sizeof(struct io_event))) || 
(nr_events > (0x10000000U / sizeof(struct kiocb)))) { 
… 

if ((unsigned long)nr_events > aio_max_nr
return ERR_PTR(-EAGAIN); 
ctx = kmem_cache_alloc(kioctx_cachepGFP_KERNEL); 
… 
memset(ctx, 0, sizeof(*ctx)); 
ctx->max_reqs = nr_events; 
mm = ctx->mm = current->mm; 
atomic_inc(&mm->mm_count); 
atomic_set(&ctx->users, 1); 
spin_lock_init(&ctx->ctx_lock); 
spin_lock_init(&ctx->ring_info.ring_lock); 
init_waitqueue_head(&ctx->wait); 
INIT_LIST_HEAD(&ctx->active_reqs); 
INIT_LIST_HEAD(&ctx->run_list); 
INIT_WORK(&ctx->wq, aio_kick_handlerctx);//声明了一个工作用于插入一个已经专门为aio定义好的工作队列,aio_kick_handler就是其执行工作函数 
if (aio_setup_ring(ctx) < 0) 
goto out_freectx; 
/* limit the number of system wide aios */ 
spin_lock(&aio_nr_lock); 
if (aio_nr + ctx->max_reqs > aio_max_nr || 
aio_nr + ctx->max_reqs < aio_nr
ctx->max_reqs = 0; 
else 
aio_nr += ctx->max_reqs; 
spin_unlock(&aio_nr_lock); 
if (ctx->max_reqs == 0) 
goto out_cleanup; 
write_lock(&mm->ioctx_list_lock); 
ctx->next = mm->ioctx_list;//这一句和下面的一句将这个kioctx连接到当前进程的mm->ioctx_list结构 
mm->ioctx_list = ctx
write_unlock(&mm->ioctx_list_lock); 
return ctx
… 

static int aio_setup_ring(struct kioctx *ctx

struct aio_ring *ring
struct aio_ring_info *info = &ctx->ring_info
unsigned nr_events = ctx->max_reqs; 
unsigned long size
int nr_pages; 
/* Compensate for the ring buffer's head/tail overlap entry */ 
nr_events += 2; /* 1 is required, 2 for good luck */ 
size = sizeof(struct aio_ring); 
size += sizeof(struct io_event) * nr_events; 
nr_pages = (size + PAGE_SIZE-1) >> PAGE_SHIFT
if (nr_pages < 0) 
return -EINVAL
nr_events = (PAGE_SIZE * nr_pages - sizeof(struct aio_ring)) / sizeof(struct io_event); 
info->nr = 0; 
info->ring_pages = info->internal_pages; 
if (nr_pages > AIO_RING_PAGES) { 
info->ring_pages = kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL); 
if (!info->ring_pages) 
return -ENOMEM

info->mmap_size = nr_pages * PAGE_SIZE
dprintk("attempting mmap of %lu bytes/n", info->mmap_size); 
down_write(&ctx->mm->mmap_sem); 
info->mmap_base = do_mmap(NULL, 0, info->mmap_size, 
PROT_READ|PROT_WRITEMAP_ANON|MAP_PRIVATE
0);//在当前进程的地址空间映射一块虚拟区间 
if (IS_ERR((void *)info->mmap_base)) { 
… 

dprintk("mmap address: 0x%08lx/n", info->mmap_base); 
info->nr_pages = get_user_pages(currentctx->mm, 
info->mmap_base, nr_pages, 
1, 0, info->ring_pages, NULL);//得到刚才映射的区间的实际物理页的页描述符 
up_write(&ctx->mm->mmap_sem); 
if (unlikely(info->nr_pages != nr_pages)) { 
aio_free_ring(ctx); 
return -EAGAIN

ctx->user_id = info->mmap_base
info->nr = nr_events; /* trusted copy */ 
ring = kmap_atomic(info->ring_pages[0], KM_USER0); 
ring->nr = nr_events; /* user copy */ 
ring->id = ctx->user_id; 
ring->head = ring->tail = 0; 
ring->magic = AIO_RING_MAGIC
ring->compat_features = AIO_RING_COMPAT_FEATURES
ring->incompat_features = AIO_RING_INCOMPAT_FEATURES
ring->header_length = sizeof(struct aio_ring); 
kunmap_atomic(ring, KM_USER0); 
return 0; 

以上的这些足以建立了前序工作,submit可以开始了,但是在说submit之前还得说一句,aio_setup_ring函数是很重要的,因为它实现物理基础设施,如果说整个异步io是一座城市的管理方针发展战略的话,那么这个aio_ring就是城市的工厂,下水道,车站等设施。 
本来准备把所有这些整合到一篇的,可是写着写着发现自己累了,休息一下......



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

相关文章
|
4天前
|
算法 Linux 调度
深入理解Linux内核调度器:从基础到优化####
本文旨在通过剖析Linux操作系统的心脏——内核调度器,为读者揭开其高效管理CPU资源的神秘面纱。不同于传统的摘要概述,本文将直接以一段精简代码片段作为引子,展示一个简化版的任务调度逻辑,随后逐步深入,详细探讨Linux内核调度器的工作原理、关键数据结构、调度算法演变以及性能调优策略,旨在为开发者与系统管理员提供一份实用的技术指南。 ####
21 4
|
8天前
|
缓存 算法 Linux
深入理解Linux内核调度器:公平性与性能的平衡####
真知灼见 本文将带你深入了解Linux操作系统的核心组件之一——完全公平调度器(CFS),通过剖析其设计原理、工作机制以及在实际系统中的应用效果,揭示它是如何在众多进程间实现资源分配的公平性与高效性的。不同于传统的摘要概述,本文旨在通过直观且富有洞察力的视角,让读者仿佛亲身体验到CFS在复杂系统环境中游刃有余地进行任务调度的过程。 ####
29 6
|
6天前
|
缓存 资源调度 安全
深入探索Linux操作系统的心脏——内核配置与优化####
本文作为一篇技术性深度解析文章,旨在引领读者踏上一场揭秘Linux内核配置与优化的奇妙之旅。不同于传统的摘要概述,本文将以实战为导向,直接跳入核心内容,探讨如何通过精细调整内核参数来提升系统性能、增强安全性及实现资源高效利用。从基础概念到高级技巧,逐步揭示那些隐藏在命令行背后的强大功能,为系统管理员和高级用户打开一扇通往极致性能与定制化体验的大门。 --- ###
27 9
|
5天前
|
缓存 负载均衡 Linux
深入理解Linux内核调度器
本文探讨了Linux操作系统核心组件之一——内核调度器的工作原理和设计哲学。不同于常规的技术文章,本摘要旨在提供一种全新的视角来审视Linux内核的调度机制,通过分析其对系统性能的影响以及在多核处理器环境下的表现,揭示调度器如何平衡公平性和效率。文章进一步讨论了完全公平调度器(CFS)的设计细节,包括它如何处理不同优先级的任务、如何进行负载均衡以及它是如何适应现代多核架构的挑战。此外,本文还简要概述了Linux调度器的未来发展方向,包括对实时任务支持的改进和对异构计算环境的适应性。
23 6
|
6天前
|
缓存 Linux 开发者
Linux内核中的并发控制机制:深入理解与应用####
【10月更文挑战第21天】 本文旨在为读者提供一个全面的指南,探讨Linux操作系统中用于实现多线程和进程间同步的关键技术——并发控制机制。通过剖析互斥锁、自旋锁、读写锁等核心概念及其在实际场景中的应用,本文将帮助开发者更好地理解和运用这些工具来构建高效且稳定的应用程序。 ####
23 5
|
6天前
|
算法 Unix Linux
深入理解Linux内核调度器:原理与优化
本文探讨了Linux操作系统的心脏——内核调度器(Scheduler)的工作原理,以及如何通过参数调整和代码优化来提高系统性能。不同于常规摘要仅概述内容,本摘要旨在激发读者对Linux内核调度机制深层次运作的兴趣,并简要介绍文章将覆盖的关键话题,如调度算法、实时性增强及节能策略等。
|
7天前
|
存储 监控 安全
Linux内核调优的艺术:从基础到高级###
本文深入探讨了Linux操作系统的心脏——内核的调优方法。文章首先概述了Linux内核的基本结构与工作原理,随后详细阐述了内核调优的重要性及基本原则。通过具体的参数调整示例(如sysctl、/proc/sys目录中的设置),文章展示了如何根据实际应用场景优化系统性能,包括提升CPU利用率、内存管理效率以及I/O性能等关键方面。最后,介绍了一些高级工具和技术,如perf、eBPF和SystemTap,用于更深层次的性能分析和问题定位。本文旨在为系统管理员和高级用户提供实用的内核调优策略,以最大化Linux系统的效率和稳定性。 ###
|
6天前
|
Java Linux Android开发
深入探索Android系统架构:从Linux内核到应用层
本文将带领读者深入了解Android操作系统的复杂架构,从其基于Linux的内核到丰富多彩的应用层。我们将探讨Android的各个关键组件,包括硬件抽象层(HAL)、运行时环境、以及核心库等,揭示它们如何协同工作以支持广泛的设备和应用。通过本文,您将对Android系统的工作原理有一个全面的认识,理解其如何平衡开放性与安全性,以及如何在多样化的设备上提供一致的用户体验。
|
6天前
|
缓存 运维 网络协议
深入Linux内核架构:操作系统的核心奥秘
深入Linux内核架构:操作系统的核心奥秘
22 2
|
8天前
|
监控 网络协议 算法
Linux内核优化:提升系统性能与稳定性的策略####
本文深入探讨了Linux操作系统内核的优化策略,旨在通过一系列技术手段和最佳实践,显著提升系统的性能、响应速度及稳定性。文章首先概述了Linux内核的核心组件及其在系统中的作用,随后详细阐述了内存管理、进程调度、文件系统优化、网络栈调整及并发控制等关键领域的优化方法。通过实际案例分析,展示了这些优化措施如何有效减少延迟、提高吞吐量,并增强系统的整体健壮性。最终,文章强调了持续监控、定期更新及合理配置对于维持Linux系统长期高效运行的重要性。 ####