转 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\t%s\t%s\n","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\t%d\t%s\n",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\n", 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!\n");
-
- return mysyscall_num;
-
-}
-
-/**
* orderly_poweroff - Trigger an orderly system poweroff
* @force: force poweroff if command execution fails