免费注册 查看新帖 |

Chinaunix

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

关于事件调度 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-03-16 13:02 |只看该作者 |倒序浏览
之前一直是做ecos的,ecos是线程调度,优点就是调度开销小,而且单个线程可以复用处理多种事件,看了关于事件的调度简单的介绍,有个问题
如果用事件调度了,也就是每个事件都会建立一个新的线程,也就是说当系统繁忙的时候,我们会需要运行一百或几百个线程,还没细研究资源调度内核上是通过什么来尽兴的
如果是通过线程,那么几百个线程的调度还是很耗费事件的吧,而且做嵌入式的都知道,创建和删除线是很耗费CPU资源的,而事件调度设计上就要频繁的做这种处理。
以上是一些小疑问,如果版主有事件希望给些讲解。
支持都江堰,希望越走越好。

论坛徽章:
0
2 [报告]
发表于 2009-03-16 14:41 |只看该作者
创建线程因为要调用malloc函数,需要花费有一定的开销,对实时系统来说是有影响,即使不是实时系统,每次事件都创建新线程也是不划算的。djyos对这种状况做了改进,方法是:
    1、紧急事件在登记事件类型的时候创建一个线程,但该线程并不投入运行(不启动),待事件发生并被调度器投入运行时,该线程立即分配给事件,当事件处理完毕以后,如果该事件使用的是该事件类型的最后一个线程,则线程不销毁,待再次发生事件时重复使用。而分配线程只需要一条赋值操作。
    2、无论是紧急事件还是普通事件,如果事件接踵发生,即前一条事件没处理完,后一天事件又发生,则前一条事件处理完毕后,线程并不销毁,而是把线程复位后直接转交给后续事件,重新启动线程后就可以处理后一条事件。转交事件也只需要一条赋值操作。
    3、如果是mark型事件(参见《都江堰操作系统与嵌入式系统设计》的第4.3.13节),则可以把接踵弹出的事件一次处理,无需新开线程。
    情况1和2,都需要在事件发生后,重启线程以处理事件,与传统的线程休眠等候事件发生的操作系统比,启动线程比直接的山下文切换来说,确实要慢一点点,你可以理解为:传统方法是汽车怠速等候,乘客上车以后立即开动车子,djyos的方法是汽车熄火等候,乘客上车后点火启动。而当今,CPU的运行速度很快,价格也低至几分钱每MIPS,人力资源成本却在上升,让CPU多干点,让程序员少干点吧!x86系列的CPU主频不断提高,你见到windows的运行速度有提高吗?
    4、djyos的内存管理算法也有巨大的改进,与传统用链表管理堆、使malloc函数调用执行时间不确定而且可能很长相比,djyos执行malloc的时间是确定而且速度很快,这也加快了创建线程的速度。比如,在4G的寻址空间内,4k为一页,寻找一个空闲页,无论该空闲页的位置在哪里,无论存储器的当前使用情况如何,定位一个空闲页只需要:4次比较、4次乘法和4次加法。(详情参见《都江堰操作系统和嵌入式系统设计》第7章),然后再用稍长一些的时间来填充内存分配位图,即可完成内存分配。
    通常,堆内存管理被认为是非实时组件,但在djyos中,只要不出现无资源可分配的情况,djyos的堆内存管理是一个实时组件。
    至于一个线程处理多个事件,应该属于事件的设计技巧问题,你把“送信”设计成一个事件类型,还是分成“收取信件”和“投递信件”两个,是应用程序的一种策略而已。

论坛徽章:
0
3 [报告]
发表于 2009-03-16 22:58 |只看该作者
首先谢谢版主的耐心解答,event type应该是下面这个结构
struct event_type
{
    //同一类型的事件可以有多条正在执行或等待调度,但这些事件有相同的属性.
    struct evtt_property    property;
    //空闲线程指针,分配线程给事件时,优先从这里分配
    struct  thread_vm  *my_free_vm;
    char *evtt_name;    //事件类型允许没有名字,但只要有名字,就不允许同名
                        //如果一个类型不希望别的模块弹出事件,可以不用名字。
                        //如模块间需要交叉弹出事件,用名字访问。
    //优先级小于0x80为紧急优先级,它影响虚拟机的构建.类型优先级在初始化时设定,
    ufast_t     default_prio;       //事件类型优先级.不同于事件优先级,1~255,0非法.
    uint16_t    pop_sum,done_sum;   //已弹出事件总数,已完成事件总数
    uint16_t    repeats;    //未开始处理的该类型事件发生的次数
    uint16_t    vpus_limit;  //本类型事件允许同时建立的线程虚拟机个数
    uint16_t    vpus;       //本类型事件已经拥有的线程虚拟机个数
    void (*thread_routine)(struct event_script *my_event);//函数指针,可能是死循环.
    uint32_t stack_size;              //thread_routine所需的栈大小

    //mark_unclear指针指向mark型尚未被clear的事件。
    //在pop一个mark型事件时,若队列中已经有该类型的、未被clear的事件,则只是增加
    //该类型的repeats数,若又是参数替换型事件类型,替换事件参数。这种情况下,
    //y_event_pop函数的参数prio是无效的。
    //若该mark型事件没有未clear的事件,但仍有事件在处理,则新事件并不加入就绪
    //队列,而是挂在mark_unclear指针下,待正在处理的事件done时把它加入就绪队列。
    //若该mark型事件既无未clear的事件,又没有正在处理的事件,则直接加入就绪队列
    //如果不是mark型事件,不使用本指针
    struct event_script *mark_unclear;
    //这两队列都是以剩余次数排队的双向循环链表
    struct  event_script *done_sync,*pop_sync;//弹出同步和完成同步队列头指针,
};
创建每个事件的时候,应该都有这个类型的返回值来标识。
我设想这样一种应用:假设我们需要对一个全局的链表频繁的进行添加和删除操作,add_req 和del_req应该是两类不同的时间,如果设置为同一类型的事件,我在这个结构中没有办法区分这两类不同的操作。
当然,有添加删除就有mod_req。
我们需要确保mod_req要执行的时候,del_req没有将要mod的节点删除,于是我们可以加锁保护,乍看上去是没有问题的,但是细考虑一下
假如我们这个链表需要频繁的大批量的添加删除和修改,长时间的lock住这个结构是不被接受的。
比如创建了1000个节点,然后要修改这1000个节点就要lock住整个链表,而这么长的时间里,我们都可能无法响应新的add或者del请求,这是不被接受的。
我的考虑是丰富event_type结构,同一event类型可以有不同的eventID,这样,可以根据ID的不同做不同的处理,在同一线程就比较容易避免这种问题了。

论坛徽章:
0
4 [报告]
发表于 2009-03-16 23:13 |只看该作者
你的想法很正确,但你还应该结合struct event_script结构来看,
struct event_type是操作系统管理某一类型事件用的,具体事件由struct event_script描述。
struct event_script两个成员数,在弹出事件的函数中可以指定这两个成员的值,可以起到你说的eventID的作用,但djyos中eventID另有用途。
    uint32_t    parameter0;     //事件参数0,具体访问类型由程序员约定
    uint32_t    parameter1;     //事件参数1,具体访问类型由程序员约定
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP