首页 > 编程知识 正文

java的运行原理,java并发编程实战

时间:2023-05-05 15:19:50 阅读:153762 作者:129

实验内容:

寻找系统呼叫,系统呼叫号码为学号的最后2位相同的系统呼叫【即97号系统呼叫】

汇编指令触发此系统调用

用gdb跟踪这个系统调用的内核处理过程

重点阅读和分析系统调用入口的保存现场、恢复现场和系统调用的返回,重点关注系统调用过程中内核堆栈状态的变化

实验环境:

VMWare虚拟机下的Ubuntu18.04.4,用于实验的内核版本为linux-5.4.34。

1环境准备

1.1内核编译

回滚实验1的修补程序操作:

cd linux-5.4.34

patch-r-P1 ./my kernel-2.0 _ for _ Linux-5.4.34.patch

生成定义

更改内核编译配置并重新编译:

打开调试相关选项

Kernel hacking ---

compile-timechecksandcompileroptions----

[ * ] compilethekernelwithdebuginfo

[ * ] providegdbscriptsforkerneldebugging

[ * ]密钥调试

关闭KASLR。 否则断点将失败

Processor type and features ---

[ ] randomizetheaddressofthekernelimage (ka SLR )。

生成菜单

make -j$(nproc )

启动内核。 内核无法正常工作。 指示Kernel panic报告错误。

QEMU-system-x86 _ 64-kernel arch/x86/boot/bzimage

错误消息表明内核无法装载,因为缺少所需的根文件系统。

1.2创建根文件系统

要打开并启动计算机,引导加载器首先加载内核。 必须立即挂载包含所需设备驱动程序和工具的内存根文件系统。

为了简化实验环境,只用BusyBox制作迷你内存根文件系统,并给出基本的用户状态可执行程序。

首先从https://www.busybox.net下载并解压缩busybox源代码,解压缩完成后配置并安装编译。

axel-n 20https://busybox.net/downloads/busybox-1.31.1.tar.bz2

tar -jxvf busybox-1.31.1.tar.bz2

设置为不使用动态链接库编译为静态链接。

cd busybox-1.31.1

生成菜单

编译安装时,缺省情况下安装在源目录的_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的内容如下。

#! /在意的乌冬面/sh

mount -t proc none /proc

mount -t sysfs none /sys

echo 'Wellcome MengningOS!'

echo-------------------- "

光盘主页

/在意的乌冬面/sh

将可执行权限添加到init脚本:

chmod x init

打包并镜像到内存根文件系统:

find.- print0| cpio-- null-ov-- format=newc|gzip-9 ./rootfs.cpio.gz

装载根文件系统,并检查是否在内核启动完成后运行init脚本。

qmu-system-x86 _ 64-kernel Linux-5.4.34/arch/x86/boot/bzimage-initrd rootfs.cpio.gz

当bootloader成功将根文件系统加载到内存中时,内核将挂载在根目录下。

接下来,运行根文件系统

统中 init 脚本执行一些启动任务,最后才挂载真正的磁盘根文件系统。

2 系统调用

2.1 查找系统调用

在 linux-5.4.34/arch/x86/entry/syscalls/syscall_64.tbl 文件中找到相应的系统调用:

2.2 触发系统调用

getrlimit用于获得每个进程能够创建的各种系统资源的限制使用量。

在rootfs/home/目录下新建getrlimit_test.c进行测试:

#include

#include

int main()

{

struct rlimit limit;

int ret = getrlimit(RLIMIT_NOFILE, &limit);

printf("ret = %d,tcur = %ld,tmax = %ldn",

ret, limit.rlim_cur, limit.rlim_max);

return 0;

}

函数执行成功返回0,失败返回1。

其中,RLIMIT_NOFILE表示每个进程能打开的最多文件数。

limit.rlim_cur为当前软件限制,limit.rlim_max为最大硬件限制。

采用静态编译:

gcc -o getrlimit_test getrlimit_test.c -static

代码测试结果如下:

getrlimit测试成功后,通过编写汇编代码来触发系统调用:

#include

#include

int main()

{

struct rlimit limit;

int ret = -1;

asm volatile(

"movq %2, %%rsint"

"movl %1, %%edint"

"movl $0x61, %%eaxnt"

"syscallnt"

"movq %%rax,%0nt"

:"=m"(ret)

:"a"(RLIMIT_NOFILE), "b"(&limit)

);

printf("ret = %d,tcur = %ld,tmax = %ldn",

ret, limit.rlim_cur, limit.rlim_max);

return 0;

}

