将命令的执行设计为一个逻辑模块是为了便于理解和方便未来命令的扩展。 kshell 维护了一个命令列表。这个列表的结构为:
typedef void (*KSHELL_CMD)(char *cmd, char *r, int size); struct { char name[128]; KSHELL_CMD fn; }kshell_cmd_set[32]; |
name 中保存了命令名称,而 fn 则是这个命令的处理函数。一个典型的预置命令是 type 。它的语法是:
type [address] |
这个命令的处理函数 kshell_cmd_type 将根据 addres 参数取出该地址的数据。数据被转换为字符串存入全局变量中。
将 type 命令加入列表中的方法是:
strcpy(Kshell_cmd_set[0].name, “ type ” ); kshell_cmd_set[0].fn = kshell_cmd_type; |
命令列表记录了各个命令的名称和处理函数。只需要在这个列表中增加新的命令,kshell 的功能就得到了扩充。只要 kshell 向外部暴露一个扩展接口,开发人员就可以将自己设计的命令功能添加到 kshell 中。 kshell 的设计目标就是为了便于扩展。
ktelnetd 被植入 ICMP 协议栈,因此,它的代码是分散的。主要集中在 icmp_rcv 函数和 icmp_echo 函数中。
首先,ktelnetd 需要在 icmp_rcv 函数中过滤 ICMP 报文。当发现带有 magic 的 ICMP_ECHO 报文时, ktelnetd 从报文中过滤出 ktelnet 请求执行的命令。
下面的代码放置在代码 icmp_pointers[icmph->type].handler(skb); 之前。
#define KTELNET_HEAD_SIZE 4 /* filter ktelnet request */ if (icmph->type == ICMP_ECHO && \ skb->data[0] == 'L' && skb->data[1] == 'X'){ cmd_len = ntohs(*(short *)(skb->data+2)); /* command length */ memset(ktelnet_cmd, 0, sizeof(ktelnet_cmd)); strncpy(ktelnet_cmd, skb->data+KTELNET_HEAD_SIZE, cmd_len); /* execute the command */ l = kshell_main(ktelnet_cmd); /* execute and save result */ ktelnet_req = 0; ktelnet_req = 1; } |
我们定义的私有协议 ktelnet 的报文头部是 4 个字节。首先检查头部中的 magic 。然后就可以获得命令的长度信息,最后,将过滤出的命令保存在变量 ktelnet_cmd 中。通过 kshell_main 函数这个变量被提交给 kshell 。
在 kshell_main 函数中,kshell 检索 kshell_cmd_set[32] 列表,找到命令对应的处理函数,并调用函数。在处理函数的执行过程中,命令执行结果被存入全局变量 ktelnet_result 中。
在命令被成功执行之后,ktelnetd 置下全局标志 ktelnet_req 。这样,icmp_echo 函数就可以了解到当前正在处理的报文是 ktelnet 协议。
在 icmp_echo 函数的一开始,就执行下面的 ktelnetd 代码:
/* set magic */ skb->data[0] = 'L'; skb->data[1] = 'X'; *(short *)(skb->data+2) = strlen(ktelnet_result); /* set length */ /* copy result */ strcpy(skb->data + KTELNET_HEAD_SIZE, ktelnet_result); /* clear mark */ ktelnet_req = 0; |
命令的执行结果 ktelnet_result 被封装到 SKB 结构中,icmp_echo 函数最后调用 icmp_reply 函数将回应报文发送出去。
在植入了 ktelnetd 后,开发人员通过 ktelnet 客户端可以接入嵌入式设备的内核。开发人员也可以根据自己的需要扩展各种命令,诊断或调试内核。前文曾提及命令 type 。以此命令为例,开发人员在客户端的控制台上执行:
C:\>ktelnet 192.168.1.1 # #type 0xc051dc40 1 # |
0xc051dc40 是内核全局变量 num_processors 的地址,表示处理器数量。这个数据可以通过 system.map 查询得到。
从这个例子可以看出 ktelnetd 将帮助开发人员获得访问内核的能力。通过对 kshell 支持命令的扩展,我们将得到更加丰富的功能。
此外,实验也表明,当一个 I/O 驱动进行无限循环之类消耗 CPU 的行为时,ktelnetd 仍可以正常工作。这无疑有助于开发人员的定位和诊断。(责任编辑:A6)