分析的内核版本截止到2014-04-15,基于1.05正式版,blogs会及时跟进最新版本的内核开发进度,若源码注释出现”???”字样,则是未深究理解部分。
Raw-OS官方网站:http://www.raw-os.org/
Raw-OS托管地址:https://github.com/jorya/raw-os/
有了前一讲queue的基础后,这次来说说queue_size这个模块,前一讲的queue通信,知道queue在通信时,为了加快数据的传递速度,是不直接发送数据的具体内容,而是发送指向用户数据的指针,而且这个指针是void指针,在取出queue当中的数据时,强制转换这个void指针成发送的原始数据内容的指针类型,就可以准确获取原始数据。
那么queue_size这个东西,就是将指向原始数据的指针,和表示原始数据大小的变量,再打包封装成一个新的msg,称这个msg为queue_size的msg。
这个打包封装好的msg就是作为queue_size存储的基本单元,每一个单元就是一个queue_size的msg,这个msg包含了指向用户发送数据段的指针void *msg_ptr,和用户数据段的大小msg_size。
还注意到,还有一个*next的指针存在,这个*next指针就是将这些msg组成一个单项链表而存在的,学过数据结构的童鞋都知道~
现在,结合queue和queue_size总结一下,然后再看看queue_size和queue在内核表示的不同方式
1.在queue中,消息是一个可以指向任何数据类型的void指针
2.在queue_size中,消息是一个可以指向任何数据类型的void指针和一个表征数据大小msg_size变量封装体
在queue中,存放queue消息是通过指针数组来实现的~
那么就就可以很负责任地告诉你,queue_size中,存放queue_size消息是通过单向链表来实现的~
其实,个人觉得实现queue_size的消息存放时,同样可以用结构的指针数组去实现,但是这样用户就需要自己定义msg结构体,总感觉怪怪的,如果内核封装过的queue_size的msg,用户只要传入一个接收数据的void指针,一个接收数据大小的变量即可~
感觉还是API搞的鬼~
好~现在知道什么事queue_size的msg,和queue_size的msg在内核的存放形式,那么还要告诉你的一点就是,Raw-OS还有queue_size的空消息和具体消息的区别,也就是说,会将存放queue_size的msg分为两条链表,一条我自称为消息链表,一条称为空闲消息链表,尼玛啊,这又是什么???
其实也很简单,queue_size初始化时,将queue_size用来存放queue_size的msg的内存空间初始化成空闲消息链表,然后当queue_size的msg发送到达时,从空闲消息链表中划一个出来存放消息,那么存放消息的queue_size链表就是消息链表~
明白这些要点之后,骚年们~你们可以去看Raw-OS的代码是如何实现的了~
下面附上queue_size有注释的若干条主要函数,其实就是创建,发送,接收的而已~
1.Queue_size的创建
RAW_U16 raw_queue_size_create(RAW_QUEUE_SIZE *p_q, RAW_U8 *p_name, RAW_MSG_SIZE *msg_start, MSG_SIZE_TYPE number) { RAW_MSG_SIZE *p_msg1; RAW_MSG_SIZE *p_msg2; /* 检查创建消息队列的边界条件 */ #if (RAW_QUEUE_SIZE_FUNCTION_CHECK > 0) if (p_q == 0) { return RAW_NULL_OBJECT; } /* * 传入的消息块的起始地址,这里是一个二级指针,通常传入的是数组指针的首地址 * 例如在应用层定义,void* msg[size],那么msg[size]就是数组,msg=msg[0]是数组首地址,&msg就是数组指针首地址 * 传入时就是传入&msg的值,通常会将这个值强制转成(RAW_VOID **)&msg的形式传入,无关紧要,传入的是数组指针首地址 * * 如果没有定义存储消息的消息数组,错误返回 */ if (msg_start == 0) { return RAW_NULL_POINTER; } /* 存储消息的消息数组大小,需要创建queue时指定,例如定义的是void* msg[10],那么这里传入的就是10,为0错误返回 */ if (number == 0) { return RAW_ZERO_NUMBER; } #endif /* 初始化queue的阻塞链表 */ list_init(&p_q->common_block_obj.block_list); /* queue名称 */ p_q->common_block_obj.name = p_name; /* queue阻塞方式 */ p_q->common_block_obj.block_way = RAW_BLOCKED_WAY_PRIO; /* current_numbers代表当前在消息数组中的消息数量 */ p_q->queue_current_msg = 0; /* queue历史最大消息数量 */ p_q->peak_numbers = 0; /* 存储消息的消息数组的大小 */ p_q->queue_msg_size = number; /* queue中消息起始地址,区别于普通queue,这里msg_start指向queue_size剩余空闲元素链表 */ p_q->free_msg = msg_start; /* * 空闲消息链表初始化 * * 在queue_size中,消息数组存放的不再是void指针,而是RAW_MSG_SIZE类型的元素 * 在queue_size组织RAW_MSG_SIZE类型消息时,分为两个链表,第一个是消息链表,第二个是空闲消息链表 * 这里初始化的是空闲消息链表 */ p_msg1 = msg_start; p_msg2 = msg_start; p_msg2++; while (--number) { p_msg1->next = p_msg2; p_msg1->msg_ptr = 0; p_msg1->msg_size = 0; p_msg1++; p_msg2++; } /* 对空闲消息链表的最后一个元素,即链表尾部,指向任何元素,即消息链表和空闲消息链表都是单向链表 */ p_msg1->next = 0; p_msg1->msg_ptr = 0; p_msg1->msg_size = 0; /* 初始queue控制块对象为Raw-OS消息队列对象类型 */ p_q->common_block_obj.object_type = RAW_QUEUE_SIZE_OBJ_TYPE; TRACE_QUEUE_SIZE_CREATE(raw_task_active, p_q); return RAW_SUCCESS; }
2.发送Queue_size消息
对于queue_size仍然有发送的queue_size的末端还是发送到queue_size的前端的发送选择,这个根据用户决定消息的紧急程度而定
RAW_U16 raw_queue_size_create(RAW_QUEUE_SIZE *p_q, RAW_U8 *p_name, RAW_MSG_SIZE *msg_start, MSG_SIZE_TYPE number) { RAW_MSG_SIZE *p_msg1; RAW_MSG_SIZE *p_msg2; /* 检查创建消息队列的边界条件 */ #if (RAW_QUEUE_SIZE_FUNCTION_CHECK > 0) if (p_q == 0) { return RAW_NULL_OBJECT; } /* * 传入的消息块的起始地址,这里是一个二级指针,通常传入的是数组指针的首地址 * 例如在应用层定义,void* msg[size],那么msg[size]就是数组,msg=msg[0]是数组首地址,&msg就是数组指针首地址 * 传入时就是传入&msg的值,通常会将这个值强制转成(RAW_VOID **)&msg的形式传入,无关紧要,传入的是数组指针首地址 * * 如果没有定义存储消息的消息数组,错误返回 */ if (msg_start == 0) { return RAW_NULL_POINTER; } /* 存储消息的消息数组大小,需要创建queue时指定,例如定义的是void* msg[10],那么这里传入的就是10,为0错误返回 */ if (number == 0) { return RAW_ZERO_NUMBER; } #endif /* 初始化queue的阻塞链表 */ list_init(&p_q->common_block_obj.block_list); /* queue名称 */ p_q->common_block_obj.name = p_name; /* queue阻塞方式 */ p_q->common_block_obj.block_way = RAW_BLOCKED_WAY_PRIO; /* current_numbers代表当前在消息数组中的消息数量 */ p_q->queue_current_msg = 0; /* queue历史最大消息数量 */ p_q->peak_numbers = 0; /* 存储消息的消息数组的大小 */ p_q->queue_msg_size = number; /* queue中消息起始地址,区别于普通queue,这里msg_start指向queue_size剩余空闲元素链表 */ p_q->free_msg = msg_start; /* * 空闲消息链表初始化 * * 在queue_size中,消息数组存放的不再是void指针,而是RAW_MSG_SIZE类型的元素 * 在queue_size组织RAW_MSG_SIZE类型消息时,分为两个链表,第一个是消息链表,第二个是空闲消息链表 * 这里初始化的是空闲消息链表 */ p_msg1 = msg_start; p_msg2 = msg_start; p_msg2++; while (--number) { p_msg1->next = p_msg2; p_msg1->msg_ptr = 0; p_msg1->msg_size = 0; p_msg1++; p_msg2++; } /* 对空闲消息链表的最后一个元素,即链表尾部,指向任何元素,即消息链表和空闲消息链表都是单向链表 */ p_msg1->next = 0; p_msg1->msg_ptr = 0; p_msg1->msg_size = 0; /* 初始queue控制块对象为Raw-OS消息队列对象类型 */ p_q->common_block_obj.object_type = RAW_QUEUE_SIZE_OBJ_TYPE; TRACE_QUEUE_SIZE_CREATE(raw_task_active, p_q); return RAW_SUCCESS; }
3.接收Queue_size消息
RAW_U16 raw_queue_size_receive(RAW_QUEUE_SIZE *p_q, RAW_TICK_TYPE wait_option, RAW_VOID **msg_ptr, MSG_SIZE_TYPE *receive_size) { RAW_U16 result; RAW_MSG_SIZE *msg_tmp; RAW_SR_ALLOC(); /* 检查中断嵌套,和用户设置的接收阻塞标志,仅当用户设置接收不到消息时不发生阻塞才能在中断中接收消息 */ #if (RAW_QUEUE_SIZE_FUNCTION_CHECK > 0) if (raw_int_nesting && (wait_option != RAW_NO_WAIT)) { return RAW_NOT_CALLED_BY_ISR; } /* 检查传入消息队列控制块的地址,为空时,说明没有实体,错误返回 */ if (p_q == 0) { return RAW_NULL_OBJECT; } /* 这里传入的是用来存放接收数据后,数据存放的变量,一个二级指针 */ if (msg_ptr == 0) { return RAW_NULL_POINTER; } /* 这里传入的是用来存放接收数据后存放消息大小的变量,一个int或uint即可 */ if (receive_size == 0) { return RAW_NULL_POINTER; } #endif /* 当开启task 0后,消息由task 0转发??? */ #if (CONFIG_RAW_ZERO_INTERRUPT > 0) if (raw_int_nesting) { return RAW_NOT_CALLED_BY_ISR; } #endif RAW_CRITICAL_ENTER(); /* 如果传入的queue控制的类型对象不是Raw-OS的队列对象,错误返回 */ if (p_q->common_block_obj.object_type != RAW_QUEUE_SIZE_OBJ_TYPE) { RAW_CRITICAL_EXIT(); return RAW_ERROR_OBJECT_TYPE; } /* * 获取queue_size中消息的实现过程 * * 判断queue_size是否有消息,如果没有,想要获取queue_size的任务阻塞 * 存在消息时: * 1.临时消息msg_tmp指向读指针,因为读指针指向消息链表的第一个消息,那么msg_tmp就指向第一个消息 * 2.传入的存放queue_size消息的内容指针指向第一个消息中的内容指针 * 3.传入的存放queue_size消息的大小变量变为第一个消息的大小 * 4.消息链表的read指针后移到下一个消息 * 5.回收消息链表被读取的消息,置空后归还到空闲消息链表 * */ if (p_q->queue_current_msg) { /* 指向read指针 */ msg_tmp = p_q->read; /* 读取queue_size消息内容 */ *msg_ptr = msg_tmp->msg_ptr; /* 读取queue_size消息大小 */ *receive_size = msg_tmp->msg_size; /* read指针后移 */ p_q->read = msg_tmp->next; /* 消息链表不为空时(消息链表的最后一个消息下一个消息链接为0),存在消息链表的消息数量-- */ if (p_q->read) { p_q->queue_current_msg--; } /* 消息链表为空时,write指针置0,消息数量清0 */ else { p_q->write = 0; p_q->queue_current_msg = 0; } /* 回收消息链表被读取的消息内存空间,加入到空间消息链表 */ msg_tmp->next = p_q->free_msg; p_q->free_msg = msg_tmp; RAW_CRITICAL_EXIT(); TRACE_QUEUE_SIZE_GET_MSG(raw_task_active, p_q, wait_option, *msg_ptr, *receive_size); return RAW_SUCCESS; } /* 用户设置不阻塞标志时,返回空指针queue_size消息,大小为0 */ if (wait_option == RAW_NO_WAIT) { *msg_ptr = 0; *receive_size = 0; RAW_CRITICAL_EXIT(); return RAW_NO_PEND_WAIT; } SYSTEM_LOCK_PROCESS_QUEUE_SIZE(); /* 当queue_size没有消息时,对要获取queue_size消息进行阻塞,阻塞到queue_size的阻塞链表上 */ raw_pend_object((RAW_COMMON_BLOCK_OBJECT *)p_q, raw_task_active, wait_option); RAW_CRITICAL_EXIT(); TRACE_QUEUE_SIZE_GET_BLOCK(raw_task_active, p_q, wait_option); /* 系统任务调度 */ raw_sched(); /* 获取阻塞任务被调度时的阻塞返回状态 */ result = block_state_post_process(raw_task_active, 0); /* 如果阻塞任务是queue_size存在消息被调度的,不是因为阻塞超时等等的情况调度,那么将获取queue_size消息 */ if (result == RAW_SUCCESS) { /* 之前分析过post msg时,消息是直接发送到任务控制块中的,当阻塞任务被调度时,从任务控制块中取出消息给msg,返回调度后阻塞状态 */ *receive_size = raw_task_active->msg_size; *msg_ptr = raw_task_active->msg; } else { *msg_ptr = 0; *receive_size = 0; } return result; }