现在我用的是虚拟机下ubuntu10.10,内核版本已经升级到2.6.38.2,要完成的任务有:
1,在内核的结构体task_struct中添加一个数据成员ctx,记录每个进程切换(被调度)的次数,并编写一个模块显示进程的信息,包括该数据成员的值;
2,在内核中增加一个系统调用,并编写用户态程序调用该系统调用;
首先,准备内核内核源码。
1,make mrproper;清除之前编译内核时的残存配置文件,和一些生成的映像,(据说可以不执行make mrproper和make clean来实现增量编译,但个人认为不可取,至于linux内核的增量编译目前个人并不懂,希望有人能讲解一下)。
2,为了任务1;在task_struct中添加一个数据成员,在include/linux/sched.h中找到task_struct所在位置,添加一个成员
在kernel/fork.c中找到do_fork函数,在其中舒适化该数据成员ctx,初始化的位置就是进程刚刚被建立的时候,而linux系统建立进程一般都是通过复制父进程的数据结构来完成的,所以在cope_process被执行后,添加
在?kernel/sched.c中找到schedule函数,当进程(switch)发生切换时,添加
,该数值加1。
3,为了任务2;在kernel/sys.c中(结尾)添加一个自定义的函数(系统调用),
在arch/x86/include/asm/unistd_32.h中添加一个系统调用号的定义
#define __NR_mycall 341
并把最后的那个__NR_syscall 341改为342,这相当于一个结束符,我们的系统调用号要加在该结束符前面;其最终代码如下:
在arch/x86/kernel/syscall_table_32.S
中添加
;
2,3两步可参考文后给出的patch文件内容。
4, 重新编译内核,请参考前一篇文章,但注意,无须编译内核模块,时间在30分钟左右,重启。
5,编写用户态程序
5.1)
任务1,编写一个模块,显示当前进程的状态信息:
5.2;编写模块的Makefile:
5.3; 加载模块sudo insmod pro_example.ko
查看当前已加载模块信息:lsmod
卸载模块sudo rmmod pro_example
当加载完模块后,就可以查看/proc目录下是否生成了相应的文件,并使用cat命令查看文件内容,还可以使用dmesg查看后台打印信息,比如printk的输出,如果要看的更清除一点,使用sudo dmesg -c清除之前的信息
任务2;编写一个程序,调用自定义的系统调用:
5.4,使用syscall函数:
运行程序,输出结果studentnum=100,查看dmesg,有success;则系统调用添加成功,调用该系统调用正确;
5.5;给出我这两次实验的patch文件,不过因为我编程需要,我在proc_dir_entry中添加了一个成员struct proc_dir_entry *owner;
,最后总结一下这两个实验:
1》模块编程,无须重新编译内核,一定要写Makefile,要有入口和出口函数;
2》系统调用,3个步骤正确,且必须重新编译内核
************对于,很多人在使用/proc/plist来列出当前进程信息的时候,可能出现显示列表不全的问题,可使用下面的程序(主要使用了seq_file接口)来解决该问题:
1,在内核的结构体task_struct中添加一个数据成员ctx,记录每个进程切换(被调度)的次数,并编写一个模块显示进程的信息,包括该数据成员的值;
2,在内核中增加一个系统调用,并编写用户态程序调用该系统调用;
首先,准备内核内核源码。
1,make mrproper;清除之前编译内核时的残存配置文件,和一些生成的映像,(据说可以不执行make mrproper和make clean来实现增量编译,但个人认为不可取,至于linux内核的增量编译目前个人并不懂,希望有人能讲解一下)。
2,为了任务1;在task_struct中添加一个数据成员,在include/linux/sched.h中找到task_struct所在位置,添加一个成员
unsigned int ctx;
在kernel/fork.c中找到do_fork函数,在其中舒适化该数据成员ctx,初始化的位置就是进程刚刚被建立的时候,而linux系统建立进程一般都是通过复制父进程的数据结构来完成的,所以在cope_process被执行后,添加
p->ctx=0;
在?kernel/sched.c中找到schedule函数,当进程(switch)发生切换时,添加
next->ctx++;
,该数值加1。
3,为了任务2;在kernel/sys.c中(结尾)添加一个自定义的函数(系统调用),
asmlinkage int sys_mycall(int num)
{
printk(KERN_INFO "success!\n");
return num;
}
在arch/x86/include/asm/unistd_32.h中添加一个系统调用号的定义
#define __NR_mycall 341
并把最后的那个__NR_syscall 341改为342,这相当于一个结束符,我们的系统调用号要加在该结束符前面;其最终代码如下:
#define __NR_fanotify_mark 339
#define __NR_prlimit64 340
#define __NR_mycall 341
#ifdef __KERNEL__
#define NR_syscalls 342
在arch/x86/kernel/syscall_table_32.S
中添加
.long sys_mycall
;
2,3两步可参考文后给出的patch文件内容。
4, 重新编译内核,请参考前一篇文章,但注意,无须编译内核模块,时间在30分钟左右,重启。
5,编写用户态程序
5.1)
任务1,编写一个模块,显示当前进程的状态信息:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#define MODULE_VERSION "1.0"
#define MODULE_NAME "proc_interface"
#define FOOBAR_LEN 8
struct fb_data_t {
char name[FOOBAR_LEN+1];
char value[FOOBAR_LEN+1];
};
static struct proc_dir_entry *example_dir,*foo_file, *bar_file,*jiffies_file,*symlink;
struct fb_data_t foo_data,bar_data;
static int proc_read_jiffies(char *page,char **start, off_t off,int count, int *eof,void *data)
{
int len;
len=sprintf(page,"jiffies=%ld\n", jiffies);
return len;
}
static int proc_read_foobar(char *page,char **start, off_t off,int count, int *eof,void *data)
{
int len;
struct fb_data_t *fb_data=(struct fb_data_t *)data;
len=sprintf(page,"%s='%s'\n", fb_data->name,fb_data->value);
return len;
}
static int proc_write_foobar(struct file *file, const char *buffer, unsigned long count, void *data)
{
int len;
struct fb_data_t *fb_data=(struct fb_data_t *)data;
if(count>FOOBAR_LEN)
len=FOOBAR_LEN;
else
len=count;
if(copy_from_user(fb_data->value,buffer,len)){
return -EFAULT;
}
fb_data->value[len]='\0';
return len;
}
static int __init hello_init(void)
{
int rv=0;
example_dir=proc_mkdir(MODULE_NAME,NULL);
if(example_dir==NULL){
rv=-ENOMEM;
goto out;
}
example_dir->owner=THIS_MODULE;
jiffies_file=create_proc_read_entry("jiffies", 0444,example_dir, proc_read_jiffies, NULL);
if(jiffies_file==NULL){
rv=-ENOMEM;
goto no_jiffies;
}
jiffies_file->owner=THIS_MODULE;
foo_file=create_proc_entry("foo",0644,example_dir);
if(foo_file==NULL){
rv=-ENOMEM;
goto no_foo;
}
strcpy(foo_data.name,"foo");
strcpy(foo_data.value,"foo");
foo_file->data=&foo_data;
foo_file->read_proc=proc_read_foobar;
foo_file->write_proc=proc_write_foobar;
foo_file->owner=THIS_MODULE;
bar_file=create_proc_entry("bar",0644,example_dir);
if(bar_file==NULL){
rv=-ENOMEM;
goto no_bar;
}
strcpy(bar_data.name,"bar");
strcpy(bar_data.value,"bar");
bar_file->data=&bar_data;
bar_file->read_proc=proc_read_foobar;
bar_file->write_proc=proc_write_foobar;
bar_file->owner=THIS_MODULE;
symlink=proc_symlink("jiffies_too",example_dir, "jiffies");
if(symlink==NULL){
rv=-ENOMEM;
goto no_symlink;
}
symlink->owner=THIS_MODULE;
printk(KERN_INFO "%s %s initialised\n",MODULE_NAME,MODULE_VERSION);
return 0;
no_symlink:
remove_proc_entry("bar",example_dir);
no_bar:
remove_proc_entry("foo",example_dir);
no_foo:
remove_proc_entry("jiffies",example_dir);
no_jiffies:
remove_proc_entry(MODULE_NAME,NULL);
out:
return rv;
}
static void __exit hello_exit(void)
{
remove_proc_entry("jiffies_too",example_dir);
remove_proc_entry("bar",example_dir);
remove_proc_entry("foo",example_dir);
remove_proc_entry("jiffies",example_dir);
remove_proc_entry(MODULE_NAME,NULL);
printk(KERN_INFO "%s %s removed\n",MODULE_NAME,MODULE_VERSION);
}
MODULE_DESCRIPTION("proc example");
MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);
5.2;编写模块的Makefile:
obj-m+=pro_example.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean
5.3; 加载模块sudo insmod pro_example.ko
查看当前已加载模块信息:lsmod
卸载模块sudo rmmod pro_example
当加载完模块后,就可以查看/proc目录下是否生成了相应的文件,并使用cat命令查看文件内容,还可以使用dmesg查看后台打印信息,比如printk的输出,如果要看的更清除一点,使用sudo dmesg -c清除之前的信息
任务2;编写一个程序,调用自定义的系统调用:
5.4,使用syscall函数:
#include <linux/unistd.h>
#include <stdio.h>
int main()
{
printf("studentnum=%d\n", syscall(341,100));
return 0;
}
运行程序,输出结果studentnum=100,查看dmesg,有success;则系统调用添加成功,调用该系统调用正确;
5.5;给出我这两次实验的patch文件,不过因为我编程需要,我在proc_dir_entry中添加了一个成员struct proc_dir_entry *owner;
diff -ruNa linux-2.6.38.2.org/arch/x86/include/asm/unistd_32.h linux-2.6.38.2/arch/x86/include/asm/unistd_32.h
--- linux-2.6.38.2.org/arch/x86/include/asm/unistd_32.h 2011-04-12 08:25:50.384313140 -0700
+++ linux-2.6.38.2/arch/x86/include/asm/unistd_32.h 2011-04-12 08:28:59.612312392 -0700
@@ -347,9 +347,11 @@
#define __NR_fanotify_mark 339
#define __NR_prlimit64 340
+#define __NR_mycall 341
+
#ifdef __KERNEL__
-#define NR_syscalls 341
+#define NR_syscalls 342
#define __ARCH_WANT_IPC_PARSE_VERSION
#define __ARCH_WANT_OLD_READDIR
diff -ruNa linux-2.6.38.2.org/arch/x86/kernel/syscall_table_32.S linux-2.6.38.2/arch/x86/kernel/syscall_table_32.S
--- linux-2.6.38.2.org/arch/x86/kernel/syscall_table_32.S 2011-04-12 08:25:50.580313137 -0700
+++ linux-2.6.38.2/arch/x86/kernel/syscall_table_32.S 2011-04-12 08:29:22.992312038 -0700
@@ -340,3 +340,4 @@
.long sys_fanotify_init
.long sys_fanotify_mark
.long sys_prlimit64 /* 340 */
+ .long sys_mycall
diff -ruNa linux-2.6.38.2.org/include/linux/proc_fs.h linux-2.6.38.2/include/linux/proc_fs.h
--- linux-2.6.38.2.org/include/linux/proc_fs.h 2011-04-12 08:25:47.060313136 -0700
+++ linux-2.6.38.2/include/linux/proc_fs.h 2011-04-12 08:38:31.888315451 -0700
@@ -49,6 +49,7 @@
unsigned long count, void *data);
struct proc_dir_entry {
+ struct proc_dir_entry *owner;
unsigned int low_ino;
unsigned short namelen;
const char *name;
diff -ruNa linux-2.6.38.2.org/include/linux/sched.h linux-2.6.38.2/include/linux/sched.h
--- linux-2.6.38.2.org/include/linux/sched.h 2011-04-12 08:25:47.680313137 -0700
+++ linux-2.6.38.2/include/linux/sched.h 2011-04-12 08:34:24.872313536 -0700
@@ -1191,6 +1191,7 @@
};
struct task_struct {
+ unsigned int ctx;
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
void *stack;
atomic_t usage;
diff -ruNa linux-2.6.38.2.org/kernel/fork.c linux-2.6.38.2/kernel/fork.c
--- linux-2.6.38.2.org/kernel/fork.c 2011-04-12 08:25:47.780313137 -0700
+++ linux-2.6.38.2/kernel/fork.c 2011-04-12 08:35:05.188316355 -0700
@@ -1441,6 +1441,8 @@
nr = task_pid_vnr(p);
+ p->ctx=0;
+
if (clone_flags & CLONE_PARENT_SETTID)
put_user(nr, parent_tidptr);
diff -ruNa linux-2.6.38.2.org/kernel/sched.c linux-2.6.38.2/kernel/sched.c
--- linux-2.6.38.2.org/kernel/sched.c 2011-04-12 08:25:47.756313137 -0700
+++ linux-2.6.38.2/kernel/sched.c 2011-04-12 08:33:38.564317495 -0700
@@ -3996,6 +3996,8 @@
rq->curr = next;
++*switch_count;
+ next->ctx++;
+
context_switch(rq, prev, next); /* unlocks the rq */
/*
* The context switch have flipped the stack from under us
diff -ruNa linux-2.6.38.2.org/kernel/sys.c linux-2.6.38.2/kernel/sys.c
--- linux-2.6.38.2.org/kernel/sys.c 2011-04-12 08:25:47.744313137 -0700
+++ linux-2.6.38.2/kernel/sys.c 2011-04-12 08:28:19.656312264 -0700
@@ -1762,6 +1762,13 @@
argv_free(info->argv);
}
+
+asmlinkage int sys_mycall(int num)
+{
+ printk(KERN_INFO "success!");
+ return num;
+}
+
/**
* orderly_poweroff - Trigger an orderly system poweroff
* @force: force poweroff if command execution fails
,最后总结一下这两个实验:
1》模块编程,无须重新编译内核,一定要写Makefile,要有入口和出口函数;
2》系统调用,3个步骤正确,且必须重新编译内核
************对于,很多人在使用/proc/plist来列出当前进程信息的时候,可能出现显示列表不全的问题,可使用下面的程序(主要使用了seq_file接口)来解决该问题:
#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("vampire");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("a test module utilise the seq_file mechanism");
static void *ps_seq_start(struct seq_file *s,loff_t *pos){
struct task_struct *task;
seq_printf(s,"%s\t%s\t%s\t%s\t%s\t%s\n","pid","ppid","uid","gid","comm","ctx");
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%d\t%d\t%s\t%d\n",task->pid,task->parent->pid,task->uid,task->gid,task->comm,task->ctx);
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);