首页 > 其他 > 详细

深入理解系统调用

时间:2020-05-26 23:21:13      阅读:86      评论:0      收藏:0      [点我收藏+]

实验要求:

  1. 找一个系统调用,系统调用号为学号最后2位相同的系统调用
  2. 通过汇编指令触发该系统调用
  3. 通过gdb跟踪该系统调用的内核处理过程
  4. 重点阅读分析系统调用入口的保存现场、恢复现场和系统调用返回,以及重点关注系统调用过程中内核堆栈状态的变化

实验步骤:

一、实验环境准备

 1.下载内核源代码

sudo apt install axel
axel -n 20 https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.4.34.tar.xz
xz -d linux-5.4.34.tar.xz
tar -xvf linux-5.4.34.tar
cd linux-5.4.34

2.配置内核选项

make defconfifig # Default confifiguration is based on x86_64_defconfifig
make menuconfifig
# 打开debug相关选项
Kernel hacking --->
Compile-time checks and compiler options --->
[*] Compile the kernel with debug info
[*] Provide GDB scripts for kernel debugging
[*] Kernel debugging
# 关闭KASLR,否则会导致打断点失败
Processor type and features ---->
[] Randomize the address of the kernel image (KASLR)

技术分享图片

3.编译和运行文件内核

make -j$(nproc) # nproc gives the number of CPU cores/threads available
# 测试?下内核能不能正常加载运?,因为没有?件系统最终会kernel panic
qemu-system-x86_64 -kernel arch/x86/boot/bzImage

编译过程中报错:

Makefile:1076: recipe for target ‘vmlinux‘ failed
make: *** [vmlinux] Error 137

解决:查明原因是swap交换空间不足,于是按以下方法增加swap分区空间。

free#查看swap可用空间大小,发现为0,增加可用空间
cd /
sudo mkdir swap
cd swap/
sudo dd if=/dev/zero of=swapfile bs=1M count=2048#增加2g空间
sudo mkswap swapfile
sudo swapon swapfile
free #查看扩展后的空间

技术分享图片

此时不能运行属于正常现象。

3.制作根文件系统

回到根文件目录,执行

axel -n 20 https://busybox.net/downloads/busybox-1.31.1.tar.bz2
tar -jxvf busybox-1.31.1.tar.bz2
cd busybox-1.31.1
make menucon?g
Settings --->
[*] Build static binary (no shared libs)
然后编译安装,默认会安装到源码?录下的 _install ?录中。
make -j$(nproc) && make install

然后制作根系统文件镜像。

