一、编译内核5.0

首先,创建LinuxKernel文件,下载linux-5.0内核源码,并解压到LinuxKernel文件下。

然后在此文件目录下通过

make menuconfig,配置编译信息。我最开始使用的内核源码是linux-5.0.2,这一步出现了很多问题,上图显示缺少ncurses包,于是通过

sudo apt-get install libncurses5-dev,解决后,继续执行

make menuconfig。

执行

sudo apt-get install flex

执行

sudo apt-get install bison

按下图一个一个配置权限

终于能配置编译信息了。依次选择

kernel hacking->Compile-time checks and compiler options->选择 [*]compile the kernel with debug info。

但是又

make,出错。

参照http://www.178linux.com/98387

要解决这个问题,你需要安装OpenSSL 开发包,

sudo apt-get install libssl–dev,安装完后,尝试重新编译程序。

还是不能make,于是换成了linux5.0.1,直接解压后,

make menuconfig,然后

make,出现下面的问题,按提示安装libelf-dev包后,make成功。

接下来要制作根文件系统。

cd ..

mkdir rootfs

git clone https://github.com/mengning/menu.git

cd menu

gcc -pthread -o init linktable.c menu.c test.c -m32 -static

出现问题

sudo apt install gcc-4.8 gcc-4.8-multilib g++-4.8 g++-4.8-multilib

gcc -pthread -o init linktable.c menu.c test.c -m32 -static

cd ../rootfs

cp ../menu/init ./

find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img

启动MenuOS

qemu-system-i386 -kernel linux-5.0.1/arch/x86/boot/bzImage -initrd rootfs.img

跟踪调试内核启动

qemu-system-i386 -kernel linux-5.0.1/arch/x86/boot/bzImage -initrd rootfs.img -S -s -append nokaslr

cd LinuxKernel/linux-5.0.1

gdb vmlinux

(gdb) target remote:1234

二、跟踪系统调用

根据学号后两位37,在/usr/include/asm/unistd_32.h中可查得#define __NR_kill 37。(以下参照舞者无罪

功能描述:
用于向任何进程组或进程发送信号。

用法: 
#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);

参数: 
pid:可能选择有以下四种

1. pid大于零时,pid是信号欲送往的进程的标识。
2. pid等于零时,信号将送往所有与调用kill()的那个进程属同一个使用组的进程。
3. pid等于-1时,信号将送往所有调用进程有权给其发送信号的进程,除了进程1(init)。
4. pid小于-1时,信号将送往以-pid为组标识的进程。

sig:准备发送的信号代码,假如其值为零则没有任何信号送出,但是系统会执行错误检查,通常会利用sig值为零来检验某个进程是否仍在执行。

返回说明: 
成功执行时,返回0。失败返回-1,errno被设为以下的某个值 
EINVAL:指定的信号码无效
EPERM;没有给任何目标进程发送信号的权限
ESRCH:目标进程或进程组不存在

在test.c中增加函数,KillChild(),由于需要杀死一个进程,因此创建了一个子进程。这部分代码如下:

int KillChild(int argc, char *argv[])
{
	pid_t childpid;
	int ret;
	childpid = fork();
	printf("fork(), childpid = %d\n", childpid);
	ret = kill(childpid, SIGKILL);
	if(ret == 0)
		printf("%d killed\n", childpid);
	else
		perror("kill");
	return 0;
}
int main()
{
    PrintMenuOS();
    SetPrompt("MenuOS>>");
    MenuConfig("version","MenuOS V1.0(Based on Linux 3.18.6)",NULL);
    MenuConfig("quit","Quit from MenuOS",Quit);
    MenuConfig("time","Show System Time",Time);
    MenuConfig("time-asm","Show System Time(asm)",TimeAsm);
    MenuConfig("killchild","fork and kill",KillChild);
    ExecuteMenu();
}

重新编译制作rootfs.img。

 

跟踪调试系统调用。

三、实验分析与总结(参照linux系统调用过程分析

linux的系统调用过程:
层次例如以下:
用户程序------>C库(即API):INT 0x80 ----->system_call------->系统调用服务例程-------->内核程序
先说明一下,我们常说的用户API事实上就是系统提供的C库。
系统调用是通过软中断指令 INT 0x80 实现的,而这条INT 0x80指令就被封装在C库的函数中。

INT 0x80 这条指令的运行会让系统跳转到一个预设的内核空间地址,它指向系统调用处理程序。即system_call函数。

system_call函数是怎么找到详细的系统调用服务例程的呢?通过系统调用号查找系统调用表sys_call_table!软中断指令INT 0x80运行时,系统调用号会被放入 eax 寄存器中,system_call函数能够读取eax寄存器获取,然后将其乘以4,生成偏移地址,然后以sys_call_table为基址。基址加上偏移地址,就能够得到详细的系统调用服务例程的地址了!

系统调用服务例程仅仅会从堆栈里获取參数,所以在system_call运行前。会先将參数存放在寄存器中。system_call运行时会首先将这些寄存器压入堆栈。system_call退出后。用户能够从寄存器中获得(被改动过的)參数。 

另外:系统调用通过软中断INT 0x80陷入内核。跳转到系统调用处理程序system_call函数,然后运行对应的服务例程。可是因为是代表用户进程,所以这个运行过程并不属于中断上下文,而是进程上下文。因此。系统调用运行过程中,能够訪问用户进程的很多信息,能够被其它进程抢占,能够休眠。

当系统调用完毕后,把控制权交回到发起调用的用户进程前。内核会有一次调度。


学号后三位:137

原创作品转载请注明出处

资料来源:https://github.com/mengning/linuxkernel/

 

posted on 2019-03-19 21:54  学号137  阅读(178)  评论(0编辑  收藏  举报