Bus Hound显示信息不完整原因及显示长度设置

Bus Hound 5.0 默认的显示buff长度32字节,当主机获取usb设备的描述符长度大于32字节时,Bus Hound就不显示。

这样在调试的时候就会令人迷惑。其实只要修改一下设置即可。

在setting界面下,修改Limits标签项的“max Phase”的值为更大的值即可。如下图修改为64:

image 

当buffer设置的比较小导致一部分信息没有显示的时候,其实可以通过分析USB协议本身的长度信息相关数据就可以推断,如果推断出的确有信息没有被显示,就可以修改buffer长度在获取一次,即可。

如下例。

修改Buffer前,获取的某设备信息:

通过第七行第三个字节根据协议推断本段应该有0x27(39)个字节信息,实际仅仅显示了32字节信息。

image 

修改Buffer后,获取的某设备信息:

image

小型开源嵌入式操作系统RTOS、模拟器

  1. RTOS:

RT-Thread :     http://www.rt-thread.org/phpbbforum 

                          http://code.google.com/p/rt-thread/

ucos

 

2、开发模拟器

qemu:  处理器模拟器 www.qemu.org

ucGUI:基于VC模拟器

skyeye:一款不错的linux模拟器

浅谈C中的malloc和free

转自:http://bbs.bccn.net/thread-82212-1-1.html

