举例跟踪分析Linux内核5.0系统调用处理过程

学号084 原创作品转载请注明出处+ https://github.com/mengning/linuxkernel/ 

一、Linux-5.0.2版本内核编译

编译内核的过程充分证明了了解底层知识是多么的必要。在使用手工根目录过程和使用Busybox编译过程中遇到同样一个问题:

在网上搜集了相关的错误信息,有些博客说是因为未能检测到device,有些博客说是因为某个libxxx.so文件出现了问题,有些博客说是virtualbox的问题,但是解决办法都没有解决我的问题。无奈,只能不断尝试。最终,参考Linux内核编译与安装博客内容完成了内核5.0.2版本的编译。

实验环境: Ubuntu 16.04.6
内核版本:

编译过程与指令:
1.下载源码并安装

mv linux-5.0.2.tar.xz /usr/src
cd linux-5.0.2
cp /boot/config<tab> ./config

2.配置与编译内核

make menuconfig
make
make modules_install
make install

问题基本都集中于编译内核的过程中,常见的问题如下:

a.缺少bison库
sudo apt-get install bison

b.缺少flex库
sudo apt-get install flex

c.openssl出错
sudo apt-get install libssl-dev

其中处理openssl错误时,又出现了libssl-dev无法安装的问题。这里是因为libssl-dev需要使用aptitude 进行降级版本安装
sudo apt-get install aptitude
sudo aptitude install libssl-dev

经过较长时间的make时间,编译系统的难题集中点就被攻克了。

3.生成启动

mkinitramfs -o /boot/initrd.img-5.0.2
update-initramfs -c -k 5.0.2
update-grub2

重启后,再次查看内核版本:

至此,内核5.0版本编译工作基本完成!这次实验的问题主要集中于Exitcode:0x0000000b的问题,耗费了很多时间着实无奈。好在,终于能够编译成功,继续接下来的实验。了解硬件与底层知识,对于解决相关问题颇为重要。

二、系统调用跟踪分析

1.基础知识补充
首先通过查询/usr/include/asm/unistd_32.h文件获取与学号对应的系统调用号及其所对应的函数。

84号对应的是oldlstat函数。 oldlstat主要包含stat、fstat、lstat,主要用于获取文件的状态。使用过程中需要使用头文件

#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>

其函数头的主要形式为:

int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);

这里我们再看一下struct stat这个结构体的内容:

struct stat {
    dev_t     st_dev;     /* ID of device containing file */
    ino_t     st_ino;     /* inode number */
    mode_t    st_mode;    /* protection */
    nlink_t   st_nlink;   /* number of hard links */
    uid_t     st_uid;     /* user ID of owner */
    gid_t     st_gid;     /* group ID of owner */
    dev_t     st_rdev;    /* device ID (if special file) */
    off_t     st_size;    /* total size, in bytes */
    blksize_t st_blksize; /* blocksize for file system I/O */
    blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
    time_t    st_atime;   /* time of last access */
    time_t    st_mtime;   /* time of last modification */
    time_t    st_ctime;   /* time of last status change */
};

可以看到结构体中定义了一堆与文件属性相关的内容,为了简化实验,这里我们针对st_size进行实验。

2.从孟老师的github上获取time()函数范例https://github.com/mengning/menu

3.参考time()函数范例,使用自己的函数调用号编写相关代码。
首先我们新建了Oldlstat()函数,设置一个整形变量用于记录lstat()函数存放在stat结构体中的相关参数。

int Oldlstat()
{
	struct stat buf;
	int num;
	lstat("/etc/passwd", &buf);
	num = buf.st_size;
	return num;
}

而后我们编写c汇编函数OldstatAsm(),这里与之前的Oldlstat()功能基本一致,只是修改了相应函数的系统调用号。

int OldstatAsm()
{
	struct stat buf;
	int num;
	asm volatile(
		"mov $0x54, %%eax\n\t"
		"int $0x80\n\t"
		"mov %%eax, %0\n\t"
		: "=m"(buf)
	);
	num = buf.st_size;
	return num;
}

ok,使用gcc编译后我们来看看输出结果:

4.使用gdb分析代码运行
我们在调试中对Oldlstat()和OldlstatAsm()函数的入口与出口插入了断点,用来分析堆栈的变化过程。

这里我们可以看到当main()函数使用Oldlstat()函数,寄存器的位置发生了变化,并保存了入口现场,待调用结束后返回。

这里对于Oldlstat()函数调用结束,此时用来存放buf.st_size结果的变量num已经获得了值,函数位置回到了main()函数并继续执行。

这里主要针对OldlstatAsm的C汇编代码,我们也可以清晰地看到,函数的位置发生了变化,完成切换->执行->返回现场的三步变化。我们使用int 0x80触发中断,然后中断处理程序保存现场,我们的进程陷入内核态。系统调用的工作机制是:当用户态进程调用一个系统调用时,CPU切换到内核态并开始执行一个内核函数,由API、中断向量和中断处理程序协调完成。

