1. 系统调用概述
用户态与内核态
- 用户态是指程序在相应的低级别执行状态下,代码的掌控范围会受到限制;只能在对应级别允许的范围内活动;
- 内核态是指操作系统在高执行级别下,程序代码可以执行特权指令,访问任意的物理地址;
- 内核态与用户态的区别不是说用户程序的运行状态如何如何,主要区别在于 CPU/OS 的控制权限得以提升或下降;
- 用户态与内核态切换的触发方式:系统调用、异常、中断;
系统调用与Shell
- 为了实现内核与用户的交互,计算机提供了两种方式:系统调用和 Shell ;
- 系统调用是操作系统为用户态进程与硬件设备进行交互提供的一组接口,是为了方便程序员编程用;
- Shell 则与编程无关,涉及到的是 Shell 脚本编程(命令的集合),是为了方便人机交互而设计的;
- 当然,Shell 命令可能暗地里调用的还是系统调用;
库函数API与系统调用
应用编程接口(Application Programming Interface)与系统调用(System Call)是不同的。API 只是函数定义,而系统调用则是通过软中断向内核发出一个明确的请求。系统调用是在内核态完成的,而以普通函数为代表的 API 是在用户态下完成的。
系统调用的工作流程
应用程序、封装例程、系统调用处理程序及 系统调用服务例程之间的关系
2. 方法论
深入系统调用的话,就需要我们在C代码中嵌入汇编语言来触发系统调用,也就是说我们需要通过库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用。那么如何做到呢?我的方法是,C 语言的编程代码我们可以顺利地编程得到,但是 C 代码中嵌入汇编的方式可能无法顺利地做到,或者做到的过程中会出现问题,我们就需要一条条语句来做,并且每条语句做好了就立马进行调试,使得最后得到的结果无限接近于用 C 代码写出的程序。
3. 程序目标
这次实验所编写的C 程序是文件拷贝程序,将当前目录下文件名为 “src_file” 的文件内容拷贝到当前目录的另一个文件 “dest_file” 中去(文件不存在则创建)。
4. 编码过程
C代码实现
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#define BUFFER_SIZE 1024 /* 每次读写缓存大小,影响运行效率 */
#define OFFSET 10240 /* 拷贝的数据大小 */
#define SRC_FILE_NAME "src_file" /* 源文件名 */
#define DEST_FILE_NAME "dest_file" /* 目标文件名 */
int main()
{
int src_fd, dest_fd;
unsigned char buff[BUFFER_SIZE];
int buff_len;
/* 以只读的方式打开源文件 */
src_fd = open(SRC_FILE_NAME, O_RDONLY);
/* 以只读的方式打开目标文件,若此文件不存在则创建,访问权限为644 */
dest_fd = open(DEST_FILE_NAME, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
if (src_fd < 0 || dest_fd < 0)
{
printf("Open File Error!\n");
exit(1);
}
/* 将源文件的读写指针移到最后10KB的起始位置 */
lseek(src_fd, -OFFSET, SEEK_END);
/* 读取源文件的最后10KB数据并写到目标文件中,每次读写1KB */
while ((buff_len = read(src_fd, buff, sizeof(buff))) > 0)
{
write(dest_fd, buff, buff_len);
}
close(dest_fd);
close(src_fd);
return 0;
}
实验截图