复杂度3/5
机密度4/5
最后更新2021/05/18
下面我们要研究被加载程序本身,这和普通c程序没什么太大区别,当然,区别还是有一些的,例如没有main。普通C程序本身,省缺会需要main函数,作为程序入口,而内核扩展程序是通过链接时定义,决定哪个函数是主入口的。甚至可能完全没有入口,只等待其它内核程序调用自己提供的扩展函数。我们先考虑最普通的内核扩展,也就是需要一个入口,需要进行初始化的内核扩展程序。下面的程序看起来与普通的C没什么不同是吧?其中几个主要区别我们说一下:
- 没有main,这个说过了,内核扩展本身没有main;
- 所有log输出,要走bsdlog,因为作为内核扩展,是没有stdout/stderr这种东西的,linux使用printk在内核里分几k的空间实现,aix没有这种机制(可以自己做一个),一般用bsdlog把信息打印出来,示范程序中将打印DEBUG和KERN两种信息;
- 当前kernel extension本身不需要pincode,所谓pincode是把代码“固定”在内存里,而不会交换出内存到磁盘。如果kernel extension的操作与磁盘交换有关而又没有pin在内存,就可能会出现因data store interrupt重入而死掉;当然,对于与磁盘交换完全无关的kernel extension(例如本例:其实啥都没干。。。),pincode不是必要的操作,而且如果使用了pincode,那么所有的退出出口前,必须记得unpin释放内存;
- 从这个例子里可以看到,对应的入口命令CFG_INIT和CFG_TERM是自己解释的,也就是由加载程序和被加载程序二者之间协商一致,通过内核sysconfig系统调用直接传送过来,并不进行额外解读,因此可以自己自由定义。同时还有入口的输入结构uio和长度,其实也是如此,但在本例中未涉及到更复杂的结构数据传送,再后续继续深入分析。
/* myExtension.c */
#include <strings.h>
#include <sys/errno.h>
#include <sys/types.h>
#include <sys/syslog.h>
#include <sys/device.h>
#include <sys/pin.h>
#include <sys/thread.h>
#include <sys/signal.h>
#include <unistd.h>
int myEntry(int const cmd, struct uio* const uio_buf)
{
int rc = 0;
bsdlog(LOG_DEBUG|LOG_KERN,"Enter myEntry: command = 0x%d \n",cmd);
if( cmd == CFG_INIT )
{
bsdlog(LOG_DEBUG|LOG_KERN,"Initializing my Kernal Extension \n");
rc = pincode( (int (*)())myEntry);
if( rc )
{
bsdlog(LOG_DEBUG|LOG_KERN,"pincode failed with rc=%d\n",rc);
return rc;
}
}
else if( cmd == CFG_TERM )
{
bsdlog(LOG_DEBUG|LOG_KERN,"Terminating my Kernal Extension \n");
rc = unpincode(myEntry);
if( rc )
{
bsdlog(LOG_DEBUG|LOG_KERN,"Failed to unpin the code. rc = %d\n", rc );
}
}
return 0;
}
/*最后这个函数是demo,作为新支持的系统调用提供 */
int test_syscall( int arg )
{
bsdlog(LOG_DEBUG|LOG_KERN,"In test_syscall: recevied arg %d.....\n",arg);
return 0;
}
关于bsdlog的使用:
- 先生成一个“种子”log文件
# touch /tmp/testbsd.log
- 通过在syslog中定义使用此log文件
#cat /etc/syslog.conf
...
kern.debug /tmp/testbsd.log rotate size 100K files 4
...
以上信息告诉syslog将kern和debug类型的bsdlog写入到/tmp/testbsd.log文件,此文件最多4个,循环使用,每个大小为100k,记录内核的DEBUG信息,这与示范程序中LOG_KERN | LOG_DEBUG对应。
- 刷新syslog使对syslog.conf的修改生效
#refresh -s syslogd
注意,bsdlog不是同步的,最后写入文件会有一定(几秒)延时,特别在多线程情况下,不能保证时间前后一致,但对于单一线程,还是能确保前后一致的,只是可能整体滞后几秒。由于调试kernel经常出现死机情况,log仅作为执行参考,而死机还是需要通过dump分析去判定具体位置和原因。
下面继续介绍内核扩展编译makefile和相关问题