2020-2021-1 20209326 《Linux内核原理与分析》第五周作业
[toc] 一、实验内容:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
###1. 13号系统调用time
编写time.c
#include<stdio.h>
#include<time.h>
int main()
{
time_t tt;
struct tm *t;
tt=time(NULL);
t=localtime(&tt);
printf("time:%d:%d:%d:%d:%d:%d\n",t->tm_year+1900,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
return 0;
}
经过编译执行:
问题:月份比当前月份提前一个月,经查阅ctime中的tm结构体,发现月份从0开始计月。
修改参数,得到正确输出。
内嵌汇编形式调用:
#include<stdio.h>
#include<time.h>
int main(){
time_t tt;
struct tm *t;
asm volatile(
"mov $0,%%ebx;\n\t"
"mov $0xd,%%eax;\n\t"
"int $0x80;\n\t"
"mov %%eax,%0;\n\t"
:"=m"(tt)
);
t = localtime(&tt);
printf("time:%d:%d:%d:%d:%d:%d\n",t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
return 0;
}
出现段错误
经参考李明帅同学的博客在汇编代码后添加,代表“eax”“ebx”两个寄存器会被改变,最后执行顺利!
:
:"eax","ebx"
###2. 8号系统调用creat
函数原型,参数中file为文件名,__mode表示权限。
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
int creat (const char *__file, mode_t __mode)
编写creat.c
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
int main(){
char *pachname="20209326";
int mode =0777;
int fd=creat(pachname,mode);
if(fd==0)
printf("create failed!\n");
else
printf("create successful!\n");
}
经过编译执行:
内嵌汇编形式调用:
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
int main(){
char *pachname="20209326";
int mode =0777;
int fd;
asm volatile(
"movl %2,%%ecx\n\t" //将权限mode值赋值给ecx寄存器
"movl %1,%%ebx\n\t" //将文件名pachname值赋值给ebx寄存器
"movl $0x8,%%eax\n\t" //creat系统调用号为8
"int $0x80" //启动中断
:"=a"(fd) //输出成功与否
:"b"(pachname),"c"(mode) //输入文件名和权限
);
if(fd==0)
printf("create failed!\n");
else
printf("create successful!\n");
}
经过编译执行:
问题:文件的权限与输入的不一致?
经查阅博客,此处设定的mode的值并不是文件最终的权限,文件最终的权限是按照mode & ~umask得到的,所以如果想按照mode的值设置文件权限,需要在建立文件之前将umask的值设置为0000。加一句umask(0000),即可。
二、总结
1.Intel x86 CPU定义了4种不同的执行级别0、1、2、3,数字越小特权越高。Linux系统采用了其中的0、3两个特权级别,分别对应内核态和用户态。内核态下可以访问所有的地址空间,但是在用户态下只能访问0x00000000-0xbfffffff的地址空间,0xc0000000以上的地址空间只能在内核态下访问。
2.中断处理是从用户态进入内核态的主要方式。硬件中断或是由调用系统调用(Trap)引起中断,陷入内核态。从用户态切换到内核态,将用户态寄存器的上下文保存起来,同时将内核态寄存器的值放入当前CPU中。
3.Linux下系统调用通过int 0x80中断完成。int指令触发中断机制会在堆栈上保存一些寄存器的值,会保存用户态栈顶地址、当时的状态字、当时的CS:EIP的值。
4.libc函数库内部定义的一些API内部就使用了系统调用的封装历程。每个系统调用对应一个系统调用的封装例程。
5.系统调用的3层机制:xyz();system_call;sys_xyz()。
6.用户进程必须指明需要哪一个系统调用,使用EAX寄存器传递一个名为系统调用号的参数。系统调用从用户态切换到内核态时使用的不同的堆栈,所以参数的传递无法通过参数压栈的方式进行传递。参数按照顺序赋值给EBX ECX EDX ESI EDI EBP 参数的个数不能超过6个寄存器。如果参数过多,就把寄存器作为指针指向内存,以传递更多的参数。