在C语言的学习中,对内存管理这部分的知识掌握尤其重要!之前对C中的malloc()和free()两个函数的了解甚少,只知道大概该怎么用——就是malloc然后free就一切OK了。当然现在对这两个函数的体会也不见得多,不过对于本文章第三部分的内容倒是有了转折性的认识,所以写下这篇文章作为一个对知识的总结。这篇文章之所以命名中有个“浅谈”的字眼,也就是这个意思了!希望对大家有一点帮助!
如果不扯得太远的话(比如说操作系统中虚拟内存和物理内存如何运做如何管理之类的知识等),我感觉这篇文章应该是比较全面地谈了一下malloc()和free().这篇文章由浅入深(不见得有多深)分三个部分介绍主要内容。
废话了那么多,下面立刻进入主题================》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
一、malloc()和free()的基本概念以及基本用法:
1、函数原型及说明:
void *malloc(long NumBytes):该函数分配了NumBytes个字节,并返回了指向这块内存的指针。如果分配失败,则返回一个空指针(NULL)。
关于分配失败的原因,应该有多种,比如说空间不足就是一种。
void free(void *FirstByte): 该函数是将之前用malloc分配的空间还给程序或者是操作系统,也就是释放了这块内存,让它重新得到自由。
2、函数的用法:
其实这两个函数用起来倒不是很难,也就是malloc()之后觉得用够了就甩了它把它给free()了,举个简单例子:
// Code...
char *Ptr = NULL;
Ptr = (char *)malloc(100 * sizeof(char));
if (NULL == Ptr)
{
exit (1);
}
gets(Ptr);
// code...
free(Ptr);
Ptr = NULL;
// code...
就是这样!当然,具体情况要具体分析以及具体解决。比如说,你定义了一个指针,在一个函数里申请了一块内存然后通过函数返回传递给这个指针,那么也许释放这块内存这项工作就应该留给其他函数了。
3、关于函数使用需要注意的一些地方:
A、申请了内存空间后,必须检查是否分配成功。
B、当不需要再使用申请的内存时,记得释放;释放后应该把指向这块内存的指针指向NULL,防止程序后面不小心使用了它。
C、这两个函数应该是配对。如果申请后不释放就是内存泄露;如果无故释放那就是什么也没有做。释放只能一次,如果释放两次及两次以上会
出现错误(释放空指针例外,释放空指针其实也等于啥也没做,所以释放空指针释放多少次都没有问题)。
D、虽然malloc()函数的类型是(void *),任何类型的指针都可以转换成(void *),但是最好还是在前面进行强制类型转换,因为这样可以躲过一
些编译器的检查。
好了!最基础的东西大概这么说!现在进入第二部分:
二、malloc()到底从哪里得来了内存空间:
1、malloc()到底从哪里得到了内存空间?答案是从堆里面获得空间。也就是说函数返回的指针是指向堆里面的一块内存。操作系统中有一个记录空闲内存地址的链表。当操作系统收到程序的申请时,就会遍历该链表,然后就寻找第一个空间大于所申请空间的堆结点,然后就将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。就是这样!
说到这里,不得不另外插入一个小话题,相信大家也知道是什么话题了。什么是堆?说到堆,又忍不住说到了栈!什么是栈?下面就另外开个小部分专门而又简单地说一下这个题外话:
2、什么是堆:堆是大家共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间。堆在操作系统对进程 初始化的时候分配,运行过程中也可以向系统要额外的堆,但是记得用完了要还给操作系统,要不然就是内存泄漏。
什么是栈:栈是线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈互相独立。每个函数都有自己的栈,栈被用来在函数之间传递参数。操作系统在切换线程的时候会自动的切换栈,就是切换SS/ESP寄存器。栈空间不需要在高级语言里面显式的分配和释放。
以上的概念描述是标准的描述,不过有个别语句被我删除,不知道因为这样而变得不标准了^_^.
通过上面对概念的描述,可以知道:
栈是由编译器自动分配释放,存放函数的参数值、局部变量的值等。操作方式类似于数据结构中的栈。
堆一般由程序员分配释放,若不释放,程序结束时可能由OS回收。注意这里说是可能,并非一定。所以我想再强调一次,记得要释放!
注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。(这点我上面稍微提过)
所以,举个例子,如果你在函数上面定义了一个指针变量,然后在这个函数里申请了一块内存让指针指向它。实际上,这个指针的地址是在栈上,但是它所指向的内容却是在堆上面的!这一点要注意!所以,再想想,在一个函数里申请了空间后,比如说下面这个函数:
// code...
void Function(void)
{
char *p = (char *)malloc(100 * sizeof(char));
}
就这个例子,千万不要认为函数返回,函数所在的栈被销毁指针也跟着销毁,申请的内存也就一样跟着销毁了!这绝对是错误的!因为申请的内存在堆上,而函数所在的栈被销毁跟堆完全没有啥关系。所以,还是那句话:记得释放!
3、free()到底释放了什么
这个问题比较简单,其实我是想和第二大部分的题目相呼应而已!哈哈!free()释放的是指针指向的内存!注意!释放的是内存,不是指针!这点非常非常重要!指针是一个变量,只有程序结束时才被销毁。释放了内存空间后,原来指向这块空间的指针还是存在!只不过现在指针指向的内容的垃圾,是未定义的,所以说是垃圾。因此,前面我已经说过了,释放内存后把指针指向NULL,防止指针在后面不小心又被解引用了。非常重要啊这一点!
好了!这个“题外话”终于说完了。就这么简单说一次,知道个大概就可以了!下面就进入第三个部分:
三、malloc()以及free()的机制:
这个部分我今天才有了新的认识!而且是转折性的认识!所以,这部分可能会有更多一些认识上的错误!不对的地方请大家帮忙指出!
事实上,仔细看一下free()的函数原型,也许也会发现似乎很神奇,free()函数非常简单,只有一个参数,只要把指向申请空间的指针传递
给free()中的参数就可以完成释放工作!这里要追踪到malloc()的申请问题了。申请的时候实际上占用的内存要比申请的大。因为超出的空间是用来记录对这块内存的管理信息。先看一下在《UNIX环境高级编程》中第七章的一段话:
大多数实现所分配的存储空间比所要求的要稍大一些,额外的空间用来记录管理信息——分配块的长度,指向下一个分配块的指针等等。这就意味着如果写过一个已分配区的尾端,则会改写后一块的管理信息。这种类型的错误是灾难性的,但是因为这种错误不会很快就暴露出来,所以也就很难发现。将指向分配块的指针向后移动也可能会改写本块的管理信息。
以上这段话已经给了我们一些信息了。malloc()申请的空间实际我觉得就是分了两个不同性质的空间。一个就是用来记录管理信息的空间,另外一个就是可用空间了。而用来记录管理信息的实际上是一个结构体。在C语言中,用结构体来记录同一个对象的不同信息是
天经地义的事!下面看看这个结构体的原型:
struct mem_control_block {
int is_available; //这是一个标记?
int size; //这是实际空间的大小
};
对于size,这个是实际空间大小。这里其实我有个疑问,is_available是否是一个标记?因为我看了free()的源代码之后对这个变量感觉有点纳闷(源代码在下面分析)。这里还请大家指出!
所以,free()就是根据这个结构体的信息来释放malloc()申请的空间!而结构体的两个成员的大小我想应该是操作系统的事了。但是这里有一个问题,malloc()申请空间后返回一个指针应该是指向第二种空间,也就是可用空间!不然,如果指向管理信息空间的话,写入的内容和结构体的类型有可能不一致,或者会把管理信息屏蔽掉,那就没法释放内存空间了,所以会发生错误!(感觉自己这里说的是废话)
好了!下面看看free()的源代码,我自己分析了一下,觉得比起malloc()的源代码倒是容易简单很多。只是有个疑问,下面指出!
// code...
void free(void *ptr)
{
struct mem_control_block *free;
free = ptr - sizeof(struct mem_control_block);
free->is_available = 1;
return;
}
看一下函数第二句,这句非常重要和关键。其实这句就是把指向可用空间的指针倒回去,让它指向管理信息的那块空间,因为这里是在值上减去了一个结构体的大小!后面那一句free->is_available = 1;我有点纳闷!我的想法是:这里is_available应该只是一个标记而已!因为从这个变量的名称上来看,is_available 翻译过来就是“是可以用”。不要说我土!我觉得变量名字可以反映一个变量的作用,特别是严谨的代码。这是源代码,所以我觉得绝对是严谨的!!这个变量的值是1,表明是可以用的空间!只是这里我想了想,如果把它改为0或者是其他值不知道会发生什么事?!但是有一点我可以肯定,就是释放绝对不会那么顺利进行!因为这是一个标记!
当然,这里可能还是有人会有疑问,为什么这样就可以释放呢??我刚才也有这个疑问。后来我想到,释放是操作系统的事,那么就free()这个源代码来看,什么也没有释放,对吧?但是它确实是确定了管理信息的那块内存的内容。所以,free()只是记录了一些信息,然后告诉操作系统那块内存可以去释放,具体怎么告诉操作系统的我不清楚,但我觉得这个已经超出了我这篇文章的讨论范围了。
那么,我之前有个错误的认识,就是认为指向那块内存的指针不管移到那块内存中的哪个位置都可以释放那块内存!但是,这是大错特错!释放是不可以释放一部分的!首先这点应该要明白。而且,从free()的源代码看,ptr只能指向可用空间的首地址,不然,减去结构体大小之后一定不是指向管理信息空间的首地址。所以,要确保指针指向可用空间的首地址!不信吗?自己可以写一个程序然后移动指向可用空间的指针,看程序会有会崩!
最后可能想到malloc()的源代码看看malloc()到底是怎么分配空间的,这里面涉及到很多其他方面的知识!有兴趣的朋友可以自己去下载源
代码去看看。
四、关于其他:
关于C中的malloc()和free()的讨论就写到这里吧!写了三个钟头,感觉有点累!希望对大家有所帮助!有不对的地方欢迎大家指出!最后
,谢谢参与这个帖子讨论的所有朋友,帖子:http://bbs.bccn.net/thread-81781-1-1.html。也谈到版权问题,如果哪位想转载这篇文章(如果我有这个荣幸的话),最起码请标明“来自bccn C语言论坛”这几个字眼,我的ID可以不用写上!谢谢合作!
五、参考文献:(只写书名)
——《UNIX环境高级编程》
——《计算机组成原理》
——《高质量C/C++编程指南》
ID:lj_860603(键键)
2006.8.4 初稿

