转 32位linux内核2.6.38.2添加系统调用,编写类似"ps"命令模块显示进程信息

 

现在我用的是虚拟机下ubuntu10.10,内核版本已经升级到2.6.38.2,要完成的任务有: 

TASK 1:在内核的结构体task_struct中添加一个数据成员ctx,记录每个进程切换(被调度)的次数,并编写一个模块显示进程的信息,包括该数据成员的值;

TASK 2:在内核中增加一个系统调用,并编写用户态程序调用该系统调用; 

首先修改内核源码(最好是之前make的源码)

FIRST:基于TASK 1的源码修改

1. 在task_struct中添加一个数据成员,在源码文件include/linux/sched.h中找到task_struct所在位置,添加一个成员ctx记录每个进程切换(被调度)的次数。如下图:

2.在源码文件kernel/fork.c中找到do_fork函数,在其中初始化该数据成员ctx,初始化的位置是进程刚被建立的地方,而linux系统建立进程一般都是通过复制父进程的数据结构来完成的,所以在cope_process被执行后,添加初始化,代码添加如下图:

3. 在源码文件kernel/sched.c中找到schedule函数,当进程(switch)发生切换时,即被调度时需要对ctx加一,代码添加如下图:

SECOND:基于TASK 2的源码修改

1. 在源码文件kernel/sys.c文件代码的末尾处加入自定义的系统调用函数sys_mysyscall ,代码如下图:

2. 在源码文件arch/x86/include/asm/unistd_32.h中添加一个系统调用号的定义 “#define __NR_mysyscall  341”,并把最后的那个“__NR_syscall 341”改为“__NR_syscall 342“,这相当于一个结束符,标志系统调用的总数,我们的系统调用号要加在该结束符前面,其最终代码如下图:

3. 在源码文件arch/x86/kernel/syscall_table_32.S中为自己的系统调用添加指针引用,这对数据的录入和你分配给自己系统调用的索引保持一致很重要,其代码添加在最后一行:

.long sys_mysyscall

以上的修改可参考之后的patch文件;

THIRD:编译内核

1. make mrproper:清除之前编译内核时的残存配置文件,和一些生成的映像。

2. 重新编译内核,请参考前一篇文章《跟党哥学linux内核系列之(ubuntu10.10下编译linux内核,升级内核到2.6.38.2)》,但注意,无须编译modules,只需执行命令make bzImage -j4,时间在30分钟左右,然后reboot就ok了。

FORTH:c文件代码

1. TASK 1中类似于“ps”命令的模块proc.c

1.1 相关数据结构:

struct file结构,该结构是字符设备驱动的重要结构,文件结构代表一个打开的文件描述符,它不是专门给驱动程序使用的,系 统中每一个打开的文件在内核中都有一个关联的struct file。它由内核在open时创建,并传递给在文件上操作的任何函数,知道最后关闭。当文件的所有实例都关闭之后,内核释放这个数据结构。

struct seq_file结构,该结构会在seq_open函数调用中分配,然后作为参数传递给每个seq_file的操作函数。Private变量可以用来在各个操作函数之间传递参数。

struct inode结构,每个存储设备或存储设备的分区被格式化为文件系统后,应该有两部份,一部份是inode,另一部份是Block,Block是用来存储数据用的。而inode就是用来存储这些数据的信息,这些信息包括文件大小、属主、归属的用户组、读写权限等。inode为每个文件进行信息索引,所以就有了inode的数值。操作系统根据指令, 能通过inode值最快的找到相对应的文件。当我们用ls查看某个目录或文件时,如果加上-i参数,就可以看到inode节点了,比如ls -li lsfile.sh,最前面的数值就是inode信息。

1.2 proc.c代码如下:

/** 
*@author DangYan 
*It's a test module like 'ps' 
**/


#include <linux/init.h> 
#include <linux/module.h> 
#include <linux/proc_fs.h> //proc_fs 
#include <linux/seq_file.h> //seq_file 
#include <linux/fs.h> //struct file,struct inode 
#include <linux/sched.h>    //next_task() 

MODULE_AUTHOR("DangYan_1001220893"); 
MODULE_LICENSE("GPL"); 
MODULE_DESCRIPTION("a test module like 'ps'"); 

