Linux内核分析作业第四周
系统调用的三个层次
一、用户态、内核态和中断
用户通过库函数与系统调用联系起来。
1、内核态
在高的执行级别下,代码可以执行特权指令,访问任意的物理地址,这时的CPU就对应内核态
2、用户态:
在低级别的指令状态下,代码 只能在级别允许的特定范围内活动。在日常操作下,执行系统调用的方式是通过库函数,库函数封装系统调用,为用户提供接口以便直接使用。
- intel x86 CPU有四个权限分级,0-3。Linux只取两种,0是内核态,3是用户态
- 区分权限级别使得系统更加稳定。
3、中断(切换)
中断处理是从用户态进入内核态的主要方式。
硬件中断系统调用
二、系统调用
1、系统调用概念
系统调用(System Call)是操作系统为在用户态运行的进程与硬件设备(如CPU、磁盘、打印机等)进行交互提供的一组接口。
2、系统调用的意义
- 把用户从底层的硬件编程中解放出来
- 极大的提高了系统的安全性
- 使用户程序具有可移植性
3、系统调用
1、当用户进程需要发生系统调用时,CPU 通过软中断切换到内核态开始执行内核系统调用函数。Linux 下有三种发生系统调用的方法:
- 通过 glibc 提供的库函数
- 使用 syscall 函数直接调用
- 通过 int 0x80指令陷入
2、总的来说,前两种最终都会通过int 0x80指令陷入进入中断处理程序。而系统调用也需要输入输出参数,例如实际的值,用户态进程地址空间的变量的地址,甚至是包含指向用户态函数的指针的数据结构的地址等。
3、system_call是linux中所有系统调用的入口点,每个系统调用至少有一个参数,即由eax传递的系统调用号,其他参数依次由ebx,ecx,edx,esi,edi,ebp传入。
4、寄存器传递参数具有如下限制:
- 每个参数的长度不能超过寄存器的长度,即32位
- 在系统调用号(eax)之外,参数的个数不能超过6个(ebx, ecx,edx,esi,edi,ebp)
4、系统调用与应用编程接口
1、应用编程接口 (application program interface, API)
- API只是一个函数定义
- 系统调用通过软中断向内核发出一个明确的请求
2、Libc库定义的一些API引用了封装例程(wrapper routine,唯一目的就是发布系统调用)
- 一般每个系统调用对应一个封装例程
- 库再用这些封装例程定义出给用户的API