2.3 跟踪系统调用内核处理过程

重新制作根文件系统:

find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz

纯命令行启动qemu:

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

开启新的terminal进行gdb调试:

cd linux-5.4.34

gdb vmlinux

target remote:1234

c

添加断点测试:

b __x64_sys_getrlimit

发现断点处无法停止,需要分析getrlimit反汇编的代码:

此处实际调用的是0x12e也就是302号系统调用,所以之前的断点才会没有反应。

重新设置断点:

b __x64_sys_prlimit64

成功进入中断:

观察函数调用栈,可以找到系统调用入口 entry_SYSCALL_64:

ENTRY(entry_SYSCALL_64)

UNWIND_HINT_EMPTY

/*

* Interrupts are off on entry.

* We do not frame this tiny irq-off block with TRACE_IRQS_OFF/ON,

* it is too small to ever cause noticeable irq latency.

*/

swapgs

/* tss.sp2 is scratch space. */

movq%rsp, PER_CPU_VAR(cpu_tss_rw + TSS_sp2)

SWITCH_TO_KERNEL_CR3 scratch_reg=%rsp

movqPER_CPU_VAR(cpu_current_top_of_stack), %rsp

/* Construct struct pt_regs on stack */

pushq$__USER_DS/* pt_regs->ss */

pushqPER_CPU_VAR(cpu_tss_rw + TSS_sp2)/* pt_regs->sp */

pushq%r11/* pt_regs->flags */

pushq$__USER_CS/* pt_regs->cs */

pushq%rcx/* pt_regs->ip */

GLOBAL(entry_SYSCALL_64_after_hwframe)

pushq%rax/* pt_regs->orig_ax */

PUSH_AND_CLEAR_REGS rax=$-ENOSYS

TRACE_IRQS_OFF

之后调用 do_syscall_64:

#ifdef CONFIG_X86_64

__visible void do_syscall_64(unsigned long nr, struct pt_regs *regs)

{

struct thread_info *ti;

enter_from_user_mode();

local_irq_enable();

ti = current_thread_info();

if (READ_ONCE(ti->flags) & _TIF_WORK_SYSCALL_ENTRY)

nr = syscall_trace_enter(regs);

if (likely(nr < NR_syscalls)) {

nr = array_index_nospec(nr, NR_syscalls);

regs->ax = sys_call_table[nr](regs);

#ifdef CONFIG_X86_X32_ABI

} else if (likely((nr & __X32_SYSCALL_BIT) &&

(nr & ~__X32_SYSCALL_BIT) < X32_NR_syscalls)) {

nr = array_index_nospec(nr & ~__X32_SYSCALL_BIT,

X32_NR_syscalls);

regs->ax = x32_sys_call_table[nr](regs);

#endif

}

syscall_return_slowpath(regs);

}

#endif

SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource,

const struct rlimit64 __user *, new_rlim,

struct rlimit64 __user *, old_rlim)

{

struct rlimit64 old64, new64;

struct rlimit old, new;

struct task_struct *tsk;

unsigned int checkflags = 0;

int ret;

if (old_rlim)

checkflags |= LSM_PRLIMIT_READ;

if (new_rlim) {

if (copy_from_user(&new64, new_rlim, sizeof(new64)))

return -EFAULT;

rlim64_to_rlim(&new64, &new);

checkflags |= LSM_PRLIMIT_WRITE;

}

rcu_read_lock();

tsk = pid ? find_task_by_vpid(pid) : current;

if (!tsk) {

rcu_read_unlock();

return -ESRCH;

}

ret = check_prlimit_permission(tsk, checkflags);

if (ret) {

rcu_read_unlock();

return ret;

}

get_task_struct(tsk);

rcu_read_unlock();

ret = do_prlimit(tsk, resource, new_rlim ? &new : NULL,

old_rlim ? &old : NULL);

if (!ret && old_rlim) {

rlim_to_rlim64(&old, &old64);

if (copy_to_user(old_rlim, &old64, sizeof(old64)))

ret = -EFAULT;

}

put_task_struct(tsk);

return ret;

}

运行结束后,通过syscall_return_slowpath返回,系统调用完毕。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。