编写 AIX 内核扩展

扩展您的技能

了解如何使用 AIX 内核来构建例程以扩展相应的功能,以及如何创建自己的系统调用、内核进程或文件系统。Power 体系结构飞快发展,加之 AIX® Version 5.3 的各种增强功能,使得人们对 AIX 出现了前所未有的兴趣。本文通过大量的示例向您介绍如何利用 AIX 中内核扩展的强大功能。

Sandesh Chopdekar, 软件工程师, EMC

Sandesh Chopdekar 的照片Sandesh Chopdekar 是 IBM 的软件工程师,他在编译器和网络方面有着非常丰富的经验。目前,他是位于印度 Pune 的 IBM Software Labs 的 Andrew File System L3 团队的负责人。在 3 年前加入 IBM 之前,Sandesh 在 Citicorp 的一家子公司工作。他获得了 Mumbai University 的化学学士学位。您可以通过 sandesh_vc@in.ibm.com 与 Sandesh 联系。



Avinesh Kumar (avineshkarn@in.ibm.com), 软件工程师, EMC

Avinesh Kumar 的照片Avinesh Kumar 是位于印度 Pune 的 IBM Software Labs 的 Andrew File System L3 团队的软件工程师。他负责内核和转储及崩溃的用户级调试,以及报告 Linux、AIX 和 Solaris 平台中的错误。Avinesh 拥有 Pune 大学计算机科学系的 MCA。Avinesh 是一个 UNIX 爱好者(主要是 Linux),并将业余时间用来玩他的 Linux 计算机 (FC-4)。您可以通过 avineshkarn@in.ibm.com 与 Avinesh 联系。



2006 年 9 月 07 日

引言

随着 AIX® Version 5.3 的引入,加之 Power 体系结构的飞快发展和 *nix 市场的增长,全世界的开发人员对 AIX 产生了更大的兴趣。本文将帮助您在 AIX 中着手开发自己的内核扩展。

通过使用内核扩展,AIX 内核提供了动态扩展其功能的接口。内核扩展是添加到内核以扩展其功能的例程。内核扩展是 AIX 内核体系结构中一个重要的部分,并且它们类似于其他平台上的可动态加载的模块,如 Linux®,这些模块可以为运行中的内核添加新的功能,而无需重新启动计算机。

内核扩展运行于内核保护域中。您可能在某些时候需要使用内核扩展,例如,需要创建自己的系统调用、内核进程或文件系统时。内核扩展与内核以相同的模式运行。如果内核运行于 64 位模式,那么扩展也必须能够运行于该模式。

本文面向那些希望在 AIX 中编写自己的内核扩展的读者。仅需要对 UNIX® 编程有基本的了解即可。

术语 kernext 有时用作内核扩展的缩写,尤其是在代码中。


关于内核扩展

添加的用于扩展 AIX 内核的例程可以分为下面的一些类别:

  • 系统调用
  • 虚拟文件系统
  • 内核扩展和设备驱动程序管理内核服务
  • 设备驱动程序

无论例程属于哪个类别,它们都称为内核扩展。

内核扩展的生命周期具有下面一些阶段,具体包括:

  • 加载——这个阶段将内核扩展加载到内核内存中。
  • 执行——这个阶段启动内核扩展的执行,并调用它的入口点。这是初始化过程。
  • 通信——这个阶段与内核扩展进行通信。现在,该内核扩展正在进行它的工作。
  • 终止——这个阶段将终止内核扩展。
  • 卸载——这个阶段将内核扩展从内存中卸载。

sysconfig() 系统调用可用来控制这些阶段。sysconfig() 需要超级用户权限,并且它是管理内核扩展的基本应用程序编程接口 (API)。其声明如下:

int sysconfig(             (Defined in /usr/include/sys/sysconfig.h )
   int cmd,                /* function requested: load, unload, etc */
   void *parmp,            /* addr of struct containing info for cmd */
   int parmlen            /* length of parmp information */
 );

