AIX内核扩展编程1
程序员文章站
2024-01-30 16:22:40
...
复杂度3/5
机密度4/5
最后更新2021/05/18
前面介绍过内核扩展也是一种执行程序,与普通执行程序不同的是它需要被加载到内核中执行,拥有内核权限。我们先来看看加载、卸载和内核扩展程序本体。
加载需要用到cfg_kmod结构和sysconfig系统调用,前者对要加载的程序进行描述,后者完成加载(或卸载)过程。要注意,不止sysconfig,还有好几个类似的系统调用都完成类似的功能,他们之间没有本质区别,只是适用场景(方便程度)不同,例如,有的调用一步完成加载及初始化,有的则分为若干步,需要使用不同参数,重复执行几次。
int sysconfig (Cmd, Parmp, Parmlen)
int Cmd; /* 子功能,使用同一调用,设置不同功能可以分别完成加载、
初始化、卸载等多个功能,例如:
SYS_KLOAD 加载内核扩展;
SYS_SINGLELOAD 同上,不同的是会检测内核扩展是否已加载,如果没加载过,则加载,而前者则是不管是否加载过,都再加载一次。此功能等于帮你做了查询+加载;
SYS_QUERYLOAD 查询内核扩展是否已经被加载。那查询的依据是什么?
很傻的,依据是被加载程序的路径,而且只是路径字符,如果分别给绝对
路径和相对路径,那该命令会人为是不同的内核扩展。再回到SINGLELOAD
的疑问,如果使用SYS_KLOAD加载了两次怎么办?凉拌,内核不管的,
随便你加载几次,卸载的时候根据kmid这个唯一值卸载。
另一个问题?谁会被使用?如果能定位(通过kmid)则用确定的程序,
如果不能定位,一般是最后加载的最优先,前面的就变成死代码;
SYS_KULOAD 卸载;
SYS_QDVSW 查询设备表,设备表是让被加载程序能被执行的关键方案之一,
我们以后说(另一个方案是export 函数名,把加载的程序当成库函数使用)
当然还有一次性的,由用用户态加载程序触发的初始化dd方案,既下一个命令;
SYS_CFGDD 告知内核去执行被加载的程序,一般内核扩展被加载到内存,
下一件事就是执行这个,让它自己把自己初始化好。sysconfig调用执行这个命令后
会让内核从被加载程序的入口点执行(关于入口设置稍后介绍);
SYS_CFGKMOD 与上一个命令没啥区别,不知道为啥弄出两个来,为了满足
“命名”需求?一个叫device driver,一个叫kernel model?
SYS_GETPARMS 告诉内核程序把你需要返回的参数返回,其实是提供了一个简单的,从内核扩展取数据的方案;
SYS_SETPARMS 如果你想发参数给内核扩展,可以用这个;
*/
void *Parmp; /* 输入参数地址,这个参数其实是个结构,对应不同子功能,
* 需要指向不同结构 */
int Parmlen; /* 输入参数长度,既对应结构的具体长度 */
/* cfg_load结构是与SYS_KLOAD或SINGLELOAD对应的,别搞错了 */
struct cfg_load
{
caddr_t path; /* 被加载程序地址,可以是绝对路径,也可以是相对路径(相对执行加载程序所在当前目录) */
caddr_t libpath; /* 如果被加载程序有动态链接库,指明链接库所在目录 */
mid_t kmid; /* 这个是返回值,加载成功会返回一个数值,其实是被加载程序所加载的地址,通过它可以唯一确定被加载程序 */
};
/* 加载之后不能扔在那里不管,还要最后交代两句,你要怎么做,这就是
SYS_KMOD(或者SYS_CFGDD)命令对应的操作,这两个命令发出去后,
内核会从被加载程序的入口点执行,具体执行什么工作,那就是你写的
扩展程序自己要求啦,可以为所欲为,当然,还是有一些约定熟成的套
路的。此处注意结构和命令要配对,如果用SYS_KMOD用cfg_kmod struct,
而如果用SYS_CFGDD,则要用sys_cfgdd struct */
struct cfg_kmod
{
mid_t kmid; /* 前面load返回的kmid,错了就对不上了 */
int cmd; /* 自己定义的命令,会发送给扩展程序 ,两边对应上就好*/
caddr_t mdiptr; /* 如果要发送更多信息,就通过这个指针传送数据结构所在位置 */
int mdilen; /* 传送数据结构的程度,这个结构是自己定义的,还是
* 要和被加载程序能识别的结构对应上
*/
};
下面是loader程序:
/* kloader.c kernel extension loader example */
#include <sys/types.h>
#include <sys/sysconfig.h>
#include <errno.h>
int rc=0;
struct cfg_load queryLoad; /* 先定义一个结构 ,query使用cfg_load结构 */
bzero (&queryLoad, sizeof (queryLoad)); /* 清零,准备填充数据 */
queryLoad.path = "./kernExt"; /* 被加载程序所在位置,程序中用命令行传进来的参数替代 */
queryLoad.libpath = NULL; /* 无链接库 */
rc=sysconfig (SYS_QUERYLOAD, &queryLoad, sizeof (queryLoad)); /* 前面介绍过要先查一下是否已经加载过 */
if (rc != 0 ) {
printf(stderr, "query load error: %d!\n", errno);
exit(errno);
}
if ( queryLoad.kmid != 0 ) {
printf(stderr, "Kernel extension has been loaded already!\n");
exit(1);
}
/* 查询过,没有被加载,万事大吉,可以load了!下面复用了query时定义的load结构,不推荐。。。但省事*/
rc=sysconfig (SYS_KLOAD|SYS_64BIT, &queryLoad, sizeof(queryLoad) );
if (rc != 0 ) {
printf(stderr, "Load kernel extension error: %d!\n", errno);
exit(errno);
}
/* 初始化kernel extension */
struct cfg_kmod configLoad; /* 定义结构 */
configLoad.kmid = queryLoad.kmid; /* 需要kmid确定是哪个程序 */
configLoad.cmd = CFG_INIT; /* 子命令,这个其实随便写点啥,是由你的被加载程序处理的,只要是int就可以 */
configLoad.mdiptr = NULL; /* 先简单点,没有别的参数 */
configLoad.mdilen = 0;
rc=sysconfig (SYS_CFGKMOD|SYS_64BIT, &configLoad, sizeof(configLoad));
/* 为什么要置SYS_64BIT位呢?因为我们要写一个64位的扩展程序,需要让加载函数知道要加载的是64位的 */
if (rc != 0 ) {
printf(stderr, "Init kernel extension error: %d!\n", rc);
/* 这里其实应当增加unload过程*/
exit(rc);
}
/* 其它工作。。。。 略,然后准备unload,先terminal,又复用结构,坏习惯!我知道我自己的程序是怎么写的呀!气死你! */
configLoad.cmd = CFG_TERM; /* 子命令,被加载程序识别为TERM就OK */
configLoad.mdiptr = NULL; /* 还是没有别的参数 */
configLoad.mdilen = 0;
rc=sysconfig (SYS_CFGKMOD|SYS_64BIT, &configLoad, sizeof(configLoad));
if (rc != 0 ) {
printf(stderr, "Term kernel extension error: %d!\n", rc);
/* 这里不能设置unload过程了,为什么?自己想 */
exit(rc);
}
/* 复用queryLoad 。。。只需要kmid */
rc = sysconfig (SYS_KULOAD|SYS_64BIT, &queryLoad, sizeof (queryLoad));
if (rc != 0) {
printf(stderr, "Unload kernel extension error: %d!\n", rc);
}
exit(rc);
好,这是全部的查询、加载、初始化、终止、卸载过程,当然还不够精细,不过都不是大问题了。下一节介绍被加载的主体:kernel extension模块
上一篇: AIX 系统应用性能分析
下一篇: AIX系统扩容文件系统