操作系统实验——系统调用

一、什么是系统调用

  • 接口:连接两个东西、信号转换、屏蔽细节……

  • 图形界面:消息框架程序+消息处理程序。

  • 命令:一个用c语言写的程序

  • 操作系统接口:接口表示为函数调用,又由系统提供,所以称为系统调用。

  • POSIX:

    分类POSIX定义含义
    任务管理fork创建一个进程
    excel运行一个可执行程序
    pthread_create创建一个线程
    文件系统open打开一个文件或目录
    EACCES返回值,表示没有权限
    mode_t st_mode文件头结构:文件属性

二、应用程序如何调用系统调用

​ 在通常情况下,调用系统调用和调用一个普通的自定义函数在代码上并没有什么区别,到那时调用之后发生的事情有很大的不同。

  • 调用自定义函数是通过call指令直接跳转到该函数的地址,继续运行。
  • 而调用系统调用,是调用系统库中为该系统调用编写的一个接口函数,叫API(Application Programming Interface)。API并不能完成系统调用的真正功能,它要做的是去调用真正的系统调用,过程是:
    1. 应用程序调用函数库(API);
    2. API将系统调用号存入EAX,然后通过中断调用使系统进入内核态;
    3. 内核中的中断处理函数根据系统调用号,调用对应的内核函数(系统调用);
    4. 系统调用完成相应功能,将返回值存入EAX,返回到中断处理函数;
    5. 中断处理函数返回到API中。
    6. API将EAX返回给应用程序。

三、实验报告

实验目的:
  • 添加系统调用iam(),原型为:

    int iam(const char * name);
    
  • 第二个系统调用是whoami(),原型为:

    int whoami(char* name, unsigned int size);
    
