操作系统实验二 内核编译

操作系统实验二 内核编译

实验1:Linux内核代码分析

1. 解压内核

桌面上的linux-2.6.21.tar.gz是linux-2.6.21的内核代码压缩包,解压:

$ cd Desktop
$ tar zxvf linux-2.6.21.tar.gz
$ cd linux-2.6.21
2. 生成内核配置文件

将当前正在运行的内核对应的配置文件作为模板来生成.config文件,即将/boot目录下的已有的config文件复制到linux-2.6.21目录下

$ make mrproper
$ cp /boot/config-`uname -r` ./config	

第一个命令make mrproper用来保证内核树是干净的,如果内核第一次编译则可以省略。其中的uname –r命令可查看当前环境下的内核版本号。
更新config文件:

$ make oldconfig

部分新配置项会提示用户选择,都选N或者缺省即可,完成后即可生成.config文件。

3. 编译安装内核

在编译内核前,可以定义自己的内核版本号,在内核代码的根目录下有Makefile文件,例如将第4行改为:
EXTRAVERSION = -seu
这样新内核版本号就是2.6.21-seu

$ make all
$ su
# make modules_install
# make install
# make headers_install

Make all的执行过程可能比较长。
如果三个命令均成功执行,可以观察引导程序grub的配置文件/boot/grub/menu.lst的内容,在hiddenmenu之后可以看到刚刚编译安装的内核版本,将hiddenmenu那一行注释或删除,方便直接操作菜单:
#hiddenmenu或者hiddenmenu
然后重启系统:

# reboot

重启后可以看到grub菜单已经包含了新编译的内核。如果新内核启动失败,一般是由于配置或者内核代码修改的有问题,选择原先的内核启动,再进行修改、编译。

请添加图片描述

实验2:新增系统调用

1.在文件arch/i386/kernel/syscall_table.S的尾部加上要新增的系统调用函数的名称,如下图中添加了psta系统调用,注释中的320表示它的系统调用号
在这里插入图片描述

2.在include/linux目录下添加头文件psta.h:

 #ifndef _LINUX_PSTA_H
#define _LINUX_PSTA_H

//#include <linux/types.h>

struct pinfo {
	int nice;
	pid_t pid;
	uid_t uid;
};
#endif

修改include/linux目录下的Kbuild文件,把psta.h文件加进去。

header-y += x25.h
header-y += psta.h

unifdef-y += acct.h

在kernel目录下新建文件psta.c,在该文件中实现sys_psta函数:

 #include <linux/linkage.h>
#include <linux/types.h>
#include <linux/psta.h>
#include <linux/kernel.h>

asmlinkage int sys_psta(struct pinfo *buf) {
	printk("Hello world!\n");
	return 0;
}

宏asmlinkage定义在linux/linkage.h中,表示函数的参数通过栈传递,而不是寄存器,所有的系统调用都遵循这种参数传递方式。在上面的函数返回之前,可以插入打印一句话到内核日志,方便后面验证执行结果。
printk("Hello world\n");
下面头文件,实现了printk。
#include <linux/kernel.h>
3. 修改文件kernel/Makefile,使得psta.c在编译时可见:
在这里插入图片描述

另外,第2步中psta的实现不一定要在一个新文件中,例如文件kernel/sys.c也许是添加这个系统调用的合适位置,这样的话第3步就不需要了。
4. 在include/asm-i386/unistd.h里加上系统调用号的宏定义:在这里插入图片描述
其中NR_syscalls表示的值应该是最大的系统调用号加一。
5. 修改include/linux/syscalls.h,加上函数sys_psta的声明。
先在头上加入结构体申明struct pinfo; 在该文件的头文件引用列表的末尾添加:

在这里插入图片描述
这算是pinfo结构体的实现。在函数申明列表的最后添加:
在这里插入图片描述
6. 重新编译内核
在清理上次编译生成的中间文件之前,最好将配置好的.config文件备份至别的目录下以防删除,否则必须再执行实验1中的第2步配置.config文件。

$ make mrproper

在实验1中就已提到该命令,用于清理编译内核代码的中间文件,它也会删除.config文件。在清理执行完成后,将备份的.config文件复制回来。然后执行实验1中的第3步就可以编译安装新内核了。在这个过程里面可以换一个内核版本名称,以示区别,比如EXTRAVERSION = -seu2。安装好的内核必须reboot后才能生效。

以上6步成功执行后系统调用psta就已被添加到系统中,下面来进行测试。
首先,可以用uname -r命令测试目前所使用的内核版本号。
然后,随便写一个test.c文件,在里面使用glic库提供的函数syscall来间接地使用新系统调用,调用syscall必须添加以下两个头文件,以及头文件#include <linux/psta.h>。
在这里插入图片描述
函数的原型如下图,syscall的第一个参数是系统调用号,后面的参数是该系统调用的各个参数,返回值就是系统调用的返回值。例如函数psta的系统调用号是320,且接受一个类型为struct pinfo *的参数。
在这里插入图片描述
具体的测试代码请大家根据自己实现的psta来编写,只要体现出调用了psta函数即可。
编译的时候可以用命令 gcc -o test test.c -I/home/seu/Desktop/linux-2.6.21/usr/include。
关于为什么这里编译用户态程序的时候还需要用 –I 参数强行指定内核头文件路径,请查考实验末尾的文章。如果有需要的话,可以自己修改kernel headers文件的存放位置。
最后,系统调用执行成功,用dmesg命令可以再内核消息列表的最后看到hello world。
在这里插入图片描述

