2020-2021-1 20209316《Linux内核原理与分析》第五周作业

2020-2021-1 20209316《Linux内核原理与分析》第五周作业

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

1、getpid()函数
(一)、调用库函数接口

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main(void)
{
    int u_id;
    u_id=getpid();
    printf("u_id=%u\n",u_id);
    return 0;
}

(二)、汇编内嵌c代码

#include<stdio.h>
#include<unistd.h>
int main(void)
{
    int u_id;
    asm volatile(
           "movl $0x14,%%eax\n\t"        /* 将系统调用号赋给eax寄存器 */
           "int  $0x80\n\t"                  /* 执行系统调用 */
           "movl %%eax, %0\n\t"             /* 将系统调用执行后的返回值赋给变量 */
           :"=m" (u_id)
           );
    printf("u_id=%u\n",u_id);
    return 0;
}

2、rename函数

含两个参数的系统调用rename

(一)、创建一个tengyuan.c文件

(二)、汇编内嵌c代码

#include <stdio.h>
int main()
{
    char* oldname="tengyuan.c";
    char* newname="tengyuan209316.c";
    int ret;
    asm volatile( 
        "movl %1,%%ebx\n\t"
        "movl %2,%%ecx\n\t" 
        "movl $0x26,%%eax\n\t"  
        "int $0x80\n\t"
        :"=a"(ret) 
        :"b"(oldname),"c"(newname)
        );
    if(ret==0)
        printf("Rename Successfully!\n");
    else
        printf("Unable to rename the file!\n");
    return 0;
}

(三)、调用库函数接口

#include<stdio.h>
int main()
{
    char *oldname = "tengyuan209316.c";
    char *newname = "tengyuan.c";
    int ret;
    ret=rename(oldname,newname);
    if(ret==0)
        printf("Rename successfully!\n");
    else
        printf("Unable to rename the file!\n");
    return 0;
}

二、学习知识

1、通过库函数的方式进行系统调用,库函数用来把系统调用给封装起来。
2、CPU有四种不同的执行级别:0、1、2、3,数字越小,特权越高。Linux操作系统中采用了0和3两个特权级别,分别对应内核态和用户态。
3、宏观上Linux操作系统的体系架构分为:用户态和内核态。
区分内核态和用户态的方法就是CS:EIP的指向范围。

  • 内核态(高指令执行级别):对所有的指令包括特权指令都可以执行,CS:EIP的值可以是任意地址。
  • 用户态(低级别指令):对于32位的4GB进程地址空间,只能访问0x00000000~0xbfffffff的地址空间。
    4、中断:从用户态进入内核态的主要方式。
    5、系统调用:为用户态进程与硬件设备进行交互提供了一组接口。
    6、系统调用的功能和特性:
  • 把用户从底层的硬件编程中解放出来;
  • 极大地提高系统安全性;
  • 使用户程序具有可移植性。
    7、API和系统调用的关系:
    系统调用通过软中断向内核发出了中断请求,int指令的执行就会触发一个中断请求。
    libc函数库定义的一些API内部使用了系统调用的封装例程,其主要目的是发布系统调用,使程序员在写代码时不需要用汇编指令和寄存器传递参数来触发系统调用。一个API可能只对应一个系统调用,也可能内部由多个系统调用实现;一个系统调用也可能被多个API调用。涉及与内核空间进行交互的API内部会封装系统调用,不涉及与内核空间进行交互的API内部不会封装系统调用。如果内核增加了一个新的系统调用,但libc函数库没有及时更新为其编写API函数,则可以利用libc提供的syscall函数直接调用。
    8、系统调用的三层机制:xyz(),system_call和sys_xyz()
    9、内核如何知道用户态进程希望调用的是哪个系统调用?
    内核通过给每个系统调用一个编号来区分,即系统调用号,将API函数xyz()和系统调用内核函数sys_xyz()关联起来,用EAX寄存器传递系统调用号参数。

三、反思总结

本章主要学习了系统调用和库函数。一个API可能只对应一个系统调用,也可能内部由多个系统调用实现,一个系统调用也可能被多个API调用。
EAX用于传递系统调用号。传递参数时参数按顺序赋值给EBX、ECX、EDX、ESI、EDI、EBP。如果参数超过6个,就把某一个寄存器作为指针指向内存,这样可以通过内存来传递更多的参数。应用程序系统调用(API)和系统调用不同的API知识函数定义。系统调用是通过软中断向内核发出中断请求。
受益匪浅。

posted @ 2020-11-06 01:12  20209316滕源  阅读(116)  评论(0编辑  收藏  举报