Linux wait_on_buffer函数研究

本文详细解析了Linux0.11内核中的wait_on_buffer函数,探讨了其工作原理,特别是在内核态下如何通过禁用和启用中断来保护临界区,防止并发操作带来的问题。

Linux0.11内核中的wait_on_buffer和wait_on_inode函数是非常有代表性的延迟性函数处理过程,网上关于这两个函数的讨论

也很多,最主要的一个问题是为什么要在判断b_lock之前关中断,这个问题也困扰了我很长时间,查了不少帖子,学到不少东西,但总是

觉得有些细节没有弄清楚,因此借着自己实践开发一个小OS的过程,研究了一下wait_on_buffer,还是学到不少东西的。贴出来,分享

给大家,欢迎讨论~

Linux0.11版函数定义:

static inline void wait_on_buffer(struct buffer_head * bh)
{
cli();
while (bh->b_lock)
sleep_on(&bh->b_wait);
sti();
}

分析如下:

1. 首先可以明确,wait_on_buffer是工作在内核态的函数。更进一步应当理解为两种可能性:

或者是当前某个用户态的进程请求数据进而引发的,那么可以认为是当前进程进入了内核态;又或者是内核本身的某个任务

引发的操作。不管怎样wait_on_buffer背后是代表这某个进程或者任务的。

2. wait_on_buffer中通过cli和sti起到一个保护临界区的作用,为什么这么说?

原因首先还在于wait_on_buffer是工作在内核态的环境下,此时cli就意味着关闭了所有的可屏蔽中断,如时钟中断和硬盘

处理中断等等,这也就意味着:

1)当前工作在用户态的进程不会因为时间片到了有可能被轮转出去,切换成其他的进程,从而保证while(bh->b_lock)形成的

若干条指令中间不会被打断,造成可能有别的进程修改了bh->b_lock的值,造成错误;

2)不会造成硬盘中断的到来引发的其他进程对bh->b_lock的操作,进而造成错误。

因此,通过关闭中断,可以达到while (bh->b_lock)语句在执行期间被他人修改,从而实现临界区的效果。

关于这一点,第3版的《Understanding the Linux Kernel》在第五章(内核同步)里明确也提到,确保一组内核语句被当作

一个临界区处理的主要机制之一就是中断禁止,可惜本人在上学时学的OS教材是理论性较强的那种,只关注了各种各样的临界操

作模型,没有提到实现机制,导致很长时间都觉得临界区的概念很虚,不好掌握。

3.还有一个重要的问题就是如果关闭了中断,那别的用户进程怎么办?是不是整个系统在这段时间内都不能收到相关的中断信号

了?这个问题也有很多人问,答案当然是不会影响其他的用户进程,原因在于sleep_on在将当前进程挂起后,会执行一次进程调

度操作,即schedule(),而一旦选择了新进程进行切换时,会将新进程的EFLAGS寄存器内容也加载到相应的寄存器的,而这里当

然包含了IF(Interrupt Flag)位,也就是说如果此时切换过来的进程是使用自己原先的中断开关状态的,原来的开就开,原来的关

就关,和之前的进程无关。因此,wait_on_buffer里对中断的操作,只影响当时的进程,故这个中断也被称为“本地中断”,这

个称呼更能反映其实质吧。

static int binder_thread_read(struct binder_proc *proc, struct binder_thread *thread, binder_uintptr_t binder_buffer, size_t size, binder_size_t *consumed, int non_block) { void __user *buffer = (void __user *)(uintptr_t)binder_buffer; void __user *ptr = buffer + *consumed; void __user *end = buffer + size; int ret = 0; bool nothing_to_do = false; bool force_spawn = false; int wait_for_proc_work; 1. Condition *consumed == 0, taking true branch. if (*consumed == 0) { 2. Condition __range_ok(__p, 4UL /* sizeof (*__p) */), taking true branch. 3. Switch case value 4. 4. Breaking from switch. 5. Falling through to end of if statement. 6. Condition ({...; __pu_err;}), taking false branch. if (put_user(BR_NOOP, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); } retry: 7. lock: Locking proc->inner_lock. binder_inner_proc_lock(proc); 8. def: Assigning data that might be protected by the lock to wait_for_proc_work. wait_for_proc_work = binder_available_for_proc_work_ilocked(thread); 9. unlock: Unlocking proc->inner_lock. wait_for_proc_work might now be unreliable because other threads can now change the data that it depends on. binder_inner_proc_unlock(proc); thread->looper |= BINDER_LOOPER_STATE_WAITING; 10. Condition wait_for_proc_work, taking false branch. 11. Condition !!thread->transaction_stack, taking true branch. 12. unlock: Unlocking proc->inner_lock. wait_for_proc_work might now be unreliable because other threads can now change the data that it depends on. 13. Condition !binder_worklist_empty(proc, &thread->todo), taking true branch. trace_binder_wait_for_work(wait_for_proc_work, !!thread->transaction_stack, !binder_worklist_empty(proc, &thread->todo)); 14. Condition wait_for_proc_work, taking false branch. if (wait_for_proc_work) { if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED))) { binder_user_error("%d:%d ERROR: Thread waiting for process work before calling BC_REGISTER_LOOPER or BC_ENTER_LOOPER (state %x)\n", proc->pid, thread->pid, thread->looper); wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); } trace_android_vh_binder_restore_priority(NULL, current); binder_restore_priority(thread, &proc->default_priority); } 15. Condition non_block, taking true branch. if (non_block) { 16. Condition wait_for_proc_work, taking false branch. 17. Condition !binder_has_work(thread, wait_for_proc_work), taking false branch. if (!binder_has_work(thread, wait_for_proc_work)) ret = -EAGAIN; 18. Falling through to end of if statement. } else { ret = binder_wait_for_work(thread, wait_for_proc_work); } thread->looper &= ~BINDER_LOOPER_STATE_WAITING; 19. Condition ret, taking false branch. if (ret) return ret; while (1) { uint32_t cmd; struct binder_transaction_data_secctx tr; struct binder_transaction_data *trd = &tr.transaction_data; struct binder_work *w = NULL; struct list_head *list = NULL; struct binder_transaction *t = NULL; struct binder_thread *t_from; size_t trsize = sizeof(*trd); 20. lockagain: Locking proc->inner_lock again. binder_inner_proc_lock(proc); 178628: (#2 of 2): Value not atomically updated (ATOMICITY) use: Using an unreliable value of wait_for_proc_work inside the second locked section. If the data that wait_for_proc_work depends on was changed by another thread, this use might be incorrect. CID 178628:(#1 of 2):Value not atomically updated (ATOMICITY) [ "select issue" ] trace_android_vh_binder_select_worklist_ilocked(&list, thread, proc, wait_for_proc_work); trace_android_vh_binder_select_special_worklist(&list, thread, proc, wait_for_proc_work, &nothing_to_do); if (list) goto skip; else if (nothing_to_do) goto no_work; if (!binder_worklist_empty_ilocked(&thread->todo)) list = &thread->todo; else if (!binder_worklist_empty_ilocked(&proc->todo) && wait_for_proc_work) list = &proc->todo; else { } 这段代码报了 值未进行原子更新 帮我看一下
最新发布
06-18
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值