SWIG (一种用于衔接C/C++和其他各种[脚本]语言的免费工具)

官网:http://www.swig.org/    该工具很活跃,从2000年开始持续更新中…

中文版主页面:http://www.swig.org/translations/chinese/index.html

功能概述:

SWIG是个帮助使用C或者C++编写的软件能与其它各种高级编程语言进行嵌入联接的开发工具。SWIG能应用于各种不同类型的语言包括常用脚本编译语言例如Perl, PHP, Python, Tcl, Ruby and PHP。支持语言列表中也包括非脚本编译语言,例如C#, Common Lisp (CLISP, Allegro CL, CFFI, UFFI), Java, Modula-3, OCAML以及R,甚至是编译器或者汇编的计划应用(Guile, MzScheme, Chicken)。SWIG普遍应用于创建高级语言解析或汇编程序环境,用户接口,作为一种用来测试C/C++或进行原型设计的工具。SWIG还能够导出XML或Lisp s-expressions格式的解析树。SWIG可以被自由使用,发布,修改用于商业或非商业中。

已经支持一下这些语言:

AllegroCL

C# - Mono

C# - MS .NET

CFFI

CHICKEN

CLISP

Guile

Java

Lua

MzScheme

Ocaml

Perl

PHP

Python

R

Ruby

Tcl/Tk

卡扎菲亲信称后悔当初未能造出核武器(图)

转自:http://war.news.163.com/11/0331/09/70FEGSI800011MTO.html

核心提示:俄《论据与事实》周刊30日刊登对卡扎菲一位亲信官员的独家专访。这名官员表示,卡扎菲及其支持者将战斗到底,别无退路。卡扎菲当下最大的遗憾是未能制造出核武器,否则西方将不敢擅自入侵。

卡扎菲亲信表示后悔当初未能造出核武器

卡扎菲的支持者在的黎波里街头挥舞旗帜展示决心。

俄《论据与事实》周刊30日刊登对卡扎菲一位亲信官员的独家专访。这名官员表示,卡扎菲及其支持者将战斗到底,别无退路。卡扎菲当下最大的遗憾是未能制造出核武器,否则西方将不敢擅自入侵。

《论据与事实》周刊利用其在某阿拉伯国家总统府内的关系,成功得到这位卡扎菲核心亲信圈官员的手机号,并于夜间对其进行了专访。可以说,该官员言论基本上反映了利比亚当局的立场,虽然部分内容的黎波里未必会赞同。

《论据与事实》:卡扎菲清楚他当下的处境么?

答:当然清楚。大家都是现实主义者,不会奢望什么奇迹。我们现在的主要目标是:在的黎波里支撑数月,不管对方如何狂轰乱炸。另外重要的一点是,卡扎菲拥有长期防御所需的一切。

《论据与事实》:您指的是武器?

答:不是,是金钱。重赏之下必有勇夫。

《论据与事实》:然后呢?

答:再看吧。

《论据与事实》:利比亚所有空军都已被摧毁,这是事实吗?

答:我们当然无法与北约军力相抗衡。我们犯下了一个错误,在西方取消制裁后我们应该立马采购新式俄制武器。虽然我们签署了合同,但是迟迟未付诸实施。但是情况还没那么糟。CNN每天都声称利比亚防空军不复存在,但是随后的报道画面显示,我们正在向敌军飞机开火。这岂不是自相矛盾?

《论据与事实》:南斯拉夫在抵抗北约3个月后,最终不得不屈服……

答:卡扎菲不会屈服。他亲眼目睹,米洛舍维奇死在狱中,而萨达姆被活活绞死。卡扎菲别无退路,所以他将战斗到底。

《论据与事实》:会坚持多久?反政府军一周时间就攻取了3座城市。

