阻塞在io_schedule里

在Android系统中遇到fsck_msdos进程因插拔存储卡而陷入D状态的问题,研究发现进程卡在sync_page的io_schedule,由于页锁导致进程无法继续。分析了同步页面、解锁流程以及块设备驱动的行为,揭示了在插拔过程中由于队列停止导致的请求未处理,使得某些页保持锁定状态,从而难以终止进程。解决方案在于移除队列中的请求以解锁页面。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近碰到一个问题,android平台,在执行fsck_msdos时拔卡,出现fsck_msdos进程状态为D。因为卡驱动插拔部分(非MMC那一套)我已经改成如果没有umount就不再进行新一轮的插卡检测,所以这里就不能正常跑了

 

1. 查看该进程堆栈 cat /proc/pid/stack

    看到停在sync_page里的io_schedule。单独看到io_schedule我以为和schedule差不多,往里看并没有设置进程状态为非RUNNING, 所以以为与schedule一样会得到再次调度,实际上io_schedule是需要唤醒的。wait_on_page_bit有将进程状态设置为TASK_UNINTERRUPTIBLE,再调用sync_page。

2. 阻塞在sync_page的io_schedule是因为page状态为lock。每个page操作期间都会上锁,如果此时状态为lock,就是同样的page被另一个进程正在访问,但ps查看所有进程并没有什么进程被阻塞,连底层card_queue_thread也是sleep状态。

3. 结果我花时间把ULK 相关3章看了一遍,还是没头绪,总认为是kernel本身的问题,认为可能是插拔这种非正常状态下导致某些路径未执行unlock_page解锁。花了好长时间在这个上面,加了无数打印看流程

4. 我的打印集中在block_read_full_page,

如果uptodate的就直接end_buffer_async_read,否则submit_bh->sumbit_bio到底层

对于page都是uptodate马上unlock。e

<think>嗯,用户这次聚焦在RxJava的retryWhen操作符上,看来是遇到了网络请求重试逻辑的具体实现问题。用户引用了四篇站内文档,其中引用[1]和[4]直接提到了retryWhen在错误重试机制中的应用,引用[2]则强调了RxJava在物联网场景处理异步事件的能力,这些正好能支撑解释retryWhen的核心价值。用户可能正在开发需要健壮网络请求的功能,比如移动端APP的登录模块或物联网设备通信层。ta特意提到“处理网络异常时的应用场景”,暗示需要实用案例而非纯理论,这点从ta引用的token机制(引用[1]第五条)和验证码功能(引用[1]第十四条)也能印证。深层需求可能是想解决自家项目中的网络抖动问题,同时避免粗暴的固定次数重试。retryWhen的难点在于理解其响应式重试机制——不是被动捕获异常,而是将异常事件转化为新的数据流决策。需要重点说明三点:1)如何用Func1函数将Throwable转化为重试触发器;2)为什么建议配合指数退避(引用[4]提到这个算法);3)内存泄漏风险(引用[13]提到过)。用户引用的资料,引用[4]关于.repeatWhen()/.retryWhen()的思考文档最相关,但要注意它的示例代码用了较旧的timer()操作符,按引用[3]的说明现在应改用interval()。对比用户引用的资料,发现有个关键细节:引用[1]的第六点“错误重试机制”和引用[4]都讨论了retryWhen,但用户没提repeatWhen。需要明确区分这两个操作符,避免用户混淆——retryWhen在onError时触发,而repeatWhen在onCompleted时触发,这点在源码设计上差异很大(retryWhen继承自RetryWhenHandler,repeatWhen则用RepeatWhenHandler)。最后要注意技术细节的准确性。比如引用[3]提到RxJava3移除了AndroidSchedulers.mainThread()的泄漏修复逻辑,那么在实现延迟重试时,若切主线程需自行处理Handler泄漏(虽然用户没直接问,但代码示例写Schdulers.io()就能规避这个问题)。</think> RxJava的`retryWhen`操作符是实现**智能错误重试机制**的核心工具,尤其在处理网络请求等不稳定性操作时至关重要。以下从逻辑原理、源码实现和应用场景三个层面详细解析: --- ### 一、操作逻辑原理 `retryWhen`的核心思想:**将原始流的异常事件转化为一个决定是否重试的决策流**。 其工作流程如下: 1. **监听异常**:当上游数据流抛出`Throwable`异常(如`IOException`)时,`retryWhen`捕获该异常 2. **生成决策流**:通过传入的`Func1<Observable<Throwable>, Observable<?>>`函数,将异常序列转化为新的“决策流” 3. **决策重试**: - 若决策流发送`onNext`事件(任意值),则重新订阅原始流(**重试**) - 若决策流发送`onError`/`onCompleted`事件,则终止流程(**不再重试**) ```java // 伪代码逻辑 sourceObservable .retryWhen(errors -> errors.flatMap(error -> { if (isRetryable(error)) { return Observable.timer(2, SECONDS); // 2秒后发送onNext触发重试 } else { return Observable.error(error); // 终止流程 } }) ); ``` --- ### 二、源码实现关键细节 1. **异常事件转换** 源码通过`OperatorRetryWithPredicate`(内部类)将上游异常事件投射到一个新Observable: ```java public void onError(Throwable e) { // 将异常发送给"决策流"生成函数 Observable<?> resume = handler.call(notificationHandler); // 监听决策流的结果 resume.subscribe(new InnerSubscriber()); } ``` 2. **重试控制** 决策流的`onNext`会触发`innerSubscription`重新订阅原始Observable: ```java if (value == NotificationLite.INSTANCE) { innerSubscription = subscribeToSource(); // 重新订阅核心方法 } ``` 3. **终止条件** 决策流若发出`onCompleted/onError`,则直接向下游传递对应事件,**结束重试循环**。 --- ### 三、网络异常处理的核心应用 #### 典型场景:指数退避重试(Exponential Backoff) ```java .retryWhen(errors -> errors .zipWith(Observable.range(1, MAX_RETRIES), (error, retryCount) -> retryCount) .flatMap(retryCount -> (retryCount > MAX_RETRIES) ? Observable.error(error) // 超过最大重试次数则失败 : Observable.timer((long) Math.pow(2, retryCount), SECONDS) // 指数延迟 ) ); ``` **优势**: ✅ 避免固定间隔重试对服务器的瞬时冲击 ✅ 根据重试次数动态延长等待时间(如 2s → 4s → 8s) ✅ 可控最大重试次数防止无限循环 #### 进阶场景:Token失效刷新后重试 结合`flatMap`实现凭证自动更新(引用[1]第五条的优化): ```java .retryWhen(errors -> errors.flatMap(error -> { if (error instanceof TokenExpiredException) { return refreshToken().flatMap(token -> Observable.just(token).delay(1, SECONDS) // 刷新后延迟重试 ); } return Observable.error(error); })); ``` --- ### 四、注意事项 1. **内存泄漏风险** 重试过程持有外部类引用时,需配合RxLifecycle或`CompositeDisposable`管理订阅 2. **线程切换** 建议在决策流中指定`observeOn(Schedulers.io())`,避免阻塞UI线程 (引用[3]中RemoteScheduler适用于跨进程场景) 3. **不可恢复错误** 需明确过滤非重试型异常(如`AuthenticationFailException`直接终止流程) ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值