系统调用的三个层次依次是:xyz函数(API)、system_ call(中断向量)和 sys_ xyz(中断服务程序)
5、C代码中嵌入汇编代码的写法
__asm__(
汇编语句模板:
输入部分:
输出部分:
破坏描述部分:);
include <stdio.h>
int main()
{
/*实现的功能:val1+val2=val3*/
unsigned int val1 = 1;
unsigned int val2 = 2;
unsigned int val3 = 0;
printf("val1:%d,val2:%d,val3:%d\n",val1,val2,val3); asm volatile(
"movl $0,%%eax\n\t" /*两个%表示转义字符,这一句的目的是把%eax清零*/
"addl %1,%%eax\n\t" /*%1指的是参数的标记,m编号为0,c为1,d为2 这一句把val1赋给eax*/
"addl %2,%%eax\n\t" /*%eax=val1+val2*/
"movl %%eax,%0\n\t" /*把val1和val2的值存储在eax里面*/
: "=m" (val3) /* =表示把val3 的值写到内存变量里面*/
: "c" (val1),"d" (val2) /*用%ecx存储val1,用%edx存储val2*/
); printf("val1:%d+val2:%d=val3:%d\n",val1,val2,val3);
return 0;
}
三、实验
1、使用库函数API获取当前系统时间
代码:
time.c
#include <stdio.h>
#include <time.h>
int main()
{
time_t tt;//int型数值
struct tm *t;//t指针指向一个结构体
tt = time(NULL);//调用系统函数time,返回一个指针类型的变量
t = localtime(&tt);//强制类型转换,便于输出
printf("time:%d:%d:%d:%d:%d:%d:\n",t->tm_year+1960,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
return 0;
}
编译:
gcc time.c -o time -m32
运行:
./time
2、用汇编方式触发系统调用获取系统当前时间
代码:
time_asm.c
#include <stdio.h>
#include <time.h>
int main()
{
time_t tt;//int型数值
struct tm *t;
asm volatile(
"mov $0,%%ebx\n\t"//系统调用传递第一个参数使用ebx,这里是null
"mov $0xd,%%eax\n\t"//传递系统调用号13(13的16进制即为d)
"int $0x80\n\t"//发生中断
"mov %%eax,$0\n\t"//通过eax这个寄存器返回系统调用值
:"=m"(tt)
);
t = localtime(&tt);
printf("time:%d:%d:%d:%d:%d:%d:\n",t->tm_year+1960,t->tm_mon,t->tm_mda,t->tm_hour,t->tm_min,t->tm_sec);
return 0;
}
系统调用返回值使用eax存储,与普通函数一样。
编译:
gcc time_asm.c -o time_asm -m32
运行:
./time-asm
3、使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
- 库函数API进行20号系统调用
代码:
#include<stdio.h>
#include<unistd.h>
int main(void)
{
int d = getpid();
printf("Provess id: %d\n" , d);
return 0;
}

实验结果:

C代码中嵌入汇编代码进行20号系统调用
代码:
#include<stdio.h>
#include <unistd.h>
int main( void ){
int d ;
asm volatile(
"mov $0,%%ebx\n\t"
"mov $0x14,%%eax\n\t"//调用20号的系统调用,即是16进制的14
"int $0x80\n\t"
"mov %%eax,%0\n\t"
:"=m"(d)
);
printf( "Process id: %d\n",d);
return 0;
}

实验结果:

四、遇到的问题


在确认代码无误后,开始运行,第一次出现了错误数据,于是再做了一次检查,对代码并未做任何修改,再运行得出了合理的数据。再次运行又出现了错误数据,不知道是个什么情况。
五、总结
- getpid函数用来取得目前进程的进程ID,许多程序利用取到的此值来建立临时文件,以避免临时文件相同带来的问题
- 系统调用把应用程序的请求传给内核,调用相应的的内核函数完成所需的处理,将处理结果返回给应用程序
- 在系统调用号(eax)之外,参数的个数不能超过6个(ebx,ecx,edx,esi,edi,ebp), 若是超过了6个,那么可以传入一个地址,地址所在地存放多个参数。
何佳 原创作品转载请注明出处《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
Linux内核分析作业第四周的更多相关文章
- 《Linux内核分析》第四周学习总结
<Linux内核分析>第四周学习总结 ——扒开系统调用的三层皮 姓名:王玮怡 学号:20135116 理论总结部分: 第一节 用户态.内核 ...
- 《Linux内核分析》 第四周
[李行之 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000] <Linux内 ...
- 《Linux内核分析》第四周学习笔记
<Linux内核分析>第四周学习笔记 扒开系统调用的三层皮(上) 郭垚 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.c ...
- 《Linux内核分析》第四周学习总结 扒开系统调用的三成皮(上)
第四周 扒开系统调用的三层皮(上) 郝智宇 无转载 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一. ...
- linux内核分析作业8:理解进程调度时机跟踪分析进程调度与进程切换的过程
1. 实验目的 选择一个系统调用(13号系统调用time除外),系统调用列表,使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用 分析汇编代码调用系统调用的工作过程,特别是参数的传递的方 ...
- Linux内核分析作业7:Linux内核如何装载和启动一个可执行程序
1.可执行文件的格式 在 Linux 平台下主要有以下三种可执行文件格式: 1.a.out(assembler and link editor output 汇编器和链接编辑器的输出) ...
- linux内核分析作业6:分析Linux内核创建一个新进程的过程
task_struct结构: struct task_struct { volatile long state;进程状态 void *stack; 堆栈 pid_t pid; 进程标识符 u ...
- linux内核分析作业5:分析system_call中断处理过程
1.增加 Menu 内核命令行 调试系统调用. 步骤:删除menu git clone (tab) make rootfs 这就是我们将 fork 函数写入 Menu 系统内核后的效果, ...
- linux内核分析作业:以一简单C程序为例,分析汇编代码理解计算机如何工作
一.实验 使用gcc –S –o main.s main.c -m32 命令编译成汇编代码,如下代码中的数字请自行修改以防与他人雷同 int g(int x) { return x + 3; } in ...
随机推荐
- Linux 小知识翻译 - 「虚拟化技术」
这次聊聊「虚拟化技术」. 虚拟化技术,有时简称为「虚拟化」,最近经常听人说它.但是却不太清楚它的意思.到底虚拟了什么东西?本来是用来干什么的? 有名的虚拟化软件要数 VMware 和 VirtualB ...
- JS学习小结(上)
学而时习之,不亦说乎,开启JS学习新乐章~ JS是干啥的?网页特效,它主要是实现控制结构和样式,是一种行为,有多重要,不言而喻吧,页面炫酷的资本. 1. JS输出: alert("hello ...
- <20190103>别傻了,一些 新的技术注定只适合新人
故障现象: 用vmware跑的虚拟机不兼容 某银行网银的U盾. 插入usb口后无法识别U盾, 解决过程: 1 更换2台各自不同电脑, 使用前置USB口, 后置USB口. 故障依旧. 2 使 ...
- sql语句如何将多个空格字符替换成一个空格字符
很多时候,数据表中某个字段的值会带有一个或多个空格字符串的情况,面对多样化的需求,我们可能需要将这些空格字符串去除,当然,这很好说,我们可以直接用replace(' ','')将单个空格变成无就可以了 ...
- Matplotlib入门
使用之前首先下载:pip install Matplotlib 接着引入:import matplotlib.pylot as plt 绘图函数:plt.plot() 显示函数:plt.show() ...
- 使用hint优化Oracle的运行计划 以及 SQL Tune Advisor的使用
背景: 某表忽然出现查询很缓慢的情况.cost 100+ 秒以上:严重影响生产. 原SQL: explain plan for select * from ( select ID id,RET_NO ...
- [NOIP2018]保卫王国
嘟嘟嘟 由于一些知道的人所知道的,不知道的人所不知道的原因,我来发NOIP2018day2T3的题解了. (好像我只是个搬运工--) 这题真可以叫做NOIplus了,跟其他几道比较水的题果然不一样,无 ...
- SQL的各种连接Join详解
SQL JOIN 子句用于把来自两个或多个表的行结合起来,基于这些表之间的共同字段. 最常见的 JOIN 类型:SQL INNER JOIN(简单的 JOIN).SQL LEFT JOIN.SQL ...
- DataX的使用
简介 DataX 是阿里巴巴集团内被广泛使用的离线数据同步工具/平台,实现包括 MySQL.Oracle.HDFS.Hive.OceanBase.HBase.OTS.ODPS 等各种异构数据源之间高效 ...
- 转载 c# automapper 使用(一) https://www.cnblogs.com/caoyc/p/6367828.html
一.最简单的用法 有两个类User和UserDto 1 public class User 2 { 3 public int Id { get; set; } 4 public string Name ...
