《庖丁解牛》第四章书本知识总结

  1. 系统调用的三层机制

API(应用程序编程接口)

中断向量(系统调用处理入口)

服务程序(系统调用内核处理系统)

  1. 计算机的硬件资源是有限的,为了减少有限资源的访问和使用冲突,CPU和操作系统必须提供一些机制对用户程序进行权限划分,Linux系统采用了Intel x86 CPU的0和3两个特权级别,分别对应内核态和用户态。
  2. 用户态和内核态很显著的区分方法

CS:EIP的指向范围(对于32位的X86机器):

  • 内核态下可以访问0x00000000~0xffffffff的地址空间,共4GB。
  • 用户态下只能访问0x00000000~0xbfffffff的地址空间,不能访问从0xc0000000开始的地址空间。
  1. 一般来说,进入内核态是由中断触发的,系统调用是一种特殊的中断。
  2. 中断的类别

硬件中断:在用户态进程执行时,硬件中断信号到来,进入内核态,就会执行这个中断对应的中断服务例程。

软中断(trap的方式):用户态程序执行的过程中,调用了一个系统调用,陷入了内核态。

  1. Linux下系统调用通过int0x80中断完成,中断保存了用户态CS:EIP的值,以及当前的堆栈段寄存器的栈顶,将EFLAGS寄存器的当前值保存到内核堆栈里,同时把当前的中断信号或者是系统调用的终端服务程序的入口加载到CS:EIP中,把当前的堆栈段SS:ESP也加载到CPU里,这些都是由中断信号或者是int指令来完成的。完成后,当前CPU在执行下一条指令时就已经开始执行中断处理程序的入口了,这时对堆栈的操作已经是内核堆栈操作了,SAVE_ALL就是内核代码,完成中断服务,如果没有发生进程调度,执行restore_all恢复中断现场,然后iret返回到原来的状态;如果发生了进程调度,当前的这些状态就会暂时地保存在系统内核堆栈里,当下一次发生进程调度有机会再切换回当前进程时,执行restore_alliret,这样中断处理过程就执行结束。
  2. 系统调用通过系统调用号进行区分,通过EAX寄存器传递。
  3. 普通函数调用是通过将参数压栈的方式传递的,而系统调用因为在用户态和内核态下使用不同的堆栈,无法通过参数压栈的方式进行传递,而是通过比较特殊的寄存器传递参数的方式进行。
  4. 参数按照顺序赋值给EBXECXEDXESIEDIEBP,参数的个数不能超过6个,否则的话全部参数应该依次放在一块连续的内存区域里,同时在寄存器EBX中保存指向该内存区域的指针。

实验:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

看了本课程教材之后,我想试试使用超过6个参数的系统调用,可是失败了,具体过程如下:

  • 先写了如下代码:
#include <stdio.h>
int main()
{
int ret;
char* n[8] = {"1","2","3","4","5","6","7","8"}; ret = mkdir(n[0],n[1],n[2],n[3],n[4],n[5],n[6],n[7],n[8]); if(ret == 0)
printf("mkdir successfully!\n");
else
printf("unable to mkdir.\n"); return 0;
}

结果如下:



好像有点不对?试试Linux系统命令:



Linux命令是可以的呀,难道是mkdir系统调用只能传递一个参数吗?

  • 编写了嵌入汇编的代码
#include <stdio.h>

int main()
{
int ret;
char* name[8] = {"1","2","3","4","5","6","7","8"}; asm volatile(
"movl %1,%%ebx\n\t"
"movl $0x27,%%eax\n\t"
"int $0x80"
:"=a"(ret)
:"m"(name) //name数组的地址放入内存变量
); if(ret == 0)
printf("mkdir successfully!\n");
else
printf("unable to mkdir.\n"); return 0;
}

果不其然,结果还是相同:



将代码修改一下,嵌入汇编中的name替换为name[2],重新编译运行,得到如下结果:



从这个结果来看,大约mkdir系统调用的参数无法这样传输多个。

  • mkdir系统调用传递两个参数

    经过搜索,mkdir系统调用可以传递的两个参数是文件夹名文件夹权限,因此编写代码如下:
#include <stdio.h>
int main()
{
int ret; ret = mkdir("1",0755); if(ret == 0)
printf("mkdir successfully!\n");
else
printf("unable to mkdir.\n"); return 0;
}

权限0755代表只有所有者才有读,写,执行的权限,组群和其他人只有读和执行的权限。编译、运行得到了想要的结果:

  • 继续编写嵌入汇编代码
#include <stdio.h>
int main()
{
int ret;
char* name = "1";
int mod = 0755; asm volatile(
"movl %2,%%ecx\n\t"
"movl %1,%%ebx\n\t"
"movl $0x27,%%eax\n\t"
"int $0x80"
:"=a"(ret)
:"b"(name),"c"(mod)
); if(ret == 0)
printf("mkdir successfully!\n");
else
printf("unable to mkdir.\n"); return 0;
}

结果相同:

其他想法

本次实验和书本学习,我学到了不少东西,比如mkdir系统调用的两个参数,再比如查看目录的相关权限可以采用命令ls -lD,或者直接用ls -la,更学到了Linux系统中系统调用的三层机制,受益匪浅,也有一个小猜想:Linux系统中的mkdir命令,在添加了多个参数时,是否是通过多次调用mkdir系统调用执行的?还望看到的老师同学们、朋友们能予以解答。

参考资料

《庖丁解牛Linux》

最新文章

  1. 用 Excel 测试“绘制两点间连线”的算法
  2. 七月十三号CSS3总结《2D的转换》
  3. whereis 和which
  4. 持续集成(CI)初探
  5. JSTL.带标签体的标签,方法和例子
  6. DOM(文本对象模型)简介
  7. ADO.NET——获取output 和 return值
  8. [转载]大道至简!!!从SAP HANA作为SAP加速器的方式,看ERP on HANA的春天
  9. This configuration file was broken by system-config-keyboard
  10. html5学习(三)
  11. Linux服务器自动备份压缩MySQL数据库的实用方法
  12. OpenFlow硬件交换机制作及刷机教程
  13. 如何利用wireshark破解网站密码
  14. [转] Java程序员学C#基本语法两个小时搞定(对比学习)
  15. Python VisibleDeprecationWarning: converting an array with ndim &gt; 0 to an index will result in an error in the future
  16. tomcat通过一个端口号实现多域名访问
  17. windows安装Redis和客户端
  18. 训练赛-Eyad and Math
  19. java爬虫笔记
  20. mac更改launchpad图标大小

热门文章

  1. WPS之替换样式
  2. cache buffers chains以及热块解决方案
  3. JS-缓冲运动:菜单栏型悬浮框
  4. deferred对象(摘自别人的文章)
  5. Eclipse &#39;&lt;&gt;&#39; operator is not allowed for source level below 1.7
  6. tomcat的添加及jar包和jQuery的加载
  7. ExtJS学习
  8. [Gradle] 查看项目依赖
  9. [Gradle] 发布构件到本地仓库
  10. http://www.cnblogs.com/nangong/p/db29669e2c6d72fb3d0da947280aa1ce.htm ASP.NET从零开始学习EF的增删改查