答:但是首都他们无法夺取。现在,每个为卡扎菲而战的人都明白:如果战败,将被处以绞刑。所以,除了誓死反击,我们别无别路。您还记得么,2月(利比亚)爆发骚动时,所有的人都说:“卡扎菲完了,他逃往委内瑞拉了。”但事实上,卡扎菲只用2个星期就平息了叛乱——如果不是美国人,我们就攻下班加西了。谁知道局势会发生逆转?

《论据与事实》:您看么看北约取得了对利战争的成功?

答:世界主要强国的精锐空军一周都搞不定一个600万人口小国的军队,的确让人见笑。这不是场战争,而是场闹剧。西方又一次向世人展示,它是如何的蛮横无礼。说是为了捍卫“自由斗士”,简直一派胡言。2008年的时候,一个小国开始残酷屠杀本国的叛乱分子,某个大国挺身而出。结果这个大国遭到所有人的抨击,被指责为“侵略者”。您是不是已经明白了,我指的是俄罗斯与格鲁吉亚之间的战争。但是,利比亚尚未出现类似情景,美国人就立即站到了反叛者一边。到处都充斥着谎言。卡扎菲被指屠杀了8000人,但是谁能展示出哪怕是20具尸体的电视画面。因为“卡扎菲的兽行”没有一名利比亚人逃往欧洲,但现在出现了成千上万的逃避北约轰炸的难民。

《论据与事实》:卡扎菲现在感到后悔的是什么?

答:他只是后悔当初中止了核武器研制工作。现在所有人都害怕去动朝鲜。如果拥有核弹的话,也没人会入侵我们。

====================================后记=========================================

在哭泣!

从Linux内核可以学习到的C编程技巧

《Linux内核源代码情景分析》----毛德操【著】  阅读笔记。

1、GCC编译器吸收了很多C++特性对ANSI C做了很多扩充。【ADI 的Visual DSP使用的就是GCC内核,也支持以下扩展特性】

1.1  增加inline关键字,并且为兼容其他以编程将“inline”作为变量的代码,还扩充了“__inline__”作为关键字,等价于“inline”。用inline定义的函数需要写在“.h”文件,用于打开编译器优化时自动将代码插入到调用的地方。

1.2  扩充了数据长度,增加“long long int”类型,定义64bit整数,增加“double double”定义64bit float数据。

2、常见以下形式宏定义:

#define    DUMP_WRITE(addr,  n)     do{memcpy(buff, addr, n);  buff+=n;}while(0)

表面上看,使用一个do..while循环,可是循环条件却永远为假,等价于不用循环。但是这样定义的好处是这个宏定义可以插在代码的任何地方而不用担心编译器报错或编译出错。比如:

if( addr )
    DUMP_WRITE;
else
    do_something_else();

就可以很好的工作,这种宏定义方式巧妙的利用了C的特性实现了宏定义一段代码的方法。

3、利用一个独立定义的链表指针结构体来供各种需要双向指针的结构体包含,以提供双向链节点。

struct   list_head{
        struct list_head * next ;
        struct list_head * prev ;
};

任何需要链表的结构体只需包含上述结构体即可,上述结构体还可以单独用于定义一个链表头类型变量或指针。

4、对于以上结构体,如果一个结构体包含了以上的结构体用于构成链表。那么怎么通过链表指针计算出其宿主结构体变量的首地址呢?Linux这么做的:

假如有如下定义:
struct  page{
    ......
    struct list_head  list ;  //不确定处于page结构体的某个偏移地址的位置上
     ......
}
【取自mm/page_alloc.c】
    page = memlist_entry( pcurr, struct page, list );
memlist_entry函数实现将一个list_head类型的指针pcurr换算成其宿主结构体变量的起始地址。
并且文件中根本没有定义list变量。第二个参数还仅仅是个类型,而不是变量。
但是同一文件中,有以下宏定义:
    #define    memlist_entry  list_entry
list_entry则在【include/linux/list.h】中定义如下:
    #define list_entry(ptr, type, member) ((type *)((char*)(ptr)-(unsigned long)(&((type *)0)->member)))
由此可见,以上“page = memlist_entry( pcurr, struct page, list );”语句将被预处理替换成如下格式:
        page = ((struct page *)((char*)(curr)-(unsigned long)(&((struct page *)0)->list))) ;
这样就很好理解了,用链表指针的地址减去list成员在结构体中的偏移。

关于日本地震

