1.使用库函数API触发一个系统调用
编写tim.c函数,使用time()获得tt变量之后,通过localtime()把tt变成struct tm这种结构 输出为可读的格式
#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: ",t->tm_year+1900,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec); return 0; }
2.C代码中嵌入汇编代码触发一个系统调用
常用的内嵌汇编常用的修饰限定符
对于下面的代码,有如下解释:
"movl $0,%%ebx "表示把EBX寄存器清零
"movl $0xd,%%eax "表示把0xd放到了EAX寄存器里面,EXA寄存器用于传递系统调用号,d在十六进制里面表示13,所以系统调用号是13
"int $0x80 "表示触发器系统调用陷入内核执行13号系统调用的内核处理函数
"movl %%eax,%0 "系统调用有一个返回值,通过EAX寄存器返回,将EAX寄存器的值放到变量tt里面
#include<stdio.h> #include<time.h> int main(){ time_t tt; struct tm *t; asm volatile( "movl $0,%%ebx " "movl $0xd,%%eax " "int $0x80 " "movl %%eax,%0 " :"=m"(tt) : :"eax","ebx" ); t=localtime(&tt); printf("time:%d:%d:%d:%d:%d:%d: ",t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec); return 0; }
3.C语言调用rename系统调用
#include<stdio.h> int main() { int ret; char *oldname = "hello.c"; char *newname = "newhello.c"; ret = rename(oldname,newname); if(ret == 0) { printf("Rnamed successfuly "); } else{ printf("Unable to rename the file "); } return 0; }
可以看到 改变后的文件夹里面已经没有hello.c 取代的则是newhello.c
4.汇编语言调用rename系统调用
#include<stdio.h> int main() { int ret; char *oldname = "hello.c"; char *newname = "newhello_20209312.c"; asm volatile( "movl %2,%%ecx " "movl %1,%%ebx " "movl $0x26,%%eax " "int $0x80" :"=a"(ret) :"b"(oldname),"c"(newname) ); if(ret == 0) { printf("Rnamed successfuly "); } else{ printf("Unable to rename the file "); } return 0; }
#include <stdio.h> int main() { char* oldname="hello.c"; char* newname="new1hello_9312.c"; int ret; asm volatile( "movl %1,%%ebx " "movl %2,%%ecx " "movl $0x26,%%eax " "int $0x80 " "movl %%eax,%0" :"=m"(ret) :"b"(oldname),"c"(newname) ); if(ret==0) printf("Rename Successfully! "); else printf("Unable to rename the file! "); return 0; }
上面的代码运行结果如下:
关于出现的两个error 处理结果是用-m32编译就好了
gcc rename_asm.c -o 20209312_test -m32
5.总结
在linux中,将程序的运行空间分为内核空间与用户空间(内核态和用户态),在逻辑上它们之间是相互隔离的,因此用户程序不能访问内核数据,也无法使用内核函数。当用户进程必须访问内核或使用某个内核函数时,就得使用系统调用(System Call)。在Linux中,系统调用是用户空间访问内核空间的唯一途径。
系统调用就是一种特殊的接口。通过这个接口,用户可以访问内核空间。系统调用规定了用户进程进入内核的具体位置。
系统调用是用户进程进入内核的接口层,它本身并非内核函数,但它是由内核函数实现,进入内核后,不同的系统调用会找到各自对应的内核函数,这些内核函数被称为系统调用的“服务例程”。比如系统调用getpid实际调用了服务例程为sys_getpid(),或者说系统调用getpid是服务例程sys_getpid()的“封装例程”。
具体步骤:用户进程-->系统调用-->内核-->返回用户空间。
系统调用就是为了解决上述问题而引入的,是提供给用户的“特殊接口”。
系统调用规定用户进程进入内核空间的具体位置。
1.程序运行空间从用户空间进入内核空间。
2.处理完后再返回用户空间。
系统调用的三层皮:API(应用程序接口),中断向量system_call,中断服务程序sys_xyz,内核实现了很多不同的系统调用,进程必须指明需要用哪个系统调用,这需要传递一些参数。其中的系统调用号,是使用eax寄存器传递。系统调用也需要输入输出参数,例如:实际的值、用户态进程地址空间的变量的地址、包含指向用户态函数的指针的数据结构的地址system_call是Linux中所有系统调用的入口点,每个系统调用至少有一个参数,即由eax传递的系统调用号。