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(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1, NULL)
3207 void fastcall __wake_up(wait_queue_head_t *q, unsigned int mode,
3208 int nr_exclusive, void *key)
3209 {
3210 unsigned long flags;
3211
3212 spin_lock_irqsave(&q->lock, flags);
3213 __wake_up_common(q, mode, nr_exclusive, 0, key);
3214 spin_unlock_irqrestore(&q->lock, flags);
3215 }
这里调用了__wake_up_common 3183 static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
3184 int nr_exclusive, int sync, void *key)
3185 {
3186 struct list_head *tmp, *next;
3187
3188 list_for_each_safe(tmp, next, &q->task_list) {
3189 wait_queue_t *curr;
3190 unsigned flags;
3191 curr = list_entry(tmp, wait_queue_t, task_list);
3192 flags = curr->flags;
3193 if (curr->func(curr, mode, sync, key) &&
3194 (flags & WQ_FLAG_EXCLUSIVE) &&
3195 !--nr_exclusive)
3196 break;
3197 }
3198 }
第3193行就是唤醒函数的调用,在进程因为种种苦衷必须休息一下的时候,调用
__add_wait_queue_tail
128 static inline void __add_wait_queue_tail(wait_queue_head_t *head,
129 wait_queue_t *new)
130 {
131 list_add_tail(&new->task_list, &head->task_list);
132 }
其中的参数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结束,整个架构就了解了。
 
1254 asmlinkage long sys_io_setup(unsigned nr_events, aio_context_t __user *ctxp)
1255 {
1256 struct kioctx *ioctx = NULL;
1257 unsigned long ctx;
1258 long ret;
1259
1260 ret = get_user(ctx, ctxp);
1261 if (unlikely(ret))
1262 goto out;
1263
1264 ret = -EINVAL;
1265 if (unlikely(ctx || nr_events == 0)) {
1266 pr_debug("EINVAL: io_setup: ctx %lu nr_events %u/n",
1267 ctx, nr_events);
1268 goto out;
1269 }
1270
1271 ioctx = ioctx_alloc(nr_events);
1272 ret = PTR_ERR(ioctx);
1273 if (!IS_ERR(ioctx)) {
1274 ret = put_user(ioctx->user_id, ctxp);
1275 if (!ret)
1276 return 0;
1277
1278 get_ioctx(ioctx); /* io_destroy() expects us to hold a ref */
1279 io_destroy(ioctx);
1280 }
1281
1282 out:
1283 return ret;
1284 }
ioctx = ioctx_alloc(nr_events)是这个函数的重点,它分配了一个kioctx结构并且初始化了一些字段
198 static struct kioctx *ioctx_alloc(unsigned nr_events)
199 {
200 struct mm_struct *mm;
201 struct kioctx *ctx;
202
203 /* Prevent overflows */
204 if ((nr_events > (0x10000000U / sizeof(struct io_event))) ||
205 (nr_events > (0x10000000U / sizeof(struct kiocb)))) {
206 pr_debug("ENOMEM: nr_events too high/n");
207 return ERR_PTR(-EINVAL);
208 }
209
210 if ((unsigned long)nr_events > aio_max_nr)
211 return ERR_PTR(-EAGAIN);
212
213 ctx = kmem_cache_alloc(kioctx_cachep, GFP_KERNEL);
214 if (!ctx)
215 return ERR_PTR(-ENOMEM);
216
217 memset(ctx, 0, sizeof(*ctx));
218 ctx->max_reqs = nr_events;
219 mm = ctx->mm = current->mm;
220 atomic_inc(&mm->mm_count);
221
222 atomic_set(&ctx->users, 1);
223 spin_lock_init(&ctx->ctx_lock);
224 spin_lock_init(&ctx->ring_info.ring_lock);
225 init_waitqueue_head(&ctx->wait);
226
227 INIT_LIST_HEAD(&ctx->active_reqs);
228 INIT_LIST_HEAD(&ctx->run_list);
229 INIT_WORK(&ctx->wq, aio_kick_handler, ctx);//声明了一个工作用于插入一个已经专门为aio定义好的工作队列,aio_kick_handler就是其执行工作函数
230
231 if (aio_setup_ring(ctx) < 0)
232 goto out_freectx;
233
234 /* limit the number of system wide aios */
235 spin_lock(&aio_nr_lock);
236 if (aio_nr + ctx->max_reqs > aio_max_nr ||
237 aio_nr + ctx->max_reqs < aio_nr)
238 ctx->max_reqs = 0;
239 else
240 aio_nr += ctx->max_reqs;
241 spin_unlock(&aio_nr_lock);
242 if (ctx->max_reqs == 0)
243 goto out_cleanup;
244
245 /* now link into global list. kludge. FIXME */
246 write_lock(&mm->ioctx_list_lock);
247 ctx->next = mm->ioctx_list;//这一句和下面的一句将这个kioctx连接到当前进程的mm->ioctx_list结构
248 mm->ioctx_list = ctx;
249 write_unlock(&mm->ioctx_list_lock);
250
251 dprintk("aio: allocated ioctx %p[%ld]: mm=%p mask=0x%x/n",
252 ctx, ctx->user_id, current->mm, ctx->ring_info.nr);
253 return ctx;
254
255 out_cleanup:
256 __put_ioctx(ctx);
257 return ERR_PTR(-EAGAIN);
258
259 out_freectx:
260 mmdrop(mm);
261 kmem_cache_free(kioctx_cachep, ctx);
262 ctx = ERR_PTR(-ENOMEM);
263
264 dprintk("aio: error allocating ioctx %p/n", ctx);
265 return ctx;
266 }
102 static int aio_setup_ring(struct kioctx *ctx)
103 {
104 struct aio_ring *ring;
105 struct aio_ring_info *info = &ctx->ring_info;
106 unsigned nr_events = ctx->max_reqs;
107 unsigned long size;
108 int nr_pages;
109
110 /* Compensate for the ring buffer's head/tail overlap entry */
111 nr_events += 2; /* 1 is required, 2 for good luck */
112
113 size = sizeof(struct aio_ring);
114 size += sizeof(struct io_event) * nr_events;
115 nr_pages = (size + PAGE_SIZE-1) >> PAGE_SHIFT;
116
117 if (nr_pages < 0)
118 return -EINVAL;
119
120 nr_events = (PAGE_SIZE * nr_pages - sizeof(struct aio_ring)) / sizeof(struct io_event);
121
122 info->nr = 0;
123 info->ring_pages = info->internal_pages;
124 if (nr_pages > AIO_RING_PAGES) {
125 info->ring_pages = kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL);
126 if (!info->ring_pages)
127 return -ENOMEM;
128 }
129
130 info->mmap_size = nr_pages * PAGE_SIZE;
131 dprintk("attempting mmap of %lu bytes/n", info->mmap_size);
132 down_write(&ctx->mm->mmap_sem);
133 info->mmap_base = do_mmap(NULL, 0, info->mmap_size,
134 PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE,
135 0);//在当前进程的地址空间映射一块虚拟区间
136 if (IS_ERR((void *)info->mmap_base)) {
137 up_write(&ctx->mm->mmap_sem);
138 printk("mmap err: %ld/n", -info->mmap_base);
139 info->mmap_size = 0;
140 aio_free_ring(ctx);
141 return -EAGAIN;
142 }
143
144 dprintk("mmap address: 0x%08lx/n", info->mmap_base);
145 info->nr_pages = get_user_pages(current, ctx->mm,
146 info->mmap_base, nr_pages,
147 1, 0, info->ring_pages, NULL);//得到刚才映射的区间的实际物理页的页描述符
148 up_write(&ctx->mm->mmap_sem);
149
150 if (unlikely(info->nr_pages != nr_pages)) {
151 aio_free_ring(ctx);
152 return -EAGAIN;
153 }
154
155 ctx->user_id = info->mmap_base;
156
157 info->nr = nr_events; /* trusted copy */
158
159 ring = kmap_atomic(info->ring_pages[0], KM_USER0);
160 ring->nr = nr_events; /* user copy */
161 ring->id = ctx->user_id;
162 ring->head = ring->tail = 0;
163 ring->magic = AIO_RING_MAGIC;
164 ring->compat_features = AIO_RING_COMPAT_FEATURES;
165 ring->incompat_features = AIO_RING_INCOMPAT_FEATURES;
166 ring->header_length = sizeof(struct aio_ring);
167 kunmap_atomic(ring, KM_USER0);
168
169 return 0;
170 }
以上的这些足以建立了前序工作,submit可以开始了,但是在说submit之前还得说一句,aio_setup_ring函数是
很重要的,因为它实现物理基础设施,如果说整个异步io是一座城市的管理方针发展战略的话,那么这个aio_ring
就是城市的工厂,下水道,车站等设施。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值