(转自:http://mikesakai.blog.163.com/blog/static/13533548920112226823207/?suggestedreading)

日本地震了,我知道会有很多人跳出来说自己多么正义多么仁慈,多么关心日本人的安危,但是我提醒这些人,如果你是中国人请收起你这份感情。日本曾经说中国能做的东西他们都能做,J-20他们行吗?现在中国人缺的是民族自豪感,而不是对日本人的同情。 海豚湾看过吗?金粒餐你吃过吗?全世界都不杀海洋哺乳动物了,日本杀,全世界的人都不会吃屎日本人吃。 汶川地震你知道日本人为什么迫不及待的来中国吗?因为他们要收集第一手的地震资料因为他们那里地震多,日本对中国的援助,是压榨了多少中国人的血汗啊,一个日本老板给日本员工2万工资,给你2000,你就当日本人是救苦救难的恩人,是你的爸妈,真为你认贼作父感到 羞耻。 你最好别批人皮虚伪的说你有人性,我觉得敢爱敢恨的人才是真有人性,日本不是没有好人,但是日本民族太虚伪,太伪善了,我就是讨厌这个民族,憎恨这个民 族,这次地震我就是很开心,其实民族与民族之间的仇恨不是你说没有就没有的,知道为什么日本人瞧不起中国人吗?就是因为太多麻木的中国人,把你打的头破血流,家破人亡,给你颗糖就把一切恩怨一笔勾销了,这样的中国人值得日本人尊重吗?知道美国人为什么尊重中国人吗?因为抗美援朝,因为志愿军是他们值得尊重的对手。他们在做天在看,海豚的血鲸鱼的泪冲刷着他们那个大和民族。 你喜欢日本可以同情他们,但是你如果当自己是个中国人,就请把你那份感情留在自己心里 ,首先日本人不肖你那份同情,中国人也会觉得你很虚伪,就好比以前那些选美,最后的冠军都会说“我希望世界和平”现在很多人拿日本和德国比,德国总理可以跪在2战纪念碑前请求全世界原谅,日本了?每年还去拜祭那些甲级战犯。说到经济,中国的通货膨胀你们真以为全是国内的贪官搞出来的?都是这些所谓发达国家,眼红中国的经济发展速度,要抑制中国的发展,你们知道他们美日,为什么要求人民币升值吗?因为外汇储备世界第一,你们自己想象下,上万亿日元美元,一天要贬值多少,这和抢中国人的血汗钱有什么区别?中国说实在的就太儒了,对谁都儒,就让其他国家觉得软弱,你们看看俄罗斯吧,中国要拿出俄罗斯的魄力,他小日本还敢在钓鱼岛那么猖狂,现在还出来同情日本地震的人真应该好好检讨下了。这次地震真的让我感触,我只想对小日本说句:人在做天在看,不是不报。

后记:关于日本地震,出于人道主义,我们应该同情,但我更愿意把它埋在心底,日本不需要中国人同情。

今日网易新闻页面,日本地震还很殷勤的送人家石油,不觉得很滑稽吗?送什么不行,讨好似的非要送石油给他,凭什么?

image

嵌入式实时操作系统中优先级翻转问题的分析与解决方案

1 优先级翻转(Priority Inversion)
优先级翻转(Priority Inversion),是指某同步资源被较低优先级的进程/线程所拥有,较高优先级的进程/线程竞争该同步资源未获得该资源,而使得较高优先级进程/线程反而推迟被调度执行的现象。

优先级翻转现象在普通的分时调度系统中对系统的影响不大,或者本来就对优先级高低的进程/线程被调度的顺序就不太在意的情况下,不用太多关注。但是对于基于优先级调度的实时系统,优先级高的进程/线程被优先调度是调度算法首要考虑的因素,调度程序对于较高优先级被调度单元总是最钟情的,所有其它相应资源分配的考量也是基于优先级做出的。SUN Solaris和Microsoft Windows CE等系统都对优先级翻转现象在操作系统级做了处理。

所谓优先级翻转问题(priority inversion)即当一个高优先级任务通过信号量机制访问共享资源时,该信号量已被一低优先级任务占有, 而这个低优先级任务在访问共享资源时可能又被其它一些中等优先级的任务抢先, 因此造成高优先级任务被许多具有较低优先级的任务阻塞, 实时性难以得到保证。

2 解决优先级翻转问题有优先级天花板(priority ceiling)和优先级继承(priority inheritance)两种办法。

2.1 优先级天花板是当任务申请某资源时, 把该任务的优先级提升到可访问这个资源的所有任务中的最高优先级, 这个优先级称为该资源的优先级天花板。这种方法简单易行, 不必进行复杂的判断, 不管任务是否阻塞了高优先级任务的运行, 只要任务访问共享资源都会提升任务的优先级。

2.2 优先级继承是当任务A 申请共享资源S 时, 如果S正在被任务C 使用,通过比较任务C 与自身的优先级,如发现任务C 的优先级小于自身的优先级, 则将任务C的优先级提升到自身的优先级, 任务C 释放资源S 后,再恢复任务C 的原优先级。这种方法只在占有资源的低优先级任务阻塞了高优先级任务时才动态的改变任务的优先级,如果过程较复杂, 则需要进行判断。

2.3 优先级继承的传递性
优先级继承的传递性,是指图二的优先级继承发生时,在Thread3的优先级已经提升到Thread1执行的T3-T4时间段里,如果Thread3竞争比Thread3的初始优先级还要低的线程Thread4拥有的同步资源,操作系统同样会把Thread4的优先级提升到Thread1的优先级。SUN的Solaris系统实现了优先级继承和继承的传递,Microsoft的Windows CE虽然实现了优先级继承,但是它只实现了一级,即,没有实现优先级继承的传递性。

3 总结
优先级翻转是多线程环境中应该尽力避免的现象,尤其是依赖线程优先级来调度的系统中。在设计多线程程序时候,避免优先级翻转的最简单方法,就是不要让不同优先级的线程去竞争同一个同步资源。

优先级继承可以减缓优先级翻转带来的问题,但是也不能保证优先级最高的线程绝对地被最先调度执行,还是有一定的延缓时间。如果有优先级继承的传递性,传递的级别很深时,对系统性能的影响还是很大的。


假定一个进程中有三个线程Thread1、Thread2和Thread3,它们的优先级顺序是Priority(Thread1) > Priority(Thread2) > Priority(Thread3)。考虑上图的执行情况。

       ◇ T0时刻,只有Thread3处于可运行状态,运行过程中,Thread3拥有了一个同步资源SYNCH1;
       ◇ T1时刻,Thread2就绪进入可运行状态,由于优先级高于正在运行的Thread3,Thread3被抢占(未释放同步资源SYNCH1),Thread2被调度执行;
       ◇ 同样地T2时刻,Thread1抢占Thread2;
       ◇ Thread1运行到T3时刻,需要同步资源SYNCH1,但SYNCH1被更低优先级的Thread3所拥有,Thread1被挂起等待该资源,而此时处于可运行状态的线程Thread2和Thread3中,Thread2的优先级大于Thread3的优先级,Thread2被调度执行。

上述现象中,优先级最高的Thread1既要等优先级低的Thread2运行完,还要等优先级更低的Thread3运行完之后才能被调度。如果Thread2和Thread3执行的很费时的操作,显然Thread1的被调度时机就不能保证,整个实时调度的性能就很差了。


1. 优先级反转(Priority Inversion)
    由于多进程共享资源,具有最高优先权的进程被低优先级进程阻塞,反而使具有中优先级的进程先于高优先级的进程执行,导致系统的崩溃。这就是所谓的优先级反转(Priority Inversion)。

2. 产生原因
      其实,优先级反转是在高优级(假设为A)的任务要访问一个被低优先级任务(假设为C)占有的资源时,被阻塞.而此时又有优先级高于占有资源的任务(C)而低于被阻塞的任务(A)的优先级的任务(假设为B)时,于是,占有资源的任务就被挂起(占有的资源仍为它占有),因为占有资源的任务优先级很低,所以,它可能一直被另外的任务挂起.而它占有的资源也就一直不能释放,这样,引起任务A一直没办法执行.而比它优先低的任务却可以执行. 

      所以,一个解决办法就是提高占有资源任务的优先级,让它正常执行,然后释放资源,以让任务A能正常获取资源而得以执行.

3. 解决方案 ( 优先级继承 / 优先级天花板 )

   目前解决优先级反转有许多种方法。其中普遍使用的有2种方法:一种被称作优先级继承(priority inheritance);另一种被称作优先级极限(priority ceilings)。

  A. 优先级继承(priority inheritance)
      优先级继承是指将低优先级任务的优先级提升到等待它所占有的资源的最高优先级任务的优先级.当高优先级任务由于等待资源而被阻塞时,此时资源的拥有者的优先级将会自动被提升.  
  B. 优先级天花板(priority ceilings)
      优先级天花板是指将申请某资源的任务的优先级提升到可能访问该资源的所有任务中最高优先级任务的优先级.(这个优先级称为该资源的优先级天花板)  
  A 和B的区别: 
优先级继承,只有当占有资源的低优先级的任务被阻塞时,才会提高占有资源任务的优先级,而优先级天花板,不论是否发生阻塞,都提升.

优先级翻转与优先级继承

田海立

2006-3-7

本文描述操作系统中的优先级翻转(Priority Inversion,也有翻译为反转,逆转或倒置的)现象以及如何用优先级继承来解决此类问题的方法,并阐述了 Microsoft Platform Builder for Windows CE 环境下,如何查看此种现象。

目 录

摘要
1 优先级翻转(Priority Inversion)
2 优先级继承(Priority Inheritance)
2.1 优先级继承
2.2 WinCE中优先级继承
2.3 优先级继承的传递性
3 总结
参考资料及进一步阅读
关于作者

优先级翻转(Priority Inversion)

优先级翻转(Priority Inversion,是指某同步资源被较低优先级的进程/线程所拥有,较高优先级的进程/线程竞争该同步资源未获得该资源,而使得较高优先级进程/线程反而推迟被调度执行的现象。

优先级翻转现象在普通的分时调度系统中对系统的影响不大,或者本来就对优先级高低的进程/线程被调度的顺序就不太在意的情况下,不用太多关注。但是对于基于优先级调度的实时系统,优先级高的进程/线程被优先调度是调度算法首要考虑的因素,调度程序对于较高优先级被调度单元总是最钟情的,所有其它相应资源分配的考量也是基于优先级做出的。SUN Solaris和Microsoft Windows CE等系统都对优先级翻转现象在操作系统级做了处理。

假定一个进程中有三个线程Thread1、Thread2和Thread3,它们的优先级顺序是Priority(Thread1) > Priority(Thread2) > Priority(Thread3)。考虑图一的执行情况。

图一、优先级翻转现象(无优先级继承)

T0时刻,只有Thread3处于可运行状态,运行过程中,Thread3拥有了一个同步资源SYNCH1;
T1时刻,Thread2就绪进入可运行状态,由于优先级高于正在运行的Thread3,Thread3被抢占(未释放同步资源SYNCH1),Thread2被调度执行;
同样地T2时刻,Thread1抢占Thread2;
Thread1运行到T3时刻,需要同步资源SYNCH1,但SYNCH1被更低优先级的Thread3所拥有,Thread1被挂起等待该资源,而此时处于可运行状态的线程Thread2和Thread3中,Thread2的优先级大于Thread3的优先级,Thread2被调度执行。

上述现象中,优先级最高的Thread1既要等优先级低的Thread2运行完,还要等优先级更低的Thread3运行完之后才能被调度,如果Thread2和Thread3执行的很费时的操作,显然Thread1的被调度时机就不能保证,整个实时调度的性能就很差了。

优先级继承(Priority Inheritance)

优先级继承

为了解决上述由于优先级翻转引起的问题,Solaris和WinCE引入了优先级继承的解决方法。优先级继承也就是,高优先级进程TH在等待低优先级的线程TL继承占用的竞争资源时,为了使TH能够尽快获得调度运行,由操作系统把TL的优先级提高到TH的优先级,从而让TL以TH的优先级参与调度,尽快让TL执行并释放调TH欲获得的竞争资源,然后TL的优先级调整到继承前的水平,此时TH可获得竞争资源而继续执行。

有了优先级继承之后的上述现象的执行情况如图二所示。

图二、优先级继承执行情况

与图一比较,图二中,到了T3时刻,Thread1需要Thread3占用的同步资源SYNCH1,操作系统检测到这种情况后,就把Thread3的优先级提高到Thread1的优先级。此时处于可运行状态的线程Thread2和Thread3中,Thread3的优先级大于Thread2的优先级,Thread3被调度执行。

Thread3执行到T4时刻,释放了同步资源SYNCH1,操作系统何时恢复了Thread3的优先级,Thread1获得了同步资源SYNCH1,重新进入可执行队列。处于可运行状态的线程Thread1和Thread2中,Thread1的优先级大于Thread2的优先级,所以Thread1被调度执行。

上述机制,使优先级最高的Thread1获得执行的时机提前。

中优先级继承

下面用在Microsoft Platform Builder for Windows CE 5.0环境的Emulator中,来做一个优先级继承的实验。

2.2.1 程序设计

主线程做了如下工作:
创建三个子线程,设置它们的优先级顺序:
Priority(Thread1) > Priority(Thread2) > Priority(Thread3);
只是启动Thread3。
创建一个互斥体g_Mutex1,初始状态是未被获得的。

三个子线程的执行体分别如下:

Thread3
获得g_Mutex1;
循环执行一定次数的
打印“Thread3: Executing BEFORE releasing the MUTEX...”
释放g_Mutex1;
循环执行:
打印“Thread3: Executing AFTER releasing the MUTEX...”

Thread2
打印:“Thread2: Start executing...”
循环执行:
打印“Thread2: executing...”

Thread1
打印:“Thread1: Start executing...”
打印:“Thread1: Waiting on Mutex1...”
执行:WaitForSingleObject(g_Mutex1, INFINITE); 
打印:“Thread1: GOT g_Mutex1, and will resume.”
循环执行:
打印“Thread1: Executing AFTER getting the MUTEX...”

2.2.2 Log结果分析

结果输出如下:

TID:a3ecaa1e Thread1: Handler = a3c867b2, ThreadId = a3c867b2
TID:a3ecaa1e Thread2: Handler = 83c8c002, ThreadId = 83c8c002
TID:a3ecaa1e Thread3: Handler = 3c866c2, ThreadId = 3c866c2
TID:3c866c2 Thread3 GOT the Mutex g_Mutex1
TID:83c8c002 Thread2: Start executing...
TID:83c8c002 Thread2: executing...
TID:a3c867b2 Thread1: Start executing...
TID:a3c867b2 Thread1: Waiting on Mutex1...
TID:3c866c2 Thread3: Executing BEFORE releasing the MUTEX...
TID:3c866c2 Thread3: Executing BEFORE releasing the MUTEX...
......
TID:3c866c2 Thread3: Executing BEFORE releasing the MUTEX...
TID:3c866c2 Thread3: Executing BEFORE releasing the MUTEX...
TID:3c866c2 Thread3: Released the mutex1.
TID:a3c867b2 Thread1: GOT g_Mutex1, and will resume.
TID:a3c867b2 Thread1: Executing AFTER getting the MUTEX...
TID:a3c867b2 Thread1: Executing AFTER getting the MUTEX...
TID:a3c867b2 Thread1: Executing AFTER getting the MUTEX...
......

结果中的斜体加粗部分标识了图二的T3-T4时刻的操作。Thread3由于拥有Thread1欲申请的同步资源g_Mutex1而继承了Thread1的优先级,获得了执行。

结果中的粗体加下划线部分标识了图二的T4时刻的操作。Thread3释放同步资源g_Mutex1,Thread3的优先级被恢复到T3之前;Thread1获得了g_Mutex1之后继续执行。

2.2.3 Platform Builder的工具查看

优先级继承时的线程的优先级是无法在程序中通过诸如GetThreadPriority() / CEGetThreadPriority() 之类的函数来查看CurrPrio的,这些函数查看的是显式设置的BasePrio。(WinCE的早期版本里,这些函数返回的是CurrPrio)

我们可以通过Platform Builder的调试工具来查看,不过因为PB提供的Emulator与BP Host之间同步需要时间,所以我们在上面的程序中需要延长Thread3在T3-T4时间段的时间以获得足够的时间查看此时Thread3继承的优先级。仅仅为了查看,这里可以设置一个极限情况,即Thread3在释放g_Mutex1之前无限循环。

下图是做完上述设置之后,通过 Platform Builder 的菜单“View | Debug Windows | Threads”,调出的Process/Thread View。

图三、通过Thread View查看优先级继承

图中,第一行对应Thread3;第二行对应PriorityInversion进程的主线程;第三行对应Thread1;第四行对应Thread2。从Thread3的CurrPrio列可以看出,它的当前的优先级确实提升到了Thread1的优先级。

优先级继承的传递性

优先级继承的传递性,是指图二的优先级继承发生时,在Thread3的优先级已经提升到Thread1执行的T3-T4时间段里,如果Thread3竞争比Thread3的初始优先级还要低的线程Thread4拥有的同步资源,操作系统同样会把Thread4的优先级提升到Thread1的优先级。SUN的Solaris系统实现了优先级继承和继承的传递,Microsoft的Windows CE虽然实现了优先级继承,但是它只实现了一级,即,没有实现优先级继承的传递性。

下图是WinCE中按照上面描述增加了Thread4之后,参看Thread View得到的一个快照。

图四、通过Thread View查看优先级继承的传递性

图中,第一行对应Thread1;第二行对应Thread2;第三行对应PriorityInversion进程的主线程;第四行对应Thread4;第五行对应Thread3。从Thread3和Thread4所在行的CurrPrio列可以看出,Thread3由于等待Thread4拥有的竞争资源而被Blocked,优先级也没有得到提升;Thread4由于拥有Thread3所要竞争的同步资源,其优先级被提升到Thread3的优先级。这种现象不具有优先级继承的传递性。

总结

优先级翻转是多线程环境中应该尽力避免的现象,尤其是依赖线程优先级来调度的系统中。在设计多线程程序时候,避免优先级翻转的最简单方法,就是不要让不同优先级的线程去竞争同一个同步资源。

优先级继承可以减缓优先级翻转带来的问题,但是也不能保证优先级最高的线程绝对地被最先调度执行,还是有一定的延缓时间。如果有优先级继承的传递性,传递的级别很深时,对系统性能的影响还是很大的。

Microsoft, MSDN, 2005.
Uresh Vahalia, UNIX Internals: The New Frontiers, PEARSON EDU / 人民邮电出版社影印, 2003/2003.7

关于作者

田海立,硕士,国家系统分析师,中国系统分析员协会顾问团专业顾问。
您可以通过 HaiLi.Tian(at)csai.cn 或 TianHaiLi(at)nju.org.cn 与他联系,到 http://blog.csdn.net/thl789/ 看他最新的文章。

版权声明:

本文为作者原创作品,版权归作者所有。
为了学习和研究,可转载本文,但必须与原文的内容和格式保持一致,并给出原文的链接!

http://blog.csdn.net/thl789/archive/2006/03/07/617629.aspx

信号量、二进制信号量、互斥信号量(二进制信号量 貌似只存在于VxWorks系统)

二进制信号量
这种信号量有两种功能:互斥和同步。它经过精心设计,效率很高,很适合性能要求高的应用。互斥信号量也是一种Binary信号量,它专门为互斥设计的,但是如果在认为没有必要使用互斥信号量的场合可以用Binary信号量提供互斥功能(需要使用互斥信号量的特殊场合将在介绍互斥信号量中进行说明)。
一个Binary信号量有两种状态:可利用(full)和不可利用(empty)。用semTake()来获得信号量。如果使用该函数的时候,信号量处于full状态,则调用函数后信号量变为empty,否则将根据该信号量的类型把申请信号量的任务挂到相应的等待队列中。当然如果任务是不等待信号量方式,则任务继续执行。
互斥信号量
互斥信号量是一种特殊的Binary信号量,它专门用于解决互斥问题如:优先级倒置,安全删除及递归访问资源等。互斥信号量和Binary信号量有以下不同:
- 它仅用于互斥;
- 它只能由申请(take)到该信号量的任务来释放(give);
- 它不能由中断来使其为full;
- semFlush()操作是非法的;

信号量和互斥信号量

信号量和互斥信号量是在多线程环境中必备资源,很多网友对它都有很丰富的经验和深刻的教训。因为这篇文章不将重点于如何使用信号量或者是互斥,而是从概念上简述他们之间的一些关系。
  信号量一般有两种运用,一是所谓的binary semaphore,它的值只有0或者1,从技术角度看它可以产生类似于互斥的功能,但是它们有区别:
信号量的释放不一定是已经获取信号量的线程,但是互斥的释放一定要是已经获取互斥的线程。 
信号量的初始值可以是0,其他线程可以通过post或者up之类的函数增加这个值,但是互斥的初始化值一定是一个有效的值。 
信号量一般情况下无法实现递归获取和释放,但是互斥一般有递归获取和释放。 
  另外一种信号量的使用就是作为计数资源,一般而言是大于1的一个数值。当获取对应资源后,线程能够执行相应的操作,比如在生产者和消费者模型中,只有获取资源后,才能进行对应的读写操作。
  那么我们在什么情况下使用信号量或者是互斥呢?我的个人意见是对于多进程之间资源的保护,信号量的使用一般比较方便,因为如果使用互斥的话,在按照posix1标准实现的系统中,我们往往要把互斥存放在共享内存中,以便进程之间能够相互访问。如果我们运用的系统是多线程,那么互斥的使用可能更加的容易,并且相对容易调试,因为互斥的释放只能是在获取线程中,减少了线程之间的太多关联。