实验3:Linux进程管理及其扩展

1.实现系统调用hide
1.1. 在include/linux/sched.h中修改task_struct,添加一个成员cloak,用来记录进程隐藏与否。 在这里插入图片描述
1.2. 在进程创建时,将task_struct的成员cloak初始化为未隐藏。fork系统调用的实现代码在kernel/fork.c中,具体实现的主要函数为do_fork,do_fork中调用copy_process函数创建子进程,建议将初始化cloak的代码添加在copy_process函数中:
在这里插入图片描述
1.3. 添加hide系统调用

#include <linux/types.h>
#include <linux/sched.h>
#include <linux/pid.h>
#include <linux/proc_fs.h>

asmlinkage int sys_hide(pid_t pid, int on)
{
	struct task_struct *p = NULL;
	if(pid>0 &&  (current->uid)==0)
	{
		p = find_task_by_pid(pid);
		p->cloak=on;
		if(1==on){
			printk("Process %d is hidden by root.\n", pid);
		}
		if(0==on){
			printk("Process %d is displayed by root.\n", pid);
		}
		proc_flush_task(p);
	}
	else
		printk("Permission denied.\n");
	return 0;
}

1.4. 修改proc_pid_readdir函数(在fs/proc/base.c文件中)
其中使用for循环遍历进程,在遍历过程中添加判断,过滤掉被隐藏的进程:
在这里插入图片描述
1.5. 修改proc_pid_lookup函数
在进程查找完成前过滤掉被隐藏的进程。在这里插入图片描述


效果展示

在这里插入图片描述
隐藏进程号4000后发现ps找不到进程号4000的进程
在这里插入图片描述


2.考虑权限问题,只有根用户才能隐藏进程。

第一次由seu用户执行,操作被拒绝
第二次由root用户执行,隐藏成功
在这里插入图片描述

3.实现系统调用hide_user_processes

#include<linux/linkage.h>
#include<linux/types.h>
#include<linux/sched.h>
#include<linux/pid.h>
#include<linux/proc_fs.h>
#include<linux/string.h>
 
asmlinkage int sys_hide_user_processes(uid_t uid,char *binname,int recover){
    struct task_struct *p=NULL;
    if(recover==0)
    {
        if(current->uid==0)//1.Paragram recover=0;2.root => you can hide the process
        {
            if(binname==NULL)//when null, hide all the processes of the corresponding user
            {
                for_each_process(p)
                {
                    if((p->uid)==uid)
                    {
                        p->cloak=1;
                        proc_flush_task(p);
                    }
                }
                printk("All of the processes of uid %d are hidden.\n",uid);
            }
            else//otherwise, hide the process of the corresponding name
            {
                for_each_process(p)
                {
                    char *s=p->comm;
                    if(strcmp(s,binname)==0 && p->uid==uid)
                    {
                        p->cloak=1;
                        printk("Process %s of uid %d is hidden.\n",binname,uid);
                        proc_flush_task(p);
                    }
                }
            }
        }
        else
            printk("Sorry, you are not root user. Permission denied.\n");
    }
    else if(recover != 0 && (current->uid)==0)//display all of the processes, including which are hidden
    {
        for_each_process(p)
        {
            p->cloak=0;
        }
    }
     
    return 0;
}

效果展示

在这里插入图片描述
运行后成功将所有由uid=500的用户创建的进程隐藏
在这里插入图片描述

4.hidden文件

4.1. 在/fs/proc/proc_misc.c中添加回调函数
dd4.2. 在/fs/proc/proc_misc.c中proc_misc_init函数的最后添加创建hidden文件的代码,并指定其回调函数。
在这里插入图片描述在这里插入图片描述
4.3. hidden文件创建成功后,需要实现通过全局变量hidden_flag来约束隐藏进程的函数。
根据题目:当hidden_flag为1时,此前通过hide调用要求隐藏的进程才被隐藏。
这就意味着修改hidden_flag不可以修改cloak,即之前使用hide和hide_user_process隐藏的进程需要在hidden_flag置为1之后保持隐藏。
所以,在proc_pid_readdir函数和proc_pid_lookup函数中判断cloak值之前添加对hidden_flag的判断就可以实现约束。
1


效果展示

重新编译后hidden默认为1(开启隐藏功能)
在这里插入图片描述
将hidden改为0,此时之前被隐藏的进程都可以重新看到了
在这里插入图片描述


  1. hidden_process文件
    方法和创建hidden文件一样,hidden_process文件只需要设置读的回调函数即可。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述


效果展示

在这里插入图片描述

  • 3
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

winnower-sliff

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值