从printk和logMsg看Linux和vxworks的设计理念差异

HandsomeHong 2012-09-17

printk是linux内核的打印函数,类似用户态下的printf,可在中断上下文调用;同样,logMsg是vxworks内核的打印函数,也可在中断上下文调用。
linux开始作为桌面系统设计,后来广泛用于服务器领域,慢慢扩展到了嵌入式系统;而vxworks则以高性能实时嵌入式系统闻名。
从这可以看出,它们的差异是很大的,考查两个同样的功能在不同系统的实现,管中窥豹,虽有不足,但也可以了解两个系统(分时系统与实时系统)设计上的一些考虑。

下面先看看printk的实现(内核版本2.6.38):
1,关内核抢占,关闭中断;
2,把打印信息格式化到一个临时缓存区;
3,添加打印级别,打印时间(可选,编译配置);
4,把格式化后的信息从临时缓存区拷贝到环回缓存区(log_buf,意味着缓存区满了之后,新的数据会覆盖老数据),
5,释放控制台的信号量,唤醒klogd守护线程;
6,打开中断,使能内核抢占;
7,klogd得到调度则会把环回缓存区的信息在控制台上显示。

再来看看vxworks下的logMsg的实现(版本号5.5.1):
1, 系统初始化时会创建一个消息队列(典型的生产者消费者模型,类似上面的环回缓存区,但不支持覆盖写入);一个系统打印线程,类似上面的klogd;
2,首先判断调用logMsg的上下文,如果是中断,则入消息队列时不等待,就是说尝试获取消息队列的信号量,不成功就返回;
3,如果获取队列信号量成功,说明没有其他人在操作消息队列,拷贝当前打印参数到消息队列中去,共拷贝7个参数:
打印字符串的指针;参数1-参数6(就是说,logMsg最多只支持6个打印参数);
4,logMsg返回;
5,系统打印线程得到调度后发现消息队列中存在待打印的消息,取出打印信息;
6,打印信息进行格式化;
7,显示格式化的信息;

从上面的流程上看,linux与vxworks的内核打印处理需要完成的动作是类似的,差异的是动作的编排上,那部分动作先做,那部分动作后做。
linux的printk是一种很常规的处理流程,而logMsg则特别做了设计,logMsg的处理非常简单高效,拷贝7个参数到消息队列中,然后就返回了,剩下的事情由打印线程完成,而linux的大部分动作都在printk完成,打印线程只负责显示。

它们两者的缺点也很明确:

1)printk关内核抢占,关中断,在大量打印情况下可能导致中断丢失,一些依赖于中断的处理可能会不及时,如高精度定时器;
2)而logMsg在中断下的打印是尝试在消息队列中保存打印参数,不做格式化,所以必须明确最大支持的打印参数个数,且打印信息必须是
const char* 类型的,就是说打印信息不能动态生成的;
3)两者在大量打印的情况下都会有丢信息,logMsg满了就不接受新的打印信息,而printk则是新的打印信息覆盖久的打印信息。

为什么要这样做呢,目的何在?

vxworks作为一个实时系统,实时系统要求能够在指定或者确定的时间内完成系统功能和外部或内部、同步或异步时间做出响应,中断的及时响
应是至关重要的。想想一个战斗机的场景,飞行员下发发射命令(对系统来说就是一个按键中断),系统100毫秒(十分之一秒)才做出反应,
而敌机立即做出反应,我方的炸弹还未发射就机毁人亡了。因此,vxworks这么调整,保证打印不会阻塞中断,也就理所当然了。
而linux开始作为一个分时的桌面系统设计,对普通用户来说,一个按键是10毫秒响应还是100毫秒响应,是很难感受出来的。

从这个简单的例子可以看出,一个系统开始的设计理念,会影响到代码的方方面面,从中细细品味,也有不少收获。