sysconfig() 接受一个命令参数 (cmd),用于选择调用者所需的操作。

下面的表 1 中列举了一些常见的操作。有关完整的列表,请参阅 /usr/include/sys/sysconfig.h

表 1. 常见操作
操作描述
*) SYS_KLOAD将内核扩展目标文件加载到内核内存中。
*) SYS_SINGLELOAD 如果尚未加载,则加载内核扩展目标文件。
*) SYS_QUERYLOAD确定是否加载了指定的内核目标文件。
*) SYS_KULOAD 卸载以前加载的内核目标文件。
*) SYS_CFGKMOD出于配置的目的,从模块入口点处调用指定的模块。

您可以使用自定义命令代码,假设对它进行了正确的处理。您还可以使用 strload 命令加载或卸载内核扩展。通常,您需要编写自己的代码来加载您的内核扩展。

让我们来研究一些重要的结构:

Struct cfg_load

当您加载或卸载内核扩展时,该结构将传递到 sysconfig()。这个结构在 /usr/include/sys/sysconfig.h 中定义为:

struct cfg_load
{
      caddr_t path;       /* ptr to object module pathname    */
      caddr_t libpath;    /* ptr to a substitute libpath */
      mid_t   kmid;       /* kernel module id (returned) */
  };

struct cfg_load 的结构成员包括:

  • path——这个命令指定了您的内核扩展模块目标文件的路径名。
  • libpath——这个命令是搜索内核扩展目标文件的路径。如果该字段为 null,那么将使用 path 字段。
  • Kmid——这个命令包括内核模块 ID。根据该选项,传递到 sysconfig()。它可以由 sysconfig() 填充,或者必须将其传递到 sysconfig( )

当您调用模块入口点时,Struct cfg_kmod 结构被传递到 sysconfig( )Struct cfg_kmod/usr/include/sys/sysconfig.h 中定义为:

struct cfg_kmod  {
      mid_t   kmid;       /* module ID of module to call */
      int cmd;            /* command parameter for module  */
      caddr_t mdiptr;     /* pointer to module dependent info */
      int mdilen;         /* length of module dependent info */
};

cfg_kmod 的结构成员包括:

  • kmid——这个命令指定了内核扩展模块 ID,模块加载后将返回该值。
  • cmd——这个命令为模块指定了命令。这可能是一个用户定义的值,或者可以来自 <sys/device.h>,正如下面的示例中所完成的工作。
  • mdiptr——这个命令用来将数据传递到内核扩展。
  • mdilen——这个命令包括 mdiptr 传入的数据的长度。

现在,让我们来研究一些实际的内容。


Hello World 内核扩展

您现在应该可以开始编写简单的“Hello World”内核扩展。您将编写两个程序:

要编译 Hello World 内核扩展的示例代码,只需在复制源代码的目录中输入 make。出于一些显著的安全性因素,只有 root 用户才能够加载或卸载内核扩展。因为加载的代码运行于内核模式,所以它可以访问系统中所有的内容。因此,即使是 root 用户拥有的 setuid 程序,也不能够加载内核扩展,您必须以 root 登录。请参见下面的清单 1

