免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3883 | 回复: 6
打印 上一主题 下一主题

周日回来加班,小声问个关于workqueue问题 [复制链接]

论坛徽章:
0
1 [报告]
发表于 2012-02-19 12:20 |显示全部楼层
linux驱动里面使用create_singlethread_workqueue建立了一个单线程workqueue, 然后在中断处理函数中使用queue_work函数把任务放到workqueue中处理,但任务出来的速度比较慢,而中断的速度比较快,想问下这样长时间下会不会引起workqueue的 溢出或者是简单丢掉下任务?
      然后我看ULK中说flush_workqueue函数会等待工作队列中所以挂起的任务执行完成,但我的实践是调用flush后很快就返回了,好像没有去执行挂起的处理,是不是让workqueue丢掉了呢?

=============================================================

问题的关键在于,你是否会在中断处理函数中提交不同的struct work_struct对象,换句话说是否你在每次中断到来时要执行不同的操作,如果是,work_struct本身有链表机制,极端情形下导致内存分配失败是可能的。但是现实中,很少会在每次中断,比如网卡显示有数据到达时,提交不同的工作节点。如果你的中断处理函数中提交相同工作结点的话,《深入Linux设备驱动程序内核机制》 6.2.5节“提交工作节点queue_work"的叙述是:"(queue_work_on)函数首先检测work->data的WORK_STRUCT_PENDING位有没有被置1,置1的话意味着此前该work已被提交还没有处理,内核禁止驱动程序在一个工作结点还没处理完就再次提交该结点",所以即便中断发生了10次,但是工作结点可能只提交了一个。  简言之,work_queue应该按照要处理的不同任务提交不同节点,而代表相同任务的工作节点只应该出现一次。flush_workqueue使用COMPLETION机制确保所有在该函数调用前提交的节点都会被执行完毕,但是之后提交的节点它就管不着了。

评分

参与人数 1可用积分 +4 收起 理由
瀚海书香 + 4 讲解很到位!

查看全部评分

论坛徽章:
0
2 [报告]
发表于 2012-02-20 11:10 |显示全部楼层
楼上的其实也不用担心丢掉的工作节点,因为假设网卡自己数据没丢,你只要提交了一个节点,当被调度执行时还是会给DMA到系统内存中的。这个跟softirq机制差不多:同一件事提交一次就够了。
另外感谢版主加分,呵呵。

论坛徽章:
0
3 [报告]
发表于 2012-02-20 13:10 |显示全部楼层
这样举个例子会更清楚:

如果3个中断源(S1,S2,S3)共用一个中断信号: INTn
S1 对应的中断下半部为 tasklet1
S2 对应的中断下半部为  tasklet2
S3 对应的中断下半部为  tasklet2
同一个中断信号其下半部有可能重叠:如中断源 S1/S2通过中断INTn能重叠提交他们的下半部。
但中断源S2/S3其下半部就不可同时提交。在实际实现时最好秉承哪里的问题哪里解决的原则,不完全寄希望于系统,在执行完tasklet2之前屏蔽掉S2/S3,这样interrupt reentry就不会发生,有利于系统的性能提高。
============================================================

不错,很好的例子。最后一点,tasklet(softirq的一种)在执行时属于中断上下文, in_interrupt(), 所以tasklet在执行时,不会有reentry的问题。

论坛徽章:
0
4 [报告]
发表于 2012-02-20 13:38 |显示全部楼层
本帖最后由 MagicBoy2010 于 2012-02-20 13:40 编辑

是的, softirq不会reentry. 我的本意是:在tasklet2执行完之前,INTn不要再被S2/S3触发,s2/s3中断的前半部也不应该发生。因为即使触发了,其下半部也会被忽略,白白增加系统中断负担。
==================================================================
事实上在整个tasklet执行过程中,内核都是将外部中断打开的,所以tasklet执行时,是允许外部中断进来的。你的说法恰好违反了softirq的本意,因为s2/s3的前半部分极大的可能是会重新提交一个tasklet,虽然这次的中断不会导致reentry正在执行的tasklet,但是tasklet在执行时会重新检查有无新的tasklet正在被加入(pending), 具体代码框架:

__do_softirq(void)
{
    ...
restart:
     ...
     do{
     ..
     }while(pending);
   
    pending = local_softirq_pending(); //重新检查有无新的softirq pending...
    if(pending && --max_restart)
       goto restart;

论坛徽章:
0
5 [报告]
发表于 2012-02-20 13:52 |显示全部楼层
除非出于保护系统共享资源的需要,系统应该尽可能地开放响应外部中断的能力,否则会带来很多问题。这也是为什么Linux内核有hardirq和softirq的主要原因。

论坛徽章:
0
6 [报告]
发表于 2012-02-21 10:51 |显示全部楼层
只屏蔽S2/S3中断源,其他中断都开放;如果S2/S3再次发生中断, 只能执行前半部没有任何意义。屏蔽S2/S3对其他中断没有任何影响。

============================
假设S2/S3的设备正在处理一个softirq时,其上的设备接收到了,比如最后一个数据包,在它的softirq部分屏蔽其硬件中断,潜在的问题是:1. 硬件中断的丢失, 2. 因为最后一个数据包接收的中断被屏蔽,hardirq无法进入,因此无法标记一个新的softirq需要处理,结果是最后一个数据包无法被接收...
   

论坛徽章:
0
7 [报告]
发表于 2012-02-21 14:19 |显示全部楼层
你的这种情况即使S2/S3中断打开,还是丢包了。S2/S3中断打开,hardirq得以运行,但tasklet2不能运行, 数据还是得不到处理;反而没必要的运行了有S2/S3触发的hardirq. 我这里不是把中断信号INT屏蔽掉, 是屏蔽S2/S3中断源; S2/S3不能在触发中断到INT上,但S1还是可以继续中断的。

===============================================
兄弟,我认为我已经讲得很清楚了,"S2/S3中断打开,hardirq得以运行,但tasklet2不能运行, 数据还是得不到处理", hardirq部分可以raise_softirq_irqoff(TASKLET_SOFTIRQ),这样导致8楼中的代码在do...while...循环后的pending!=0,将goto restart,换句话说在一个softirq处理过程中处理了两次外部中断,而屏蔽S2/S3,8楼中的代码在do...while...之后的pending==0,这个softirq就结束了,它不会再去处理最后一个数据包了。8楼列出的代码我认为已经足够表述softirq的运作机制了:softirq部分,内核会主动打开硬件中断(local_irq_enable),这意味着要让处理器尽快可以接收外部中断。假设一个设备只会发生两次中断,A和B,在A发生时导致它的softirq部分执行,在softirq部分执行时,中断B发生,B最有可能的动作是标示一个新的softirq,但是B的hardirq部分结束后将不会重入A引起的softirq,详见do_softirq代码:if(in_interrupt()) return. 但是B标示的一个pending位被记录了下来,所以在A的softirq部分do...while...循环之后会重新读取pending = local_softirq_pending()。看来明天的博客内容又有新的题材了。。。



   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP