RTT的时基函数是rt_tick_increase(),该函数有个不妥之处,比如我把每个线程的时间片长度设置为1个时基。那么,有以下情况可能发生:rt_timer_check()函数使更高优先级的线程就绪,而该线程的时间片为1。此后rt_thread_self()函数返回的就是这个就绪线程,接下来紧接着时间片计数器就被减1,变为0,也就是说线程还没执行就被rt_thread_yield()函数当作放弃了执行对待!这就是不妥之处!望Bernard兄妥善解决。
RTT的定时计数器变量rt_tick在每个Tick中断内自增,自增到0xffffffff再溢出到0周而复始。
所有定时器对象的定时结束时基计算为:timer->timeout_tick = rt_tick_get() + timer->init_tick;
在时基中断,检查if (rt_tick >= t->timeout_tick)是否成立,如果成立则定时时间到。似乎很合理,但是错误就出现在这里。如果来了20个tick的定时请求在执行“timer->timeout_tick = rt_tick_get() + timer->init_tick;”时,若rt_tick为0xfffffff0,于是得出定时结束时间为0x00000004,于是该节点加入定时器列表等待超时。结果,下一次进入中断的rt_tick为0xfffffff1,使if (rt_tick >= t->timeout_tick)检查成立,所以定时时间结束,错误发生了!
这个错误平时发现不了,是因为等到rt_tick接近溢出需要很长时间(如果1毫秒一个tick的话,需要50天,10毫秒一个tick就需要500天,哈哈!),Bernard兄是不是考虑到这个情况才故意放过这个臭虫的呢?如果要解决该问题,必定添加不少冗余代码,影响性能。但留着这个漏洞又很不舒服,怎么办呢?
RTT还需要很长的时间来完善才能用于商业乃至工业现场啊!今天又发现了一个很要命的漏洞,这种漏洞是对临界代码段的理解太随意导致的。
我知道,很多人有时候为了所谓的实时性,都忽略了临界代码段的重要性。但是,如果不仔细推敲代码而随意放任的话,系统会变得异常脆弱!
下面我就来给你分析以下rt_object_find()函数的漏洞。
rt_object_find()函数是通过对象的名称来查找对象,并返回指针。调用该函数之前,线程大都不会关中断来保护rt_object_find(),而该函数自身更是没有临界段保护措施,这样就造成了下面的漏洞:
在遍历链表节点的过程中,node指向当前所检查的对象节点,而在此判断过程中,有可能高优先级线程得到运行,从而导致当前对象节点有可能被删除,这时,对象的链表指针next,prev就指向它自身。之后,CPU上下文回到调用rt_object_find()函数的线程,继续遍历对象链表,结果node = node->next语句总是使node指向它自身,而且符合node != &(information->object_list)条件,因此,for()循环形成了死循环!该函数将无法退出,其结果无法预测!
我是一个思维很严谨的人,总是很小心的对待代码,即使是在单任务软件开发过程中,我也很注意中断函数和main()循环之间的临界代码段问题。希望大家能跟我一样多查看内核代码,尽快完善RTT!
(我的经验都来自ucos源代码,因为国外对RTOS的认证是很严格的,ucos是经过航空级认证的RTOS)
RTT的定时计数器变量rt_tick在每个Tick中断内自增,自增到0xffffffff再溢出到0周而复始。
所有定时器对象的定时结束时基计算为:timer->timeout_tick = rt_tick_get() + timer->init_tick;
在时基中断,检查if (rt_tick >= t->timeout_tick)是否成立,如果成立则定时时间到。似乎很合理,但是错误就出现在这里。如果来了20个tick的定时请求在执行“timer->timeout_tick = rt_tick_get() + timer->init_tick;”时,若rt_tick为0xfffffff0,于是得出定时结束时间为0x00000004,于是该节点加入定时器列表等待超时。结果,下一次进入中断的rt_tick为0xfffffff1,使if (rt_tick >= t->timeout_tick)检查成立,所以定时时间结束,错误发生了!
这个错误平时发现不了,是因为等到rt_tick接近溢出需要很长时间(如果1毫秒一个tick的话,需要50天,10毫秒一个tick就需要500天,哈哈!),Bernard兄是不是考虑到这个情况才故意放过这个臭虫的呢?如果要解决该问题,必定添加不少冗余代码,影响性能。但留着这个漏洞又很不舒服,怎么办呢?
这个问题mbbill和阿干都和我讨论过,只是一直没能够找到比较好一些的办法,所以拖着没解决。呵呵,这个也是0.4.x分支必须要解决的问题之一(解决了会merge回0.3.x分支)。
因为RT-Thread目前主要面向小型应用,如果简单的解决这个问题,只需要更改32位为两个32位变量即可(当然这样仅仅是延长了很多出错的时间,从 50天或500天延长到了4294967295 * 50天或4294967295 * 500天),这样唯一的问题就是多了4个字节。
/* SECTION: device filesystem
#define RT_USING_DFS
#define RT_USING_DFS_ELMFAT */
/* SECTION: lwip, a lighwight TCP/IP protocol stack
#define RT_USING_LWIP */
/* LwIP uses RT-Thread Memory Management
#define RT_LWIP_USING_RT_MEM */
Program Size: Code=37646 RO-data=4322 RW-data=440 ZI-data=4912
Build target 'RT-Thread STM32'
compiling stm32f10x_it.c...
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(23): error: #256: invalid redeclaration of type name "s32" (declared at line 312 of "C:KeilARMINCSTSTM32F10xstm32f10x.h")
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef signed long s32;
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(27): error: #256: invalid redeclaration of type name "sc32" (declared at line 316 of "C:KeilARMINCSTSTM32F10xstm32f10x.h")
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef signed long const sc32; /* Read Only */
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(31): error: #256: invalid redeclaration of type name "vs32" (declared at line 320 of "C:KeilARMINCSTSTM32F10xstm32f10x.h")
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef volatile signed long vs32;
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(35): error: #256: invalid redeclaration of type name "vsc32" (declared at line 324 of "C:KeilARMINCSTSTM32F10xstm32f10x.h")
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef volatile signed long const vsc32; /* Read Only */
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(39): error: #256: invalid redeclaration of type name "u32" (declared at line 328 of "C:KeilARMINCSTSTM32F10xstm32f10x.h")
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef unsigned long u32;
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(43): error: #256: invalid redeclaration of type name "uc32" (declared at line 332 of "C:KeilARMINCSTSTM32F10xstm32f10x.h")
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef unsigned long const uc32; /* Read Only */
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(47): error: #256: invalid redeclaration of type name "vu32" (declared at line 336 of "C:KeilARMINCSTSTM32F10xstm32f10x.h")
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef volatile unsigned long vu32;
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(51): error: #256: invalid redeclaration of type name "vuc32" (declared at line 340 of "C:KeilARMINCSTSTM32F10xstm32f10x.h")
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef volatile unsigned long const vuc32; /* Read Only */
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(55): error: #101: "FALSE" has already been declared in the current scope
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef enum {FALSE = 0, TRUE = !FALSE} bool;
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(55): error: #101: "TRUE" has already been declared in the current scope
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef enum {FALSE = 0, TRUE = !FALSE} bool;
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(55): error: #256: invalid redeclaration of type name "bool" (declared at line 345 of "C:KeilARMINCSTSTM32F10xstm32f10x.h")
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef enum {FALSE = 0, TRUE = !FALSE} bool;
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(57): error: #101: "RESET" has already been declared in the current scope
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus;
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(57): error: #101: "SET" has already been declared in the current scope
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus;
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(57): error: #256: invalid redeclaration of type name "FlagStatus" (declared at line 348 of "C:KeilARMINCSTSTM32F10xstm32f10x.h")
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus;
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(57): error: #256: invalid redeclaration of type name "ITStatus" (declared at line 348 of "C:KeilARMINCSTSTM32F10xstm32f10x.h")
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus;
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(59): error: #101: "DISABLE" has already been declared in the current scope
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState;
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(59): error: #101: "ENABLE" has already been declared in the current scope
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState;
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(59): error: #256: invalid redeclaration of type name "FunctionalState" (declared at line 350 of "C:KeilARMINCSTSTM32F10xstm32f10x.h")
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState;
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(62): error: #101: "ERROR" has already been declared in the current scope
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef enum {ERROR = 0, SUCCESS = !ERROR} ErrorStatus;
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(62): error: #101: "SUCCESS" has already been declared in the current scope
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef enum {ERROR = 0, SUCCESS = !ERROR} ErrorStatus;
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_type.h(62): error: #256: invalid redeclaration of type name "ErrorStatus" (declared at line 353 of "C:KeilARMINCSTSTM32F10xstm32f10x.h")
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: typedef enum {ERROR = 0, SUCCESS = !ERROR} ErrorStatus;
C:KeilARMINCSTSTM32F10xstm32f10x_type.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_conf.h(147): warning: #47-D: incompatible redefinition of macro "HSE_Value" (declared at line 85 of "C:KeilARMINCSTSTM32F10xstm32f10x.h")
C:KeilARMINCSTSTM32F10xstm32f10x_conf.h: #define HSE_Value ((u32)8000000) /* Value of the External oscillator in Hz*/
C:KeilARMINCSTSTM32F10xstm32f10x_conf.h: ^
C:KeilARMINCSTSTM32F10xstm32f10x_conf.h: stm32f10x_it.c: 1 warning, 21 errors
compiling board.c...
board.c: Error: #5: cannot open source input file "board.c": No such file or directory
board.c: board.c: 0 warnings, 1 error
Target not created
#define RT_USING_DEVICE
#define RT_USING_UART1
while (1)
{
if( myFlagUsart1WaitFor == FLAG_OVER )
{
usart1_cmd_process( );
//重回接收态;
DIR485_1_RE;
myFlagUsart1WaitFor = FLAG_SOI ;
}
//COM1重回接收状态;
//myFlagUsart1WaitFor = SOI ;
rt_thread_delay(2);//rt_thread_yield (); /* switch to other tasks */
}
在spstm3210project_lwip这个工程目录下,这个本来是一个LWIP的演示项目,但我想做个TCP通讯怎么都连不上,UDP通讯怎么都发不出去。后来分析代码才发现,在初始化DM9000A网卡的时候,网卡数据中断用的是外部中断4,而在stm32f10x_it.c文件中,却把中断处理放在了EXTI9_5_IRQHandler函数中,而不是EXTI4_IRQHandler,MCU收不到网络数据导致,我发现这个问题是在0.3.0版本,后来下载了0.3.1版本还是没改掉这个问题
在spstm3210project_lwip这个工程目录下,这个本来是一个LWIP的演示项目,但我想做个TCP通讯怎么都连不上,UDP通讯怎么都发不出去。后来分析代码才发现,在初始化DM9000A网卡的时候,网卡数据中断用的是外部中断4,而在stm32f10x_it.c文件中,却把中断处理放在了EXTI9_5_IRQHandler函数中,而不是EXTI4_IRQHandler,MCU收不到网络数据导致,我发现这个问题是在0.3.0版本,后来下载了0.3.1版本还是没改掉这个问题
这个是由于无相对应的开发板照成的。或许以后完全按照STM32Radio来进行?又或者,我们需要把驱动做得更智能化,更容易修改些。Anyway,谢谢你指出问题。
#ifdef __CC_ARM /* ARM Compiler */
【省略的代码】
#elif defined (__IAR_SYSTEMS_ICC__) /* IAR Compiler */
#elif defined (__GNUC__) /* GNU GCC Compiler, with minilibc */
#endif
#ifdef __CC_ARM /* ARM Compiler */
#elif defined (__IAR_SYSTEMS_ICC__) /* IAR Compiler */
【省略的代码】
#elif defined (__GNUC__) /* GNU GCC Compiler, with minilibc */
#endif
void rt_hw_interrupt_install(int vector, rt_isr_handler_t new_handler, rt_isr_handler_t *old_handler)
{
if(vector >= 0 && vector < MAX_HANDLERS)
{
/* get VIC address */
rt_uint32_t* vect_addr = (rt_uint32_t *)(VIC_BASE_ADDR + 0x100 + (vector << 2));
rt_uint32_t* vect_ctl = (rt_uint32_t *)(VIC_BASE_ADDR + 0x200 + (vector << 2));
/* assign IRQ slot and enable this slot */
*vect_ctl = 0x20 | (vector & 0x1F);
if (old_handler != RT_NULL) *old_handler = (rt_isr_handler_t) *vect_addr;
if (new_handler != RT_NULL) *vect_addr = (rt_uint32_t) new_handler;
}
}
355 /* lock filesystem */
356 dfs_lock();
357
358 fs = dfs_filesystem_lookup(fullpath);
在dfs_fs.c的dfs_unmount()函数中存在死锁:355 /* lock filesystem */
356 dfs_lock();
357
358 fs = dfs_filesystem_lookup(fullpath);
dfs_filesystem_lookup()也会去获取锁,356行和358需调换位置
請問我目前使用STM32F + MDK環境開發
用SVN版的RT-Thread遇到只要設complier flag -O1 以上就會有context switch無動作的情形
請問有人遇到跟我一樣的問題嗎:?:
請問我目前使用STM32F + MDK環境開發
用SVN版的RT-Thread遇到只要設complier flag -O1 以上就會有context switch無動作的情形
請問有人遇到跟我一樣的問題嗎:?:
void* rt_memmove(void *dest, const void *src, rt_ubase_t n)
{
char *tmp = (char *) dest, *s = (char *) src;
if (s < tmp && tmp < s + n)
{
tmp+=n;
s+=n;
while (n--)
*tmp-- = *s--;
}
else
{
while (n--)
*tmp++ = *s++;
}
return dest;
}
void* rt_memmove(void *dest, const void *src, rt_ubase_t n)
{
char *tmp = (char *) dest, *s = (char *) src;
if (tmp < s)
{
while (n--)
*tmp++ = *s++;
}
else if(tmp == s)
{
// do nothing!
}
else
{
tmp += n-1;
s += n-1;
while (n--)
*tmp-- = *s--;
}
return dest;
}
/* copy name */
for (temp = 0; temp < RT_NAME_MAX; temp ++)
{
object->name[temp] = name[temp];
}
这个代码是否有问题,如果name比较长的话,会不会导致截断的字符串没有/0结束符
今年六七月份的时候,在网上偶然发现国人的实时操作系统RT-Thread,令我欣喜无限。实验室正好一个项目需要做个网关,了解到RT-Thread已移入了TCP/IP协议栈LwIP,性能也不错,而且基于C的面向对象设计很有特色,就尝试使用了RT-Thread。现已做了一段时间,后来也一直关注着RT-Thread的发展,在此把开发过程中发现的RT-Thread的一些问题和建议反馈在这,希望对RT-Thread的发展有一点点点的帮助,不对的地方还请不吝赐教。
PS:如果能投入实际使用,我会向RT-Thread工作组申请License的,哈哈...
****
既然函数rt_thread_delay()与rt_thread_sleep()是相同的,就不要使用函数调用的方式编写函数rt_thread_delay(),只要将rt_thread_delay()使用宏替换为rt_thread_sleep()就好了,这样可避免由于函数调用而产生的额外的运行时间和空间的消耗,效率更高。
还有函数rt_sem_trytake()等类似。
****
typedef定义的命名风格不统一。
有的typedef后加了后缀_t而有的没有。
对于加后缀的,指针加_t后缀,非指针类型的也加后缀_t,这样导致不能从类型名的字面上区分指针和非指针变量。
改进:
① 对于typedef定义,要么统一加后缀_t,要么都不加后缀_t。
② 区别指针与非指针,若加了后缀_t,则以_pt和_t区别,若不加_t,则以加_p和不加_p区别。
另建议,RT-Thread作为一个开源项目,从开源的角度来看,应该让编程的风格易于用户理解,提高可读性,下面附上Linux中代码风格说明中Typedef的部分,是不是能参考下。
****
字符数组越界读取,虽然一般不会导致什么异常,但数组越界操作总是不好的,而且有些空间确实不能随便读取,否则会产生错误。
/* copy name */
for (temp = 0; temp < RT_NAME_MAX; temp ++)
{
object->name[temp] = name[temp];
}