static void *ps_seq_start(struct seq_file *s,loff_t *pos){ 
struct task_struct *task; 

seq_printf(s,"%s %s %s ","pid","ctx","comm"); //读取格式 

if(*pos>0) 
return NULL; 
else{ 
task=next_task(current); 
return task; 



static void *ps_seq_next(struct seq_file *s,void *v,loff_t *pos){ 
struct task_struct *task=(struct task_struct *)v; 
++*pos; 
if(task->pid== current->pid){ 
return NULL; 
}else{ 
task=next_task(task); 
return task; 



static void ps_seq_stop(struct seq_file *s,void *v){} 

static int ps_seq_show(struct seq_file *s,void *v){ 
rwlock_t lock = RW_LOCK_UNLOCKED; 
struct task_struct *task=(struct task_struct *)v; 
read_lock(&lock); 

seq_printf(s,"%d %d %s ",task->pid,task->ctx,task->comm); 

read_unlock(&lock); 
return 0; 


static struct seq_operations ps_seq_ops = { 
.start = ps_seq_start, 
.next = ps_seq_next, 
.stop = ps_seq_stop, 
.show = ps_seq_show 
}; 

static int ps_open(struct inode *inode,struct file *file){ 
return seq_open(file,&ps_seq_ops); 


static struct file_operations ps_file_ops = { 
.owner = THIS_MODULE, 
.open = ps_open, 
.read  =  seq_read, 
.llseek = seq_lseek, 
.release= seq_release 
}; 



static int __init ps_init(void){ 
struct proc_dir_entry *entry; 
entry = create_proc_entry("plist",0,NULL); 
if(entry) 
entry->proc_fops = &ps_file_ops; 
return 0; 


static void __exit ps_exit(void){ 
remove_proc_entry("plist",NULL); 


module_init(ps_init); 
module_exit(ps_exit);

1.3 makefile文件,代码如下:

obj-m += proc.o 
default: 
    $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 
clean: 
    $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

1.4 加载模块proc.ko

首先在proc.c的根目录下执行make命令进行编译

然后执行sudo insmod proc.ko来加载module

查看当前已加载模块信息:lsmod

卸载模块sudo rmmod pro_example

当加载完模块后,就可以查看/proc目录下是否生成了相应的文件,并使用cat命令查看文件内容,还可以使用dmesg查看后台打印信息,比如printk的输出,如果要看的更清除一点,使用sudo dmesg -c清除之前的信息

2. TASK 2使用syscall函数编写用户态程序

代码如下:

#include <linux/unistd.h>  
#include <stdio.h>  

int main()  
{  
   printf("My syscall number =%d ", syscall(341,1));  
   return 0;  
}

运行程序,输出结果为1,查看dmesg,有success;则系统调用添加成功,调用该系统调用正确。

FIFTH:前后内核文件的patch

diff -ruNa linux-2.6.38.2/arch/x86/include/asm/unistd_32.h linux-2.6.38.2org/arch/x86/include/asm/unistd_32.h 
--- linux-2.6.38.2/arch/x86/include/asm/unistd_32.h    2011-04-14 17:53:41.849076001 +0800 
+++ linux-2.6.38.2org/arch/x86/include/asm/unistd_32.h    2011-03-28 02:37:20.000000000 +0800 
@@ -346,11 +346,10 @@ 
 #define __NR_fanotify_init    338 
 #define __NR_fanotify_mark    339 
 #define __NR_prlimit64        340 
-#define __NR_mysyscall          341   /* dangyan */ 
 
 #ifdef __KERNEL__ 
 
-#define NR_syscalls 342 
+#define NR_syscalls 341 
 
 #define __ARCH_WANT_IPC_PARSE_VERSION 
 #define __ARCH_WANT_OLD_READDIR 
diff -ruNa linux-2.6.38.2/arch/x86/kernel/syscall_table_32.S linux-2.6.38.2org/arch/x86/kernel/syscall_table_32.S 
--- linux-2.6.38.2/arch/x86/kernel/syscall_table_32.S    2011-04-14 17:53:40.689076002 +0800 
+++ linux-2.6.38.2org/arch/x86/kernel/syscall_table_32.S    2011-03-28 02:37:20.000000000 +0800 
@@ -340,4 +340,3 @@ 
     .long sys_fanotify_init 
     .long sys_fanotify_mark 
     .long sys_prlimit64        /* 340 */ 
-        .long sys_mysyscall             /* 341 dangyan */ 
diff -ruNa linux-2.6.38.2/include/linux/sched.h linux-2.6.38.2org/include/linux/sched.h 
--- linux-2.6.38.2/include/linux/sched.h    2011-04-14 17:54:19.457076000 +0800 
+++ linux-2.6.38.2org/include/linux/sched.h    2011-03-28 02:37:20.000000000 +0800 
@@ -1191,7 +1191,6 @@ 
 }; 
 
 struct task_struct { 
-        unsigned int ctx;       /* dangyan */ 
     volatile long state;    /* -1 unrunnable, 0 runnable, >0 stopped */ 
     void *stack; 
     atomic_t usage; 
diff -ruNa linux-2.6.38.2/kernel/fork.c linux-2.6.38.2org/kernel/fork.c 
--- linux-2.6.38.2/kernel/fork.c    2011-04-14 17:52:17.245075996 +0800 
+++ linux-2.6.38.2org/kernel/fork.c    2011-03-28 02:37:20.000000000 +0800 
@@ -1437,8 +1437,6 @@ 
     if (!IS_ERR(p)) { 
         struct completion vfork; 
 
-                p->ctx = 0;          /* dangyan */ 
- 
         trace_sched_process_fork(current, p); 
 
         nr = task_pid_vnr(p); 
diff -ruNa linux-2.6.38.2/kernel/sched.c linux-2.6.38.2org/kernel/sched.c 
--- linux-2.6.38.2/kernel/sched.c    2011-04-14 17:52:18.061076002 +0800 
+++ linux-2.6.38.2org/kernel/sched.c    2011-03-28 02:37:20.000000000 +0800 
@@ -3995,7 +3995,6 @@ 
         rq->nr_switches++; 
         rq->curr = next; 
         ++*switch_count; 
-                next->ctx++;               /* dangyan */ 
 
         context_switch(rq, prev, next); /* unlocks the rq */ 
         /* 
diff -ruNa linux-2.6.38.2/kernel/sys.c linux-2.6.38.2org/kernel/sys.c 
--- linux-2.6.38.2/kernel/sys.c    2011-04-14 17:52:18.221075999 +0800 
+++ linux-2.6.38.2org/kernel/sys.c    2011-03-28 02:37:20.000000000 +0800 
@@ -1763,19 +1763,6 @@ 
 } 
 
 /** 
-*@author:dangyan 
-*sys_mysyscall 
-*This is an testing system call 
-*/ 
- asmlinkage int sys_mysyscall(int mysyscall_num) 
-{ 
-        printk(KERN_INFO "success! "); 
- 
-        return mysyscall_num; 
-   
-} 
- 
-/** 
  * orderly_poweroff - Trigger an orderly system poweroff 
  * @force: force poweroff if command execution fails 

原文地址:https://www.cnblogs.com/i0ject/p/3658026.html