mkdir rootfs 
cd rootfs 
cp ~/busybox-1.31.1/_install/* ./ -rf 
mkdir dev proc sys home 
sudo cp -a /dev/{null,console,tty,tty1,tty2,tty3,tty4} dev/

准备init脚本?件放在根?件系统跟?录下(rootfs/init),添加如下内容到init?件。

#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
echo "Welcome My OS!"
echo "-------------------"
cd home
/bin/sh

给init脚本增加可执行权限

chmod +x init

输入下面代码查看qemu运行情况:

#打包成内存根?件系统镜像
?nd . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz
#回根目录测试挂载根?件系统,看内核启动完成后是否执?init脚本
qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd busybox-1.31.1/rootfs.cpio.gz 

技术分享图片

执行成功,可以进行接下来的系统调用实验。

二、触发系统调用

1.打开/linux-5.4.34/arch/x86/entry/syscalls/syscall_64.tb文件,查询得知

技术分享图片

找到对应函数原型,

技术分享图片

shmat的作用是把共享内存区对象映射到调用进程的地址空间,通常要和shmget函数一起出现,达到创建和使用共享内存的目的。共享内存是进程间通信中最简单的方式之一。共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。

所需头文件:

#include <sys/types.h>

#include <sys/shm.h>

函数传入值:

msqid(共享内存标识符)

shmaddr(指定共享内存出现在进程内存地址的什么位置,直接指定为NULL让内核自己决定一个合适的地址位置)

shmflg(SHM_RDONLY:为只读模式,其他为读写模式)

函数返回值

成功:附加好的共享内存地址

出错:-1,错误原因存于error中

fork后子进程继承已连接的共享内存地址。exec后该子进程与已连接的共享内存地址自动脱离(detach)。进程结束后,已连接的共享内存地址会自动脱离(detach)

错误代码:

EACCES:无权限以指定方式连接共享内存

EINVAL:无效的参数shmid或shmaddr

ENOMEM:核心内存不足

2.编写测试函数 testsh.c

#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
typedef struct{
    char name[4];
    int age;
} people;
int main(int argc, char** argv)
{
    int shm_id,i;
    key_t key;
    char temp;
    people *p_map;
    char* name = "/dev/shm/myshm2";
    key = ftok(name,0);
    if(key==-1)
        printf("ftok error");
    shm_id=shmget(key,4096,IPC_CREAT);  
    if(shm_id==-1)
    {
        printf("shmget error");
        return 0;
    }
    p_map=(people*)shmat(shm_id,NULL,0);
    temp=a;
    for(i = 0;i<10;i++)
    {
        temp+=1;
        memcpy((*(p_map+i)).name,&temp,1);
        (*(p_map+i)).age=20+i;
    }
    if(shmdt(p_map)==-1)
        printf(" detach error ");

   return 0;
}

将测试文件放在rootfs/home 目录下,进行静态编译,再在rootfs目录下重新打包。

gcc -o testsh testsh.c -static
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz

3.gdb系统跟踪调用

先启动qemu

qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz -S -s -nographic -append "console=ttyS0"

再重新打开一个终端,运行以下命令:

cd linux-5.4.34/
gdb vmlinux
target remote:1234
b __x64_sys_shmat
c

技术分享图片

在运行qemu的终端中,输入./testssh即可开始单步调试,调试过程如下:

Breakpoint 1, __x64_sys_shmat (regs=0xffffc900001b7f58) at ipc/shm.c:1591
1591    SYSCALL_DEFINE3(shmat, int, shmid, char __user *, shmaddr, int, shmflg)
(gdb) n
do_syscall_64 (nr=18446744071600450016, regs=0xffffc900001b7f58)
    at arch/x86/entry/common.c:300
300        syscall_return_slowpath(regs);
(gdb) n
301    }
(gdb) n
entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:184
184        movq    RCX(%rsp), %rcx
(gdb) n
185        movq    RIP(%rsp), %r11
(gdb) n
187        cmpq    %rcx, %r11    /* SYSRET requires RCX == RIP */
(gdb) n
188        jne    swapgs_restore_regs_and_return_to_usermode
(gdb) n
205        shl    $(64 - (__VIRTUAL_MASK_SHIFT+1)), %rcx
(gdb) n
206        sar    $(64 - (__VIRTUAL_MASK_SHIFT+1)), %rcx
(gdb) n
210        cmpq    %rcx, %r11
(gdb) n
211        jne    swapgs_restore_regs_and_return_to_usermode
(gdb) n
213        cmpq    $__USER_CS, CS(%rsp)        /* CS must match SYSRET */
(gdb) n
214        jne    swapgs_restore_regs_and_return_to_usermode
(gdb) n
216        movq    R11(%rsp), %r11
(gdb) n
217        cmpq    %r11, EFLAGS(%rsp)        /* R11 == RFLAGS */
(gdb) n
218        jne    swapgs_restore_regs_and_return_to_usermode
(gdb) n
238        testq    $(X86_EFLAGS_RF|X86_EFLAGS_TF), %r11
(gdb) n
239        jnz    swapgs_restore_regs_and_return_to_usermode
(gdb) n
243        cmpq    $__USER_DS, SS(%rsp)        /* SS must match SYSRET */
(gdb) n
244        jne    swapgs_restore_regs_and_return_to_usermode
(gdb) n
253        POP_REGS pop_rdi=0 skip_r11rcx=1
(gdb) n
entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:259  //现场恢复
259        movq    %rsp, %rdi
(gdb) n
260        movq    PER_CPU_VAR(cpu_tss_rw + TSS_sp0), %rsp
(gdb) n
entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:262
262        pushq    RSP-RDI(%rdi)    /* RSP */
(gdb) n
entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:263
263        pushq    (%rdi)        /* RDI */
(gdb) n
entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:271
271        SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi
(gdb) n
273        popq    %rdi
(gdb) n
entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:274
274        popq    %rsp
(gdb) n
entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:275
275        USERGS_SYSRET64
(gdb) n
0x000000000044b9f7 in ?? ()
(gdb) n
Cannot find bounds of current function

4.总结分析

进程间需要共享的数据被放在一个叫做IPC共享内存区域的地方,所有需要访问该共享区域的进程都要把该共享区域映射到本进程的地址空间中去。系统共享内存通过shmget获得或创建一个IPC共享内存区域,并返回相应的标识符。内核在保证shmget获得或创建一个共享内存区,初始化该共享内存区相应的shmid_kernel结构注同时,还将在特殊文件系统shm中,创建并打开一个同名文件,并在内存中建立起该文件的相应dentry及inode结构,新打开的文件不属于任何一个进程(任何进程都可以访问该共享内存区)。所有这一切都是系统调用shmget完成的。

在创建了一个共享内存区域后,还要将它映射到进程地址空间,系统调用shmat()完成此项功能,通过do_syscall_64执行系统调用内容。触发相应函数,完成对象映射后恢复现场。

深入理解系统调用

原文:https://www.cnblogs.com/su-perqaq/p/12956807.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!