实验步骤:
  1. 既然我们知道了系统调用的原型,那么就先来编写这两个函数,再~/work/oslab/linux-0.11/kernel下编写who.c程序,实现这两个函数:

    #define __LIBRARY__
    #include <unistd.h>
    #include <errno.h>
    #include <asm/segment.h>
    #include <linux/kernel.h>
    
    char string_buf[64]={0};
    
    int sys_iam(const char* name)
    {
      printk("debug:iam is called successfully\n");
      int i =0;
      char char_buf;
      while((char_buf=get_fs_byte(name+i)) != '\0' && i<63)
      {
    	string_buf[i]=char_buf;
    	i++;
      }
      string_buf[i]='\0';
      
      if(i>23)
      {
    	errno=EINVAL;
    	return -1;
      }
      else
      {
    	return i;
      }
    }
    
    int sys_whoami(char* name,unsigned int size)
    {
    	int i=0;
    	char char_buf;
    	
    	while(i < 23)
    	{
    		char_buf=string_buf[i];
    		put_fs_byte(char_buf,name+i);
    		i++;
    	}
    	
    	put_fs_byte("\0",name+i);
    	
    	if(string_buf[i] != '\0')
    	{
    	  errno = EINVAL;
    	  return -1;
    	}
    	else
    	{
    		return i;
    	}
    
    }
    
  2. 我们已经知道进系统调用时,应用程序调用的是系统函数库,所以我们需要编写关于这两个函数 的系统API。在0.11的lib目录下,已经有一些实现的API,我们可以仿照的创建iam.c:

    #define __LIBRARY__
    #include <unistd.h>
    
    _syscall1(int,iam,const char*,name);
    

    再创建whoami.c

    #define __LIBRARY__
    #include <unistd.h>
    
    _syscall2(int,whoami,char*,name,unsigned int,size);
    
  3. 添加系统调用时需要修改include/unistd.h文件,使其包含__NR_whoami__NR_iam。修改unistd.h文件,第一处修改是添加两行:

    define __NR_setregid	71
    define __NR_iam			72
    define __NR_whoami		73
    

    第二处修改是文件的最后:

    pid_t getpgrp(void);
    pid_t setsid(void);
    int iam(const char* name);
    int whoami(char* name,unsigned int size);
    #endif
    
  4. 然后再修改~/work/oslab/linux-0.11/include/linux/sys.h,添加:

    extern int sys_iam();
    extern int sys_whoami();
    

    在文件的最后添加:

    sys_setregid,sys_iam,sys_whoami
    
  5. 由于我们添加了系统调用,所以必须修改kernel/system_call.s,其中第61行中,将72改为74,代表系统调用总数。

    nr_system_calls = 74
    
  6. 修改``kernal/MakeFile`文件:

    OBJS  = sched.o system_call.o traps.o asm.o fork.o \
            panic.o printk.o vsprintf.o sys.o exit.o \
            signal.o mktime.o who.o
    
    ### Dependencies:
    who.s who.o: who.c ../include/linux/kernel.h ../include/unistd.h
    exit.s exit.o: exit.c ../include/errno.h ../include/signal.h \
      ../include/sys/types.h ../include/sys/wait.h ../include/linux/sched.h \
      ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \
      ../include/linux/kernel.h ../include/linux/tty.h ../include/termios.h \
      ../include/asm/segment.h
    
  7. 在应用程序中,必须要有:

    #define __LIBRARY__                    /* 有它,_syscall1等才有效。详见unistd.h */
    #include <unistd.h>                /* 有它,编译器才能获知自定义的系统调用的编号 */
    _syscall1(int, iam, const char*, name);        /* iam()在用户空间的接口函数 */
    _syscall2(int, whoami,char*,name,unsigned int,size);    /* whoami()在用户空间的接口函数 */
    
  8. 编写应用程序:

    • user_iam.c

      #define __LIBRARY__
      #include <unistd.h> 
      #include <errno.h>
      #include <asm/segment.h> 
      #include <linux/kernel.h>
      _syscall1(int, iam, const char*, name);
       
      int main(int argc, char *argv[])
      {
          iam(argv[1]);
       
          return 0;
      }
      
    • user_whoami.c

      #define __LIBRARY__
      #include <unistd.h> 
      #include <errno.h>
      #include <asm/segment.h> 
      #include <linux/kernel.h>
      #include <stdio.h>
       
      _syscall2(int, whoami,char *,name,unsigned int,size);
       
      int main(int argc, char *argv[])
      {
          char username[64] = {0};
          whoami(username, 24);
          printf("%s\n", username);
          return 0;
      }
      
  9. 编写测试脚本

    #!/bin/bash
    gcc use_iam.c -o iam
    gcc use_whoami.c -o whoami
    ./iam Lizhijun
    ./whoami              
    
  10. 注意:一定要将use_iam.c、use_whoami.c、和test.sh上传到linux0.11文件系统的同一文件夹下,然后执行。

注意:可能会报错如下:

在这里插入图片描述

原因是找不到系统调用好,但是明明改了的,这时需要在当前的Linux 0.11系统中,进入/usr/include,修改unistd.h,添加这两个系统调用。

运行:
在这里插入图片描述

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SPOOLING技术 一、实验目的 理解和掌握SPOOLING技术。 二、实验内容 编写程序实现SPOOLING技术的模拟。 三、实验要求 1、设计一个实现SPOOLING技术的进程 设计一个SPOOLING输出进程和两个请求输出的用户进程及一个SPOOLING输出服务程序。 SPOOLING输出进程工作时,根据请求块记录的各进程要输出的信息,将其实际输出到打印机或显示器。这里,SPOOLING进程与请求输出的用户进程可并发运行。 2、设计进程调度算法 进程调度采用随机算法,这与进程输出信息的随机性相一致。两个请求输出的用户进程的调度概率各为45%,SPOOLING输出进程为10%,这由随机数发生器产生的随机数模拟决定。 3、进程状态 进程基本状态有3种,分别为可执行、等待和结束。可执行状态就是进程正在运行或等待调度的状态;等待状态又分为等待状态1、等待状态2、等待状态3。 状态变化的条件为: (1)进程执行完成时,置为“结束”状态。 (2)服务程序在将输出信息送至输出井时,如发现输出井已满,将调用进程置为“等待状态1”。 (3)SPOOLING进程在进行输出时,若输出井空,则进入“等待状态2”。 (4)SPOOLING进程输出一个信息块后,应立即释放该信息块所占的输出井空间,并将正在等待输出的进程置为“可执行状态”。 (5)服务程序在输出信息到输出井并形成输出请求信息块后,若SPOOLING进程处于等待状态则将其置为“可执行状态”。 (6)当用户进程申请请求输出块时,若没有可用请求块时,调用进程进入“等待状态3”。 4、数据结构 1)进程控制块PCB struct pcb { int status; int length; }pcb[3]; 其中status表示进程状态,其取值: 0 表示可执行状态; 1 表示等待状态1; 2 表示等待状态2; 3 表示等待状态3 2)请求输出块reqblock struct{ int reqname;//请求进程名 int length;// 本次输出信息长度 int addr;// 信息在输出井的首地址 }reqblock[10]; 3)输出井BUFFER SPOOLING系统为每个请求输出的进程在输出井中分别开辟一个区。本实验可设计一个二维数组(int buffer[2][10])作为输出井。每个进程在输出井最多可占用10个位置。 5、编程说明 为两个请求输出的用户进程设计两个输出井。每个可存放10个信息,即buffer[2][10]。当用户进程将其所有文件输出完时,终止运行。 为简单起见,用户进程简单的设计成:每运行一次,随机输出数字0~9之间的一个数,当输入10个数时形成一个请求信息块,填入请求输出信息块reqblock结构中。
【实验目的】 1. 了解文件系统的原理; 2. 用高级语言编写和调试一个简单的文件系统,模拟文件管理的工作过程。从而对各种文件操作命令的实质内容和执行过程有比较深入的了解。 【实验准备】 1.文件的逻辑结构  顺序文件  索引文件  索引顺序文件  直接文件和哈希文件 2.外存分配方式  连续分配  链接分配  索引分配 【实验内容】 1. 实验要求 要求设计一个 n个用户的文件系统,每次用户可保存m个文件,用户在一次运行中只能打开一个文件,对文件必须设置保护措施,且至少有Create、delete、open、close、read、write等命令。 2. 实验题目  设计一个10个用户的文件系统,每次用户可保存10个文件,一次运行用户可以打开5个文件。  程序采用二级文件目录(即设置主目录[MFD])和用户文件目录(UED)。另外,为打开文件设置了运行文件目录(AFD)。  为了便于实现,对文件的读写作了简化,在执行读写命令时,只需改读写指针,并不进行实际的读写操作。 因系统小,文件目录的检索使用了简单的线性搜索。文件保护简单使用了三位保护码:允许读写执行、对应位为 1,对应位为0,则表示不允许读写、执行。程序中使用的主要设计结构如下:主文件目录和用户文件目录( MFD、UFD)打开文件目录( AFD)(即运行文件目录)。 M D F 用户名 文件目录指针 用户名 文件目录指针 U F D 文件名 保护码 文件长度 文件名 A F D 打开文件名 打开保护码 读写指针
【实验目的】 1. 通过编写和调试存储管理的模拟程序以加深对存储管理方案的理解; 2. 熟悉虚存管理的各种页面淘汰算法; 3. 通过编写和调试地址转换过程的模拟程序以加强对地址转换过程的了解。 【实验准备】 1.虚拟存储器的管理方式  段式管理  页式管理  段页式管理 2.页面置换算法  先进先出置换算法  最近最久未使用置换算法  Clock置换算法  其他置换算法 【实验内容】 1. 实验题目 设计一个请求页式存储管理方案。并编写模拟程序实现之。产生一个需要访问的指令地址流。它是一系列需要访问的指令的地址。为不失一般性,你可以适当地(用人工指定地方法或用随机数产生器)生成这个序列,使得 50%的指令是顺序执行的。25%的指令均匀地散布在前地址部分,25%的地址是均匀地散布在后地址部分。为简单起见。页面淘汰算法采用 FIFO页面淘汰算法,并且在淘汰一页时,只将该页在页表中抹去。而不再判断它是否被改写过,也不将它写回到辅存。 2. 具体做法 产生一个需要访问的指令地址流;指令合适的页面尺寸(例如以 1K或2K为1页);指定内存页表的最大长度,并对页表进行初始化;每访问一个地址时,首先要计算该地址所在的页的页号,然后查页表,判断该页是否在主存——如果该页已在主存,则打印页表情况;如果该页不在主存且页表未满,则调入一页并打印页表情况;如果该页不足主存且页表已满,则按 FIFO页面淘汰算法淘汰一页后调入所需的页,打印页表情况;逐个地址访问,直到所有地址访问完毕。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值