清单 1. 控制器应用程序:kctrl.c
1 /* Controller App */
2
3 #include  <stdio.h>
4 #include  <errno.h>
5 #include  <string.h>
6 #include  <sys/types.h>
7 #include  <sys/stat.h>
8 #include  <sys/sysconfig.h>
9 #include  <sys/device.h>
10
11 int main(int argc, char *argv[])
12 {
13     struct  cfg_kmod    opt_kmod;
14     struct  cfg_load    opt_load, query_load;
15     struct  stat        statbuf;
16     char    szKernExt[256], c;
17
18     /* Check if the user has appropriate privileges  */
19     if (getuid() != 0) {
20         fprintf(stderr, " Not SuperUser.\n");
21         exit(EACCES);
22     }
23
24     /* Check arguments */
25     if (argc != 2) {
26         printf ("Usage: %s <kernel_extension>\n", argv[0]);
27         exit(EINVAL);
28     }
29
30     strcpy(szKernExt,argv[1]);
31
32         /* Check existence of file */
33     if (stat(szKernExt,&statbuf) != 0) {
34         perror("stat");
35         exit(errno);
36     }
37
38     /* Fill up the cfg_load structure */
39     opt_load.path = szKernExt;      /* file name  */
40     opt_load.libpath = NULL;        /* no library */
41     opt_load.kmid = 0;
42
43     /* Perform various operations on the kernel extension */
44     while(1) {
45
46         fprintf (stderr, "\n Enter choice, (l)oad, (u)nload, (i)nit, (t)erm, 
                (q)uery or (e)nd\n");
47
48         while((c = getchar()) < 'a' && c > 'z')  ;  /* discard garbage */
49
50         switch(c) {
51
52         case 'l':         /* load a kernel extension */
53
54                         /* load kernel extension request  */
55                      if (sysconfig(SYS_KLOAD,&opt_load,sizeof(struct   cfg_load)))
56                 perror("sysconfig(SYS_KLOAD)");  /* print error message  */
57            else                         /* otherwise             */
58               printf("Extension Successfully loaded, kmid is %d\n", opt_load.kmid);
59
60                         break;
61
62                 case 'i': /* Initialize a KernExt */
63
64                 /* Initialize  the kernel extension  */
65             opt_kmod.kmid = opt_load.kmid;
66             opt_kmod.cmd = CFG_INIT;
67             opt_kmod.mdiptr = NULL;
68             opt_kmod.mdilen = 0;
69             if (sysconfig(SYS_CFGKMOD,&opt_kmod,sizeof(struct cfg_kmod)))
70                 perror("sysconfig(SYS_CFGKMOD)");  /* print error message */
71                         else
72                                 printf(" Extension Initialized \n");
73
74             break;
75
76         /* Unload kernel extension */
77         case 'u':
78             /* Check if KernExt is loaded */
79             if (opt_load.kmid == 0)
80                 printf("kernel Extension not loaded\n");
81             else  {
82                 if (sysconfig(SYS_KULOAD,&opt_load,sizeof(struct cfg_load)))
83                     perror("sysconfig(SYS_KULOAD)");
84                 else                    
85                     fprintf(stderr, "KernExt Successfully Unloaded \n");
86             }
87
88             break;
89
90          /* Terminate the kernel extension */
91                 case 't':
92
93             /* Check if KernExt is loaded */
94             if (opt_load.kmid == 0)
95                 fprintf(stderr, "Extension not loaded\n");
96             else  {
97               opt_kmod.kmid = opt_load.kmid;
98               opt_kmod.cmd  = CFG_TERM;    /* Terminate  the kernel extension */
99               opt_kmod.mdiptr = NULL;
100               opt_kmod.mdilen = 0;
101               if (sysconfig(SYS_CFGKMOD,&opt_kmod,sizeof(struct  cfg_kmod)))
102                   perror("sysconfig(SYS_CFGKMOD)");  /* print error */
103                            else
104                                fprintf(stderr, " KernExtension  Terminated \n");
105                               }
106
107                         break;
108
109
110         /* query kernel extension existence */
111         case 'q':
112
113             query_load.path     = opt_load.path;
114             query_load.libpath  = opt_load.libpath ;
115             query_load.kmid     = 0;
116
117             if (sysconfig(SYS_QUERYLOAD,&query_load,sizeof(struct cfg_load)))
118                 perror("sysconfig(SYS_QUERYLOAD)");
119             else
120                       {
121                             if(query_load.kmid > 0)
122                       fprintf(stderr, " Extension is loaded, with kmid   %d \n",
                                query_load.kmid);
123                             else
124                       fprintf(stderr, " Extension is not loaded \n");
125                       }
126
127             break;                       /* done  */
128
129
130         case 'e':
131             exit(0);
132
133
134         default:
135             fprintf(stderr, "Incorrect option \n");
136             break;
137         }
138         getchar();
139     }
140
141         return 0;
142 }

现在您需要定义一些变量,特别是 struct cfg_kmodstruct cfg_load 类型的变量。

struct  cfg_kmod    opt_kmod;
struct  cfg_load    opt_load, query_load;

加载内核扩展

首先,要加载内核扩展,您需要调用带 SYS_KLOAD 命令参数的 sysconfig。传递 opt_loadsizeof(struct cfg_load)

sysconfig(SYS_KLOAD,&opt_load,sizeof(struct cfg_load))

在成功调用 sysconfig 之后,您可以得到 kmid,并显示在屏幕上。

使用内核扩展的路径填充 cfg_load 结构:

opt_load.path = szKernExt;

libpath 设置为 NULL,因为在本示例中,并不依赖于任何非系统定义的库:

opt_load.libpath = NULL;

kmid 字段初始化为 0:

opt_load.kmid = 0;

如下所示,执行其代码:

[root@aix1] ./kctrl ./kernext_hello
Enter choice, (l)oad, (u)nload, (i)nit, (t)erm, (q)uery or (e)nd
l
Extension Successfully loaded, kmid is 49033924

使用 genkex 命令,您可以查看系统中已加载的内核扩展的列表:

[root@aix1] genkex 
Text address     Size File
2ec3000      2d8 ./kernext_hello 
<Snip>

初始化内核扩展

在加载了内核扩展之后,调用 sysconfig( ) 以使用 CFG_INIT 命令调用其入口点。

控制器应用程序使用了 sysconfig() 例程。如下所示,填充 struct cfg_kmod opt_kmod

opt_kmod.kmid = opt_load.kmid;
opt_kmod.cmd = CFG_INIT;
opt_kmod.mdiptr = NULL;
opt_kmod.mdilen = 0;

将内核扩展的标识符填入 kmid 字段,在上一次调用 sysconfig( ) 时可以获得该内核扩展标识符。

CFG_INIT 命令为内核扩展调用入口点。现在,没有向内核扩展传递任何参数,因此,mdiptrNULLMdilen 包含 mdiptr 的长度。

在示例内核扩展中,我们添加了将输出结果打印到 syslog 文件的例程。检查您系统中的 syslog 输出,它的输出应该与下面所示类似:

Apr  3 08:22:40 aix1 kern:debug unix: Enter hello_init:: command = 0x1
Apr  3 08:22:40 aix1 kern:debug unix:  Initializing Hello World  KernExt

查询内核扩展

您可以使用 struct cfg_load 来查询扩展是否已经加载。您可以使用 query_load 变量来完成该任务。

现在,根据 opt_load 变量的值填充 path 和 libpath,并调用带 SYS_QUERYLOAD 参数的 sysconfig 命令,如下所示:

query_load.path     = opt_load.path;
query_load.libpath  = opt_load.libpath ;
query_load.kmid     = 0;

如果该内核扩展已加载,那么 kmid 字段中包含了这个内核扩展的 kmid,其值始终大于 0。如下所示,您得到了该内核扩展的 kmid

[root@aix1] ./kctrl ./kernext_hello
Enter choice, (l)oad, (u)nload, (i)nit, (t)erm, (q)uery or (e)nd
q
Extension is loaded, with kmid 49033924

终止内核扩展

要终止内核扩展,可以使用嵌入在 struct cfg_kmod 中的 CFG_TERM 命令来调用 sysconfig()。然后,如下所示,将相应的值填入 opt_kmod 变量:

opt_kmod.kmid = opt_load.kmid;
opt_kmod.cmd  = CFG_TERM;
opt_kmod.mdiptr = NULL;
 opt_kmod.mdilen = 0;

现在已经有了 kmid,可以将 CFG_TERM 填入该命令,如下所示:

[root@aix1] ./kctrl ./kernext_hello
Enter choice, (l)oad, (u)nload, (i)nit, (t)erm, (q)uery or (e)nd
t
KernExtension Terminated

在 syslog 的输出中,您将看到下面的几行内容:

Apr  3 08:22:43 fsaix9 kern:debug unix: Enter hello_init:: command = 0x2
Apr  3 08:22:43 fsaix9 kern:debug unix:  Terminating Hello World  KernExt

卸载内核扩展

要卸载内核扩展,可以调用带 SYS_KULOAD 参数的 sysconfig,并且传递 struct cfg_load,该结构已填入了 pathkmid 字段,如下所示:

sysconfig(SYS_KULOAD,&opt_load,sizeof(struct cfg_load)

要卸载内核扩展,可以使用选项“u”并选择 Enter,如下所示:

[root@aix1] ./kctrl ./kernext_hello
Enter choice, (l)oad, (u)nload, (i)nit, (t)erm, (q)uery or (e)nd
u
KernExt Successfully Unloaded

在使用 sysconfig() 时请多加小心,不正确的 kmid 值可能会使系统崩溃(请参见清单 2)。

清单 2. Hello World 内核扩展:kernext_hello.c
1 /* A Hello World Kernel Extension */
2 #include <errno.h>
3 #include <syslog.h>
4 #include <sys/device.h>
5
6
7
8  int hello_init(int cmd, struct uio *uio)
9  {
10
11    bsdlog(LOG_DEBUG | LOG_KERN, "Enter hello_init::? command = 0x%x \ n",cmd);
12
13    if (cmd == CFG_INIT)
14          bsdlog(LOG_DEBUG | LOG_KERN, " Initializing Hello World  KernExt \n");
15    else if (cmd == CFG_TERM)
16              bsdlog(LOG_DEBUG | LOG_KERN, " Terminating Hello World   KernExt \n");
17    else
18              bsdlog(LOG_DEBUG | LOG_KERN, " Unknown command to  Adv  KernExt \n");
19
20
21       return 0;
22  }

内核扩展没有 main() 例程,相反,它包含一个入口点,当通过 sysconfig() 使用 SYS_CFGKMOD 发出 CFG_INIT 命令时,控制器应用程序将调用这个入口点。

在这个内核扩展示例中,只有一个函数,即内核扩展入口点 hello_init()。该入口点有两个参数,其中包括一个整数,该整数是从 sysconfig() 传入的嵌入在 struct cfg_kmod 中的命令参数。另一个参数是 struct uio,它用于将数据传递到该内核扩展。在 Hello World 内核扩展中,没有像前面的部分那样使用 struct uio

在缺省情况下,入口点的名称必须是 __start( )。如果要指定另一个名称,如本示例所示,您需要在编译您的内核扩展时加以指定。

Hello World 内核扩展告知收到相应的命令,并将其打印出来。它使用 bdslog() 进行打印。bsdlog 的第 1 个参数是优先级。这里使用了两种标志来表示优先级:

  • LOG_DEBUG 告诉 syslog,这是一则调试消息
  • LOG_KERN 告诉 syslog,这则消息来自内核

要获得日志消息,请确保配置了 syslogd

例如,在 /etc/syslog.conf 中,可以添加如下内容:

*.debug         /tmp/syslog.out     rotate size 100k files 4

然后添加 ps –aef | grep syslog,并对其提供 kill –HUP

下面的清单 3 中的编译过程是用于 Hello World 内核扩展的 Makefile。

清单 3. Hello World 内核扩展的 Makefile
1  all: kernext_hello kctrl
2
3  kctrl: kctrl.c
4      cc -o kctrl kctrl.c
5
6
7  kernext_hello: kernext_hello.c
8      cc -q32 -o kernext_hello32.o -c kernext_hello.c
9      ld -b32 -o kernext_hello32 kernext_hello32.o -e hello_init 
             -bI:/usr/lib/kernex.exp -lsys -l csys
10      cc -q64 -o kernext_hello64.o -c kernext_hello.c
11      ld -b64 -o kernext_hello64 kernext_hello64.o -e hello_init 
             -bI:/usr/lib/kernex.exp -lsys -l csys
12      rm -f kernext_hello
13      ar -X32_64 -r -v kernext_hello kernext_hello32 kernext_hello64
14
15  clean:
16      rm -f *.o kernext_hello kernext_hello32 kernext_hello64 kctrl  2> /dev/null

在这个 Makefile 中,有两个目标:控制器应用程序和 Hello World 内核扩展。

用于控制器应用程序的命令非常简单,其中使用 -o 选项指定了目标文件:

4     cc -o kctrl kctrl.c

编译内核扩展

先对内核扩展进行编译,然后进行连接。正如前面提到的,如果内核扩展的缺省入口点不是 __start,那么必须使用带 -e 开关的连接程序 ld 对其进行指定。

请注意,该内核扩展不应该使用 cc 进行编译,应该在最后的连接步骤中使用 ld

要编译 32 位和 64 位版本的内核扩展,可以分别使用 –q32–q64 标志。

在本示例中,内核扩展入口点不是 __start(),所以使用 –e 开关标识该入口点。因为您在内核扩展中使用了从内核导出的符号,所以需要使用 -bI:/usr/lib/kernex.exp 指定这个依赖关系,该依赖关系可以指向相应的文件。

AIX 提供了两个库 libsys.a 和 libcsys.a,内核扩展可以使用这两个库。您可以使用 –lsys–lcsys 开关将这两个库连接到您的内核扩展。

支持 32 和 64 位

AIX 运行于 32 和 64 位模式,并且它具有双模式 内核扩展。双模式内核扩展可用于简化运行于 32 和 64 位内核的内核扩展的加载。双模式内核扩展是一个存档文件,其中包含了 32 和 64 位版本的内核扩展作为其成员。当 sysconfigkmod_load 调用中指定的路径名为存档时,加载器将加载目标模式与内核的执行模式相匹配的第 1 个存档成员。在 Hello World 内核扩展中,您建立了 32 和 64 位内核扩展的双模式内核扩展。

AIX 内核扩展的复杂之处在于它识别内核扩展的方式。AIX 根据内核扩展的路径名来辨认这些内核扩展。如果您使用不同的路径来调用内核扩展,如绝对路径(假设您位于 /tmp 目录):

[root@aix1] ./kctrl  /tmp/kernext_hello

而下一次使用相对路径:

[root@aix1] ./kctrl ./kernext_hello

那么这两个内核扩展的示例将被内核作为不同的 内核扩展对待。


高级内核扩展

本部分内容将研究更高级一些的内核扩展。通过将新的系统调用列举到一个导出文件,并附加上 syscall 标签,内核扩展可以为内核添加新的系统调用或接管现有的系统调用。当加载这样的扩展时,将创建系统调用表的新副本。

您将编写两个与将添加到运行中的内核的系统调用有关的文件。一个用来导出系统调用符号 (demo_syscall.exp),另一个包含系统调用的定义 (demo_syscall.c),即控制器程序 (kctrl.c),用来控制新的系统调用的加载和卸载,一个使用新的系统调用的示例程序 (invoke_syscall.c),而一个编译所有这些程序的 Makefile,如下面的清单 4 所示。

清单 4. demo_syscall.exp 文件
1 #!/unix
2 demo_syscall syscall

在这个文件中,仅导出了正在定义的系统调用符号,如下面的清单 5 所示。

清单 5. 系统调用符号
1 /* A bit advanced  kernext */
2
3 #include <stdio.h>
4 #include <errno.h>
5 #include <syslog.h>
6 #include <sys/device.h>
7
8 int demo_syscall_init(int cmd, struct uio *uio)
9 {
10       int iErr = 0;
11
12       bsdlog(LOG_DEBUG | LOG_KERN, "demo_syscall_init:? command = 0x%x  \n",cmd);
13
14       if (cmd == CFG_INIT) {
15
16               bsdlog(LOG_DEBUG | LOG_KERN, " Loading Adv KernExt \n");
17               if(iErr = pincode(demo_syscall_init))
18                       return iErr;
19
20       } else if (cmd == CFG_TERM) {
21
22               bsdlog(LOG_DEBUG | LOG_KERN, " UnLoading Adv KernExt \n");
23               if(iErr = unpincode(demo_syscall_init))
24                       return iErr;
25
26       } else {
27               bsdlog(LOG_DEBUG | LOG_KERN, " Unknown command to  Adv KernExt \n");
28               return -1;
29       }
30
31       return 0;
32 }
33
34   /* Implementation of the demo syscall */
35   demo_syscall(int arg)
36   {
37        return(arg + 25);
38   }

新的系统调用非常简单。它仅仅只是将传入的参数加上 25,然后返回这个和(请参见清单 6)。

清单 6. kctrl.c 的清单
1 /* Controller App for KernExt */
2
3 #include  <stdio.h>
4 #include  <errno.h>
5 #include  <string.h>
6 #include  <sys/types.h>
7 #include  <sys/stat.h>
8 #include  <sys/sysconfig.h>
9 #include  <sys/device.h>
10
11 int main(int argc, char *argv[])
12 {
13     struct  cfg_kmod    opt_kmod;
14     struct  cfg_load    opt_load, query_load;
15     struct  stat        statbuf;
16     char    szKernExt[256], c;
17
18     /* Check if the user has appropriate privileges  */
19     if (getuid() != 0) {
20         fprintf(stderr, " Not SuperUser.\n");
21         exit(EACCES);
22     }
23
24     /* Check arguments */
25     if (argc != 2) {
26         printf ("Usage: %s <kernel_extension>\n", argv[0]);
27         exit(EINVAL);
28     }
29
30     strcpy(szKernExt,argv[1]);
31
32         /* Check existence of file */
33     if (stat(szKernExt,&statbuf) != 0) {
34         perror("stat");
35         exit(errno);
36     }
37
38     /* Fill up the cfg_load structure */
39     opt_load.path = szKernExt;      /* file name */
40     opt_load.libpath = NULL;        /* no library  */
41     opt_load.kmid = 0;
42
43     /* Perform various operations on the kernel extension */
44     while(1) {
45
46         fprintf (stderr, "\n Enter choice, (l)oad, (u)nload, (i)nit, (t) erm, 
                (q)uery or (e)nd\n");
47
48         while((c = getchar()) < 'a' && c > 'z')  ;  /* discard garbage */
49
50         switch(c) {
51
52         case 'l':         /* load a kernel extension      */
53
54                       /* load kernel extension request   */
55                    if (sysconfig(SYS_KLOAD,&opt_load,sizeof(struct   cfg_load)))
56               perror("sysconfig(SYS_KLOAD)");  /* print error message  */
57            else
58              printf("Extension Successfully loaded, kmid is %d\n", opt_load.kmid);
59
60                         break;
61
62                 case 'i': /* Initialize a KernExt */
63
64                 /* Initialize  the kernel extension  */
65             opt_kmod.kmid = opt_load.kmid;
66             opt_kmod.cmd = CFG_INIT;
67             opt_kmod.mdiptr = NULL;
68             opt_kmod.mdilen = 0;
69             if (sysconfig(SYS_CFGKMOD,&opt_kmod,sizeof(struct cfg_load)) )
70                 perror("sysconfig(SYS_CFGKMOD)");  /* print error message */
71                         else
72                                 printf(" Extension Initialized \n");
73
74             break;
75
76         /* Unload kernel extension */
77         case 'u':
78             /* Check if KernExt is loaded */
79             if (opt_load.kmid == 0)
80                 printf("kernel Extension not loaded\n");
81             else  {
82                 if (sysconfig(SYS_KULOAD,&opt_load,sizeof(struct cfg_load)))
83                     perror("sysconfig(SYS_KULOAD)");
84                 else
85                     fprintf(stderr, "KernExt Successfully Unloaded \n");
86             }
87
88             break;
89
90          /* Terminate the kernel extension   */
91                 case 't':
92
93             /* Check if KernExt is loaded */
94             if (opt_load.kmid == 0)
95                 fprintf(stderr, "Extension not loaded\n");
96             else  {
97                opt_kmod.kmid = opt_load.kmid;
98                opt_kmod.cmd  = CFG_TERM;    /* Terminate  the kernel extension */
99                opt_kmod.mdiptr = NULL;
100                opt_kmod.mdilen = 0;
101                if (sysconfig(SYS_CFGKMOD,&opt_kmod,sizeof(struct  cfg_kmod)))
102                    perror("sysconfig(SYS_CFGKMOD)");  /* print error */
103                           else
104                               fprintf(stderr, " KernExtension  Terminated \n");
105                            }
106
107                        break;
108
109
110         /* query kernel extension existence */
111         case 'q':
112
113             query_load.path     = opt_load.path;
114             query_load.libpath  = opt_load.libpath ;
115             query_load.kmid     = 0;
116
117             if (sysconfig(SYS_QUERYLOAD,&query_load,sizeof(struct cfg_load)))
118                 perror("sysconfig(SYS_QUERYLOAD)");
119             else
120                     {
121                            if(query_load.kmid > 0)
122                    fprintf(stderr, " Extension is loaded, with kmid %d \n", 
                            query_load.kmid);
123                            else
124                    fprintf(stderr, " Extension is not loaded \n");
125                     }
126             break;
127
128         case 'e':
129             exit(0);
130
131
132         default:
133             fprintf(stderr, "Incorrect option \n");
134             break;
135         }
136         getchar();
137     }
138
return 0;
140  }

清单 7 是处理内核扩展加载和卸载的控制器程序。

清单 7. invoke_syscall.c 的清单
1 /* Call the implemented Demo system call */
2 #include <stdio.h>
3
4 int main()
5 {
6      int iVal = 0;
7
8      fprintf(stderr, " Invoking demo syscall \n");
9
10      if ( (iVal = demo_syscall(99)) < 0)
11      {
12         perror("demo_syscall error");
13         exit(1);
14      }
15
16      fprintf(stderr, " Got Value - %d\n",iVal);
17
18
19         return 0;
20 }

这只是一个使用新的系统调用的示例程序。

下面的清单 8 是编译高级内核扩展中所有程序的 Makefile。

清单 8. 编译高级内核扩展中所有程序的 Makefile
1 all: demo_syscall invoke_syscall kctrl
2
3 kctrl: kctrl.c
4      cc -o kctrl kctrl.c
5
6 invoke_syscall: invoke_syscall.c
7      cc  -o invoke_syscall -bI:demo_syscall.exp invoke_syscall.c
8
9 demo_syscall: demo_syscall.c
10      cc -q32 -o demo_syscall32.o -qlist -qsource -c demo_syscall.c
11      mv demo_syscall.lst demo_syscall32.lst
12      ld -b32 -o demo_syscall32 demo_syscall32.o -e demo_syscall_init
            -bI:/usr/lib/kernex.exp -bE:demo_syscall.exp -lsys -l csys
13      cc -q64 -o demo_syscall64.o -qlist -qsource -c demo_syscall.c
14      mv demo_syscall.lst demo_syscall64.lst
15      ld -b64 -o demo_syscall64 demo_syscall64.o -e demo_syscall_init
             -bI:/usr/lib/kernex.exp -bE:demo_syscall.exp -lsys -l csys
16      rm -f demo_syscall
17      ar -X32_64 -r -v demo_syscall demo_syscall32 demo_syscall64
18
19 clean:
20      rm -f *.o invoke_syscall demo_syscall demo_syscall32 demo_syscall64 kctrl
            demo_syscall64.lst demo_syscall32.lst 2> /dev/null

参考资料

学习

获得产品和技术

  • IBM 试用软件:使用 IBM 软件开发您的下一个项目,可直接从 developerWorks 下载这些试用软件。

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=AIX and UNIX
ArticleID=158126
ArticleTitle=编写 AIX 内核扩展
publish-date=09072006