- 论坛徽章:
- 0
|
本帖最后由 71v5 于 2014-07-10 07:09 编辑
在前面简单分析了系统调用机制后,下面看看内核提供的用来注册系统调用的接口,这样就可以编写一个具有特殊功能的系统调用,以满足特定的需要,既然注册的是
系统调用,系统调用属于内核的一部分,所以编写这个系统调用,基本上使用都是内核中的变量,数据结构,函数等等。
内核的这个接口,是以模块的形式来提供的, 当需要注册的系统调用编写完成后,在文件中使用宏SYSCALL_MODULE向内核声明这个模块,然后将相应的源文件编译
成可共享的目标文件(一般是.ko结尾),然后就可以使用外部工具kldload将这个模块加载到内核中。
先来看一个SYSCALL_MODULE宏会用到的一个数据结构:
[struct syscall_module_data对象]:- /********************************************************************************************
- * 该数据结构主要由函数syscall_module_handler来使用。
- 成员描述:
- chainevh:
- 一个函数指针,当模块被加载或者卸载时,如果该函数非空,就会被调用。
- chainarg:
- chainevh成员指向的函数的参数。
- new_sysent:
- 指向类型为struct sysent类型的对象,这个数据对象中的成员需要我们来显式初始化,
- 用来描述将要注册到内核中的系统调用。
-
- offset:
- 指向一个整形变量,该整形变量的值为数组sysent即系统调用方法表的索引,也就是所注册
- 系统调用将要使用的系统调用号。
- 在数组sysent中,.sy_call成员指向lkmnosys函数的数组元素共有10个,在选择系统调用号
- 时,优先选择这些数组元素的索引.
- 在数组sysent中,.sy_call成员指向lkmressys函数的数组元素共有50个,这些数组元素的索引
- 也能作为所注册系统调用的系统调用号,不过,这些数组元素索引的使用貌似已经约定好了,
- 每个索引都有对应的函数,而且这些函数在内核中都有相应的实现,所以即使这些数组元素
- 的.sy_call成员指向lkmressys函数,为了避免冲突,尽量不要使用这些数组元素的索引。
- 当保存在变量offset中的值为NO_SYSCALL时,内核会为我们选择一个合适的系统调用号。
- #define NO_SYSCALL (-1)
-
- old_sysent:
- 和new_sysent,offset成员结合起来,假设x为所选择的有效的系统调用号,那么当注册完成
- 后有:
- old_sysent = sysent[x];
- sysent[x] = *new_sysent;
- offset,old_sysent,new_sysent三个成员可以结合函数syscall_register看会更清楚。
- 该函数简要分析如下。
- 一般情况下,当把有意义的参数传递给宏SYSCALL_MODULE后,该宏会帮我们声明并初始化
- 相关的数据对象。
- ****************************************************************/
- 166 struct syscall_module_data {
- 167 int (*chainevh)(struct module *, int, void *); /* next handler */
- 168 void *chainarg; /* arg for next event handler */
- 169 int *offset; /* offset into sysent */
- 170 struct sysent *new_sysent; /* new sysent */
- 171 struct sysent old_sysent; /* old sysent */
- 172 };
复制代码 [列举部分数组sysent中的元素]:- struct sysent sysent[] = {
- ...............................................................................................................
- ...............................................................................................................
- { AS(nosys_args), (sy_call_t *)lkmnosys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 210 = lkmnosys */
- { AS(nosys_args), (sy_call_t *)lkmnosys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 211 = lkmnosys */
- { AS(nosys_args), (sy_call_t *)lkmnosys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 212 = lkmnosys */
- { AS(nosys_args), (sy_call_t *)lkmnosys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 213 = lkmnosys */
- { AS(nosys_args), (sy_call_t *)lkmnosys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 214 = lkmnosys */
- { AS(nosys_args), (sy_call_t *)lkmnosys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 215 = lkmnosys */
- { AS(nosys_args), (sy_call_t *)lkmnosys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 216 = lkmnosys */
- { AS(nosys_args), (sy_call_t *)lkmnosys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 217 = lkmnosys */
- { AS(nosys_args), (sy_call_t *)lkmnosys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 218 = lkmnosys */
- { AS(nosys_args), (sy_call_t *)lkmnosys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 219 = lkmnosys */
- ..............................................................................................................
- ..............................................................................................................
- { AS(nfssvc_args), (sy_call_t *)lkmressys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 155 = nfssvc */
- { AS(semsys_args), (sy_call_t *)lkmressys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 169 = semsys */
- { AS(msgsys_args), (sy_call_t *)lkmressys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 170 = msgsys */
- { AS(shmsys_args), (sy_call_t *)lkmressys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 171 = shmsys */
- ..............................................................................................................
- ..............................................................................................................
- };
复制代码 下面将创建并注册一个简单的系统调用来看一下注册过程,这样就会对系统调用有更深刻的理解。
该系统调用名字为cu_printpid,cu_printpid用来输出系统中每个进程的name和pid,以及每个进程的子进程的name和pid,以及内核中
全部struct module对象的name;该系统调用不需要参数,选择的系统调用号为212,系统调用cu_printpid没有对返回值进行处理,对互斥
这方面的处理也不是很到位,所以不是很健壮,不过从测试过程来开,貌似没有出问题,系统调用cu_printpid简单实现如下所示:
[将要注册的系统调用cu_printpid简单分析]:- /***********************************************************
- * 1-13:
- 需要用到的内核头文件。
- ************************************/
- 1 #include <sys/ctype.h>
- 2 #include <sys/param.h>
- 3 #include <sys/kernel.h>
- 4 #include <sys/module.h>
- 5 #include <sys/proc.h>
- 6 #include <sys/sysent.h>
- 7 #include <sys/sysproto.h>
- 8 #include <sys/systm.h>
- 9 #include <sys/lock.h>
- 10 #include <sys/sx.h>
- 11 #include <sys/mutex.h>
- 12 #include <sys/module.h>
- 13 #include <sys/linker.h>
- 14
- /********************************************************************
- * 15-27:
- 自定义的函数cu_mod_event,在实现系统调用cu_printpid的模块
- 被加载和卸载时,将调用该函数。
- 当被加载时,将输出"register syscall"。
- 当被卸载时,将输出"unregister syscall"。
- *************************/
- 15 static int cu_mod_event(struct module *cu_mod,int cu_what,void *cu_data)
- 16 {
- 17 switch (cu_what) {
- 18 case MOD_LOAD:
- 19 uprintf("register syscall\n");
- 20 break;
- 21 case MOD_UNLOAD:
- 22 uprintf("unregister syscall\n");
- 23 break;
- 24 }
- 25
- 26 return 0;
- 27 }
- 28
- /****************************************************************************************
- * 29-37:
- 自定义的传递给内核函数linker_file_foreach的回调函数。用来输出系统中全部
- module的name。
- 这里简单说明一下,当一个模块被加载到内核时,实际上是实现模块的可共享
- 目标文件被加载,内核会为该目标文件创建一个struct linker_file类型的数据对象
- 来描述该目标文件,所有的struct linker_file对象都链接到以linker_files
- 为head的链表上,struct linker_file对象的modules成员链接了包含在该文件
- 中的所有module。
- 内核函数linker_file_foreach遍历链表linker_files上的每一个struct linker_file对象,
- 然后我们这里自定义的回掉函数cu_predicate遍历每个struct linker_file对象的modules成员。
- *****************************/
- 29 static int cu_predicate(linker_file_t cu_linker_file,void *cu_data)
- 30 {
- 31 module_t cu_mod;
- 32 for (cu_mod = TAILQ_FIRST(&cu_linker_file->modules); cu_mod;cu_mod = module_getfnext(cu_mod)) {
- 33 uprintf("%s\n",module_getname(cu_mod));
- 34 }
- 35
- 36 return 0;
- 37 }
- 38
- /************************************************************
- * 39:
- cu_index变量是我们这里局部定义的,保存的是系统调用号,
- 这里我们选择的系统调用号为212,即显示指定。
- *****************************/
- 39 int cu_index = 212;
- 40
- 41
- /***********************************************************
- * 42-44:
- freebsd内核中系统调用实现函数的参数的标准结构。
- 这里我们将其定义为struct cu_printpid_args类型。
- ************************/
- 42 struct cu_printpid_args {
- 43 int dummy;
- 44 };
- 45
- /*************************************************************************
- * 实现系统调用cu_printpid的内核函数,将要被注册到内核。
- 参数描述:
- cu_td:系统调用异常处理程序将控制传递给实现系统调用cu_printpid的内核
- 函数时,cu_td指向当前正在运行线程对应的struct thread对象。
-
- uap:参数,sys_cu_printpid不会使用这个参数。
- ***********************/
- 46 static int sys_cu_printpid(struct thread *cu_td, struct cu_printpid_args *uap)
- 47 {
- 48
- 49 struct proc *cu_p;
- 50
- 51
- /*************************************************************************************
- * 53-66:
- 遍历链表allproc。
- 链表allproc链接了系统中全部的进程(状态不是PRS_ZOMBIE)。
-
- 53:
- 链表allproc由共享独占锁allproc_lock保护,为了防止并发访问,先要申请
- 加锁。
-
- 55:
- 内核提供了宏FOREACH_PROC_IN_SYSTEM来简化对链表allporc的访问,遍历时,
- cu_p指向链接到链表allproc上进程对应的struct proc对象。
- 57:
- 使用内核函数uprintf输出struct proc对象中的相应成员。这里需要使用函数uprintf,
- 内核中对该函数的描述如下:
- Uprintf prints to the controlling terminal for the current process。
- 59-63:
- 访问进程cu_p的p_children成员,该成员是链表的head,链接了进程cu_p
- 的全部子进程,该成员由共享独占锁proctree_lock保护,因此需要先加锁。
-
- 66:执行相应的解锁。
- *******************************/
- 52
- 53 sx_slock(&allproc_lock);
- 54
- 55 FOREACH_PROC_IN_SYSTEM(cu_p) {
- 56 struct proc *cu_child;
- 57 uprintf("My name:%s, My pid:%d\n",cu_p->p_comm,cu_p->p_pid);
- 58
- 59 sx_slock(&proctree_lock);
- 60 LIST_FOREACH(cu_child, &cu_p->p_children, p_sibling) {
- 61 uprintf(" process%d's child: [name: %s], [pid: %d]\n",cu_p->p_pid,cu_child->p_comm,cu_child->p_pid);
- 62 }
- 63 sx_sunlock(&proctree_lock);
- 64 }
- 65
- 66 sx_sunlock(&allproc_lock);
- 67
- /********************************************************************************
- * 68:
- 调用内核函数linker_file_foreach输出内核中全部模块的name,注意,这里的模块
- 是由struct module对象描述的,输出的name为struct module对象name成员指向的
- 字符串,该字符串才是模块的实际name。
- ******************************************/
- 68 linker_file_foreach(cu_predicate,NULL);
- 69
- /*****************************************************************
- * 70:设置相应的返回值。
- ********************************/
- 70 cu_td->td_retval[0] = 0;
- 71
- 72 return 0;
- 73 }
- 74
- /****************************************************************
- * 75-84:
- 这里就是我们静态定义的用来描述系统调用cu_printpid的
- struct sysent对象。
- ****************************/
- 75 struct sysent cu_printpid_sysent = {
- 76 .sy_narg = 0,
- 77 .sy_call = (sy_call_t *)sys_cu_printpid,
- 78 .sy_auevent = AUE_NULL,
- 79 .sy_systrace_args_func = NULL,
- 80 .sy_entry = 0,
- 81 .sy_return = 0,
- 82 .sy_flags = 0,
- 83 .sy_thrcnt = 0,
- 84 };
- 85 SYSCALL_MODULE(cu_printpid_syscall, &cu_index, &cu_printpid_sysent,cu_mod_event, NULL);
复制代码 [宏SYSCALL_MODULE展开后如下所示]:- static struct syscall_module_data cu_printpid_syscall_syscall_mod = {
- .chainevh = cu_mod_event,
- .chainarg = NULL,
- .offset = &cu_index,
- .new_sysent = &cu_printpid_sysent,
- { 0, NULL, AUE_NULL } // old_sysent.sy_narg, old_sysent.sy_call, old_sysent .sy_auevent
- };
-
- static moduledata_t cu_printpid_syscall_mod = {
- .name = "sys/cu_printpid_syscall",
- .evhand = syscall_module_handler,
- .priv = &cu_printpid_syscall_syscall_mod
- };
- MODULE_DEPEND(cu_printpid_syscall, kernel, __FreeBSD_version, __FreeBSD_version, MODULE_KERNEL_MAXVER);
- static struct mod_metadata _mod_metadata_md_cu_printpid_syscall = {
- .md_version = MDT_STRUCT_VERSION,
- .md_type = MDT_MODULE,
- .md_data = &cu_printpid_syscall_mod,
- .md_cval = "cu_printpid_syscall"
- };
- DATA_SET(modmetadata_set, _mod_metadata_md_cu_printpid_syscall)
- SYSINIT(cu_printpid_syscallmodule, SI_SUB_SYSCALLS, SI_ORDER_MIDDLE, module_register_init, &cu_printpid_syscall_mod);
- struct __hack
复制代码 从宏SYSCALL_MODULE的展开结果来看,如下:
1:内核对实现系统调用的代码是以模块的方式管理。
2:定义了一个对该模块具体事件进行响应的回调函数。
3:定义并初始化了几个描述模块的数据结构。
4:将类型为struct mod_metadata数据对象的地址保存到name为"set_modmetadata_set"的section中。
5:通过SYSINIT机制来对模块进行相应的初始化。
当实现系统调用cu_printpid的文件被编译成成可共享目标文件cu_printpid.ko时,就可以使用外部工具kldload将其加载到内核,最终都是通过系统调用kldload
来处理的,上面第4步,第5步的工作是由系统调用kldload来处理的,这个系统调用较复杂,要完成符号解析等等很多工作,暂是先不用关心其内部实现。
第四步,会创建一个struct module对象来描述cu_printpid.ko,并调用module_register将该模块注册到内核,该函数的实现可以参考"freebsd9.2-创建elf文件格式对应的struct module对象"
中的描述;第五步,确定cu_printpid.ko中set_sysinit_set节的起始和中止地址,然后依次执行相应的初始化函数,这里只有一个初始化函数,即module_register_init函数,该函数可以参考
"freebsd9.2-对execve系统调用实现的支持-创建execsw数组"中的描述。
现在我们只需要了解函数syscall_module_handler就行了,因为在这里,我们感兴趣的是系统调用对应的struct sysent对象是怎样被insert到系统调用方法表即数组sysent中以及怎样从系统调用
调用方法表中delete的,这两个过程都由函数syscall_module_handler来处理.
[函数syscall_module_handler]:- /******************************************************************************************************
- * 参数描述:
- mod:即上面第四步创建的描述cu_printpid.ko的struct module对象,这里忽略。
-
- arg:这个参数比较重要,调用该函数时,传递都是上面定义的cu_printpid_syscall_syscall_mod对象。
- static struct syscall_module_data cu_printpid_syscall_syscall_mod = {
- .chainevh = cu_mod_event,
- .chainarg = NULL,
- .offset = &cu_index,
- .new_sysent = &cu_printpid_sysent,
- { 0, NULL, AUE_NULL } // old_sysent.sy_narg, old_sysent.sy_call, old_sysent .sy_auevent
- };
- 153-167:
- 加载模块时进行相应的处理。
-
- 154:
- 调用函数syscall_register完成系统调用的注册,该函数简要分析如下。
-
- 161-164:模块相关的处理,这里忽略。
- 165-166:
- 这里将调用我们自定义的函数cu_mod_event。
- 168-182:
- 卸载模块时进行相应的处理。
- 这里就不分析了,和syscall_register完成的是相反的工作,很容易理解。
- ***************************************************/
- 145 int
- 146 syscall_module_handler(struct module *mod, int what, void *arg)
- 147 {
- 148 struct syscall_module_data *data = arg;
- 149 modspecific_t ms;
- 150 int error;
- 151
- 152 switch (what) {
- 153 case MOD_LOAD:
- 154 error = syscall_register(data->offset, data->new_sysent,
- 155 &data->old_sysent);
- 156 if (error) {
- 157 /* Leave a mark so we know to safely unload below. */
- 158 data->offset = NULL;
- 159 return (error);
- 160 }
- 161 ms.intval = *data->offset;
- 162 MOD_XLOCK;
- 163 module_setspecific(mod, &ms);
- 164 MOD_XUNLOCK;
- 165 if (data->chainevh)
- 166 error = data->chainevh(mod, what, data->chainarg);
- 167 return (error);
- 168 case MOD_UNLOAD:
- 169 /*
- 170 * MOD_LOAD failed, so just return without calling the
- 171 * chained handler since we didn't pass along the MOD_LOAD
- 172 * event.
- 173 */
- 174 if (data->offset == NULL)
- 175 return (0);
- 176 if (data->chainevh) {
- 177 error = data->chainevh(mod, what, data->chainarg);
- 178 if (error)
- 179 return error;
- 180 }
- 181 error = syscall_deregister(data->offset, &data->old_sysent);
- 182 return (error);
- 183 default:
- 184 if (data->chainevh)
- 185 return (data->chainevh(mod, what, data->chainarg));
- 186 return (EOPNOTSUPP);
- 187 }
- 188
- 189 /* NOTREACHED */
- 190 }
复制代码 [函数syscall_register]:- /*********************************************************************************************************
- * 参数描述:
- offset:
- 指向的整形变量包含了所注册的系统调用将要使用的系统调用号,这里我们选择的是212.
-
- new_sysent:
- 描述所注册系统调用的struct sysent对象,这里我们要注册的系统调用为cu_printpid,其对应的struct sysent
- 对象如下:
- 60 struct sysent cu_printpid_sysent = {
- 61 .sy_narg = 0,
- 62 .sy_call = (sy_call_t *)sys_cu_printpid,
- 63 .sy_auevent = AUE_NULL,
- 64 .sy_systrace_args_func = NULL,
- 65 .sy_entry = 0,
- 66 .sy_return = 0,
- 67 .sy_flags = 0,
- 68 .sy_thrcnt = 0,
- 69 };
- old_sysent:
- 如果非空,将把之前数组元素sysent[212]的值保存到old_sysent指向的数据对象中,从我们的注册
- 过程来看,该参数非空。
- *****************************************/
- 106 int
- 107 syscall_register(int *offset, struct sysent *new_sysent,
- 108 struct sysent *old_sysent)
- 109 {
- 110 int i;
- 111
- /*********************************************************************************************
- * 112-123:
- 结合系统调用方法表即数组sysent看会更清楚。
- 112-119:系统调用号是NO_SYSCALL,此时由内核自动选择一个。
-
- 119-120:系统调用号无效时的处理。
- 121-123:系统调用号已经显示指定,检查对象sysent[*offset]的sy_call成员。如果该成员
- 既不是lkmnosys也不是lkmressys,那么将不能使用该系统调用号,因为此时该系统
- 调用号已经被使用。
- 从这里的处理来看,选择未被使用的系统调用号最好的办法就是结合系统调用方法表即数组sysent。
- *************************************.
- 112 if (*offset == NO_SYSCALL) {
- 113 for (i = 1; i < SYS_MAXSYSCALL; ++i)
- 114 if (sysent[i].sy_call == (sy_call_t *)lkmnosys)
- 115 break;
- 116 if (i == SYS_MAXSYSCALL)
- 117 return (ENFILE);
- 118 *offset = i;
- 119 } else if (*offset < 0 || *offset >= SYS_MAXSYSCALL)
- 120 return (EINVAL);
- 121 else if (sysent[*offset].sy_call != (sy_call_t *)lkmnosys &&
- 122 sysent[*offset].sy_call != (sy_call_t *)lkmressys)
- 123 return (EEXIST);
- 124
- 125 KASSERT(sysent[*offset].sy_thrcnt == SY_THR_ABSENT,
- 126 ("dynamic syscall is not protected"));
- /****************************************************************************************
- * 127:
- 此时先将数组元素sysent[212]的旧值保存到old_sysent指向的对象中,在实现系统调用的模块
- 被卸载时,恢复使用。
- 128-130:
- 执行完后,数组元素sysent[212]的值已经被成功更新,此时如果执行系统调用,系统调用号
- 为212时,将调用函数sys_cu_printpid。
- **************************************/
- 127 *old_sysent = sysent[*offset];
- 128 new_sysent->sy_thrcnt = SY_THR_ABSENT;
- 129 sysent[*offset] = *new_sysent;
- 130 atomic_store_rel_32(&sysent[*offset].sy_thrcnt, 0);
- 131 return (0);
- 132 }
复制代码 下来我们测试一下。
测试系统是freebsd9.2,需要安装源代码树,系统版本不一样的话,内核相关宏或函数的实现有可能不同,所以这里测试的系统调用cu_printpid有可能不能正常运行,大家测试如果出现问题的话,及时联系一下,
还有一个,就是注意一下权限。
创建一个测试使用的目录,这里为/71-kernel,将系统调用cu_printpid的源代码保存到文件cu_printpid.c中,该文件的文件名可以任意,只要保证和Makefile文件中一致就行,文件cu_printpid.c
和Makefile文件要在一个目录下,这里在/71-kernel下:- root@:/71-kernel # ls
- Makefile cu_printpid.c
复制代码 Makefile文件内容如下:- root@:/71-kernel # cat Makefile
- KMOD = cu_printpid
- SRCS = cu_printpid.c
- .include <bsd.kmod.mk>
复制代码 然后执行make,输出如下:- root@:/71-kernel # make
- Warning: Object directory not changed from original /71-kernel
- @ -> /usr/src/sys
- machine -> /usr/src/sys/i386/include
- x86 -> /usr/src/sys/x86/include
- cc -O2 -pipe -fno-strict-aliasing -Werror -D_KERNEL -DKLD_MODULE -nostdinc -I. -I@ -I@/contrib/altq -finline-limit=8000 --param inline-unit-growth=100 --param large-function-growth=1000 -fno-common -mno-align-long-strings -mpreferred-stack-boundary=2 -mno-mmx -mno-sse -msoft-float -ffreestanding -fstack-protector -std=iso9899:1999 -fstack-protector -Wall -Wredundant-decls -Wnested-externs -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Winline -Wcast-qual -Wundef -Wno-pointer-sign -fformat-extensions -Wmissing-include-dirs -fdiagnostics-show-option -c cu_printpid.c
- ld -d -warn-common -r -d -o cu_printpid.kld cu_printpid.o
- :> export_syms
- awk -f /sys/conf/kmod_syms.awk cu_printpid.kld export_syms | xargs -J% objcopy % cu_printpid.kld
- ld -Bshareable -d -warn-common -o cu_printpid.ko cu_printpid.kld
- objcopy --strip-debug cu_printpid.ko
复制代码 make完后,/71-kernel目录内容如下:- root@:/71-kernel # ls
- @ Makefile cu_printpid.c cu_printpid.kld cu_printpid.ko cu_printpid.o export_syms machine x86
复制代码 可以看到,已经成功创建了相应的模块cu_printpid.ko,加载前:- root@:/71-kernel # kldstat
- Id Refs Address Size Name
- 1 1 0xc0400000 1205064 kernel
复制代码 加载后:- root@:/71-kernel # kldload /71-kernel/cu_printpid.ko
- register syscall
- root@:/71-kernel # kldstat
- Id Refs Address Size Name
- 1 3 0xc0400000 1205064 kernel
- 2 1 0xcb65b000 2000 cu_printpid.ko
复制代码 此时我们编写一个测试用的程序,因为系统调用cu_printpid是我们自己实现并注册到内核的,所以就得我们发出一个"int 0x80"软中断.
测试程序很简单,使用内联汇编来完成,文件名为cu_syscall.c,这里的212就是系统调用cu_printpid的系统调用号,我们将其保存到EAX寄存器中:- root@:/71-kernel # cat cu_syscall.c
- int main(void)
- {
- __asm __volatile(
- "movl $212,%eax;"
- "int $0x80;"
- );
- }
复制代码 编译并运行,输出很多,省略了一些模块名,注意一下输出中最后一个模块名"sys/cu_printpid_syscall",和上面kldstat的输出不同,因为系统调用
kldstat访问的不是struct module对象:- root@:/71-kernel # gcc -o cu cu_syscall.c
- root@:/71-kernel # ./cu
- My name:cu, My pid:3126
- My name:more, My pid:2907
- My name:csh, My pid:2739
- My name:sshd, My pid:2737
- process2737's child: [name: csh], [pid: 2739]
- My name:csh, My pid:2335
- My name:sshd, My pid:2333
- process2333's child: [name: csh], [pid: 2335]
- My name:csh, My pid:2292
- My name:sshd, My pid:2290
- process2290's child: [name: csh], [pid: 2292]
- My name:csh, My pid:2278
- My name:sshd, My pid:2276
- process2276's child: [name: csh], [pid: 2278]
- My name:more, My pid:2095
- My name:cat, My pid:2094
- My name:csh, My pid:1500
- process1500's child: [name: cu], [pid: 3126]
- My name:sshd, My pid:1498
- process1498's child: [name: csh], [pid: 1500]
- My name:csh, My pid:1485
- process1485's child: [name: more], [pid: 2907]
- My name:sshd, My pid:1483
- process1483's child: [name: csh], [pid: 1485]
- My name:csh, My pid:1443
- My name:sshd, My pid:1441
- process1441's child: [name: csh], [pid: 1443]
- My name:csh, My pid:1439
- My name:sshd, My pid:1437
- process1437's child: [name: csh], [pid: 1439]
- My name:csh, My pid:1435
- process1435's child: [name: more], [pid: 2095]
- process1435's child: [name: cat], [pid: 2094]
- My name:sshd, My pid:1433
- process1433's child: [name: csh], [pid: 1435]
- My name:csh, My pid:1228
- My name:sshd, My pid:1226
- process1226's child: [name: csh], [pid: 1228]
- My name:csh, My pid:1157
- My name:sshd, My pid:1155
- process1155's child: [name: csh], [pid: 1157]
- My name:getty, My pid:1154
- My name:getty, My pid:1153
- My name:getty, My pid:1152
- My name:getty, My pid:1151
- My name:getty, My pid:1150
- My name:getty, My pid:1149
- My name:getty, My pid:1148
- My name:getty, My pid:1147
- My name:cron, My pid:1113
- My name:sendmail, My pid:1109
- My name:sendmail, My pid:1106
- My name:sshd, My pid:1103
- process1103's child: [name: sshd], [pid: 2737]
- process1103's child: [name: sshd], [pid: 2333]
- process1103's child: [name: sshd], [pid: 2290]
- process1103's child: [name: sshd], [pid: 2276]
- process1103's child: [name: sshd], [pid: 1498]
- process1103's child: [name: sshd], [pid: 1483]
- process1103's child: [name: sshd], [pid: 1441]
- process1103's child: [name: sshd], [pid: 1437]
- process1103's child: [name: sshd], [pid: 1433]
- process1103's child: [name: sshd], [pid: 1226]
- process1103's child: [name: sshd], [pid: 1155]
- My name:syslogd, My pid:1002
- My name:devd, My pid:883
- My name:moused, My pid:866
- My name:dhclient, My pid:849
- My name:dhclient, My pid:809
- My name:softdepflush, My pid:17
- My name:syncer, My pid:16
- My name:vnlru, My pid:15
- My name:bufdaemon, My pid:9
- My name:pagezero, My pid:8
- My name:vmdaemon, My pid:7
- My name:pagedaemon, My pid:6
- My name:xpt_thrd, My pid:5
- My name:sctp_iterator, My pid:4
- My name:fdc0, My pid:3
- My name:usb, My pid:14
- My name:mpt_recovery0, My pid:2
- My name:geom, My pid:13
- My name:intr, My pid:12
- My name:idle, My pid:11
- My name:init, My pid:1
- process1's child: [name: getty], [pid: 1154]
- process1's child: [name: getty], [pid: 1153]
- process1's child: [name: getty], [pid: 1152]
- process1's child: [name: getty], [pid: 1151]
- process1's child: [name: getty], [pid: 1150]
- process1's child: [name: getty], [pid: 1149]
- process1's child: [name: getty], [pid: 1148]
- process1's child: [name: getty], [pid: 1147]
- process1's child: [name: cron], [pid: 1113]
- process1's child: [name: sendmail], [pid: 1109]
- process1's child: [name: sendmail], [pid: 1106]
- process1's child: [name: sshd], [pid: 1103]
- process1's child: [name: syslogd], [pid: 1002]
- process1's child: [name: devd], [pid: 883]
- process1's child: [name: moused], [pid: 866]
- process1's child: [name: dhclient], [pid: 809]
- process1's child: [name: dhclient], [pid: 849]
- My name:audit, My pid:10
- My name:kernel, My pid:0
- process0's child: [name: softdepflush], [pid: 17]
- process0's child: [name: syncer], [pid: 16]
- process0's child: [name: vnlru], [pid: 15]
- process0's child: [name: bufdaemon], [pid: 9]
- process0's child: [name: pagezero], [pid: 8]
- process0's child: [name: vmdaemon], [pid: 7]
- process0's child: [name: pagedaemon], [pid: 6]
- process0's child: [name: xpt_thrd], [pid: 5]
- process0's child: [name: sctp_iterator], [pid: 4]
- process0's child: [name: fdc0], [pid: 3]
- process0's child: [name: usb], [pid: 14]
- process0's child: [name: mpt_recovery0], [pid: 2]
- process0's child: [name: geom], [pid: 13]
- process0's child: [name: intr], [pid: 12]
- process0's child: [name: idle], [pid: 11]
- process0's child: [name: init], [pid: 1]
- process0's child: [name: audit], [pid: 10]
- if_lo
- newreno
- elf32
- shell
- ..........................................................................
- ..........................................................................
- sys/cu_printpid_syscall
复制代码 为了方便大家测试,未加注释的系统调用cu_printpid代码如下:- #include <sys/ctype.h>
- #include <sys/param.h>
- #include <sys/kernel.h>
- #include <sys/module.h>
- #include <sys/proc.h>
- #include <sys/sysent.h>
- #include <sys/sysproto.h>
- #include <sys/systm.h>
- #include <sys/lock.h>
- #include <sys/sx.h>
- #include <sys/mutex.h>
- #include <sys/module.h>
- #include <sys/linker.h>
- static int cu_mod_event(struct module *cu_mod,int cu_what,void *cu_data)
- {
- switch (cu_what) {
- case MOD_LOAD:
- uprintf("register syscall\n");
- break;
- case MOD_UNLOAD:
- uprintf("unregister syscall\n");
- break;
- }
- return 0;
- }
- static int cu_predicate(linker_file_t cu_linker_file,void *cu_data)
- {
- module_t cu_mod;
- for (cu_mod = TAILQ_FIRST(&cu_linker_file->modules); cu_mod;cu_mod = module_getfnext(cu_mod)) {
- uprintf("%s\n",module_getname(cu_mod));
- }
- return 0;
- }
- int cu_index = 212;
- struct cu_printpid_args {
- int dummy;
- };
- static int sys_cu_printpid(struct thread *cu_td, struct cu_printpid_args *uap)
- {
-
- struct proc *cu_p;
-
-
- sx_slock(&allproc_lock);
- FOREACH_PROC_IN_SYSTEM(cu_p) {
- struct proc *cu_child;
- uprintf("My name:%s, My pid:%d\n",cu_p->p_comm,cu_p->p_pid);
- sx_slock(&proctree_lock);
- LIST_FOREACH(cu_child, &cu_p->p_children, p_sibling) {
- uprintf(" process%d's child: [name: %s], [pid: %d]\n",cu_p->p_pid,cu_child->p_comm,cu_child->p_pid);
- }
- sx_sunlock(&proctree_lock);
- }
- sx_sunlock(&allproc_lock);
- linker_file_foreach(cu_predicate,NULL);
- cu_td->td_retval[0] = 0;
- return 0;
- }
- struct sysent cu_printpid_sysent = {
- .sy_narg = 0,
- .sy_call = (sy_call_t *)sys_cu_printpid,
- .sy_auevent = AUE_NULL,
- .sy_systrace_args_func = NULL,
- .sy_entry = 0,
- .sy_return = 0,
- .sy_flags = 0,
- .sy_thrcnt = 0,
- };
- SYSCALL_MODULE(cu_printpid_syscall, &cu_index, &cu_printpid_sysent,cu_mod_event, NULL);
复制代码 |
|