一、编译内核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
#include
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/