三、总结与补充

系统调用的方式: Linux下主要有三种系统调用方式:int 0x80 ,sysenter 和 syscall。传统系统调用(int 0x80) 通过中断/异常实现,在执行 int 指令时,发生 trap。硬件找到在中断描述符表中的表项,在自动切换到内核栈 (tss.ss0 : tss.esp0) 后根据中断描述符的 segment selector 在 GDT / LDT 中找到对应的段描述符,从段描述符拿到段的基址,加载到 cs ,将 offset 加载到 eip。最后硬件将 ss / sp / eflags / cs / ip / error code 依次压到内核栈。返回时,iret 将先前压栈的 ss / sp / eflags / cs / ip 弹出,恢复用户态调用时的寄存器上下文。我们的实验内容主要采取的就是这种方式。

那么为什么需要系统调用呢? linux内核中设置了一组用于实现系统功能的子程序,称为系统调用。系统调用和普通库函数调用非常相似,只是系统调用由操作系统核心提供,运行于内核态,而普通的函数调用由函数库或用户自己提供,运行于用户态。一般的进程是不能访问内核的。它不能访问内核所占内存空间也不能调用内核函数。为了和用户空间上运行的进程进行交互,内核提供了一组接口。透过该接口,应用程序可以访问硬件设备和其他操作系统资源。这组接口在应用程序和内核之间扮演了使者的角色,应用程序发送各种请求,而内核负责满足这些请求。

总结,系统调用是一项非常重要的功能,通过分割用户态与内核态,一方面实现对内核的保护,另一方面解决了用户的需要。但是手写汇编中断程序还是一件非常危险的事情,要谨慎对待!
参考:
https://www.binss.me/blog/the-analysis-of-linux-system-call/ https://blog.csdn.net/gatieme/article/details/50779184

转载于:https://my.oschina.net/u/1469512/blog/3024229

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在MATLAB中,NURBS(非均匀有理B样条)是一种强大的数学工具,用于表示和处理复杂的曲线和曲面。NURBS在计算机图形学、CAD(计算机辅助设计)、CAM(计算机辅助制造)等领域有着广泛的应用。下面将详细探讨MATLAB中NURBS的绘制方法以及相关知识点。 我们需要理解NURBS的基本概念。NURBS是B样条(B-Spline)的一种扩展,其特殊之处在于引入了权重因子,使得曲线和曲面可以在不均匀的参数空间中进行平滑插值。这种灵活性使得NURBS在处理非均匀数据时尤为有效。 在MATLAB中,可以使用`nurbs`函数创建NURBS对象,它接受控制点、权值、 knot向量等参数。控制点定义了NURBS曲线的基本形状,而knot向量决定了曲线的平滑度和分布。权值则影响曲线通过控制点的方式,大的权值会使曲线更靠近该点。 例如,我们可以使用以下代码创建一个简单的NURBS曲线: ```matlab % 定义控制点 controlPoints = [1 1; 2 2; 3 1; 4 2]; % 定义knot向量 knotVector = [0 0 0 1 1 1]; % 定义权值(默认为1,如果未指定) weights = ones(size(controlPoints,1),1); % 创建NURBS对象 nurbsObj = nurbs(controlPoints, weights, knotVector); ``` 然后,我们可以用`plot`函数来绘制NURBS曲线: ```matlab plot(nurbsObj); grid on; ``` `data_example.mat`可能包含了一个示例的NURBS数据集,其中可能包含了控制点坐标、权值和knot向量。我们可以通过加载这个数据文件来进一步研究NURBS的绘制: ```matlab load('data_example.mat'); % 加载数据 nurbsData = struct2cell(data_example); % 转换为cell数组 % 解析数据 controlPoints = nurbsData{1}; weights = nurbsData{2}; knotVector = nurbsData{3}; % 创建并绘制NURBS曲线 nurbsObj = nurbs(controlPoints, weights, knotVector); plot(nurbsObj); grid on; ``` MATLAB还提供了其他与NURBS相关的函数,如`evalnurbs`用于评估NURBS曲线上的点,`isoparm`用于生成NURBS曲面上的等参线,以及`isocurve`用于在NURBS曲面上提取特定参数值的曲线。这些工具对于分析和操作NURBS对象非常有用。 MATLAB中的NURBS功能允许用户方便地创建、编辑和可视化复杂的曲线和曲面。通过对控制点、knot向量和权值的调整,可以精确地控制NURBS的形状和行为,从而满足各种工程和设计需求。通过深入理解和熟练掌握这些工具,可以在MATLAB环境中实现高效的NURBS建模和分析
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值