操作系统课程设计实验报告

        河北大学工商学院

课程设计

    题目    操作系统课程设计        

学    部     信息学部          

学科门类     电气信息          

专    业     计算机            

学    号     2011482370         

姓    名     耿雪涛            

指导教师     朱亮               

            

  20## 年 6月19日

主要内容

一、设计目的

通过模拟操作系统的实现,加深对操作系统工作原理理解,进一步了解操作系统的实现方法,并可练习合作完成系统的团队精神和提高程序设计能力。

二、设计思想

实现一个模拟操作系统,使用VB、VC、CB等windows环境下的程序设计语言,以借助这些语言环境来模拟硬件的一些并行工作。模拟采用多道程序设计方法的单用户操作系统,该操作系统包括进程管理、存储管理、设备管理、文件管理和用户接口四部分。

设计模板如下图:

注:本人主要涉及设备管理模块

三、设计要求

设备管理主要包括设备的分配和回收。

⑴模拟系统中有A、B、C三种独占型设备,A设备1个,B设备2个,C设备2个。

⑵采用死锁的预防方法来处理申请独占设备可能造成的死锁。

⑶屏幕显示

注:屏幕显示要求包括:每个设备是否被使用,哪个进程在使用该设备,哪些进程在等待使用该设备。

设备管理模块详细设计

一、设备管理的任务

I/O设备是按照用户的请求,控制设备的各种操作,用于完成I/O设备与内存之间的数据交换(包括设备的分配与回收,设备的驱动管理等),最终完成用户的I/O请求,并且I/O设备为用户提供了使用外部设备的接口,可以满足用户的需求。

二、设备管理函数的详细描述

1、检查设备是否可用(主要代码)

  public bool JudgeDevice(DeviceType type)

        {

            bool str = false;

            switch (type)

            {

                case DeviceType.a:

                    {

                        if (table[0].total > 0)

                        {

                            str = true;

                        }

                        break;

                    }

                case DeviceType.b:

                    {

                        if (table[1].total > 0)

                        {

                            str = true;

                        }

                        break;

                    }

                case DeviceType.c:

                    {

                        if (table[2].total > 0)

                        {

                            str = true;

                        }

                        break;

                    }

            }

            return str;

        }

注意:只有当设备处于可用状态时,那么用户发出的I/O请求才可以及时给出回应,如果在用户发出请求的时候,当前没有空闲的设备,那么用户只能等待,只有当设备回收以后有空闲设备的时候才可以继续完成用户的I/O请求,因此,这一部分相当重要。

2、设备的分配(主要代码)

 public int Allocate(DeviceType type)

        {

            int k = 0;

            switch (type)

            {

                case DeviceType.a:

                    {

                        table[0].total--;

                        for (int i = 0; i < 3; i++)

                        {

                            if (table[0].useState[i] == 0)

                            {

                                table[0].useState[i] = 1;

                                k = i;

                                break;

                            }

                        }

                        break;

                    }

                case DeviceType.b:

                    {

                        table[1].total--;

                        for (int i = 0; i < 2; i++)

                        {

                            if (table[0].useState[i] == 0)

                            {

                                table[0].useState[i] = 1;

                                k = i;

                                break;

                            }

                        }

                        break;

                    }

                case DeviceType.c:

                    {

                        table[2].total--;

                        break;

                    }

            }

            return k;

        }

这一部分定义了三类共六个设备,分别是A类型的三个,B类型的两个,C类型的一个。

3、设备的回收(主要代码)

 public void DeAllocate(DeviceType type, int a)

        {

            switch (type)

            {

                case DeviceType.a:

                    {

                        table[0].total++;

                        table[0].useState[a] = 0;                      

                        break;

                    }

                case DeviceType.b:

                    {

                        table[1].total++;

                        table[1].useState[a] = 0;

                        break;

                    }

                case DeviceType.c:

                    {

                        table[2].total++;

                        table[2].useState[a] = 0;

                        break;

                    }

            }

        }

    }

每次占用完了某一个或多个I/O设备,都要对其进行设备的回收,以便后续使用。

三、屏幕显示

1、当刚刚开机后,没有任何操作,那么此时的屏幕显示为下图:

注:模拟操作系统分为进程管理、文件管理、内存管理、设备管理四个主要部分。进程管理分别为就绪队列和阻塞队列,来显示进程名、优先级、以及阻塞原因;文件管理包含命令接口和计算机磁盘(C、D磁盘),绿色表示系统使用的磁盘,深蓝表示已使用的内存,灰色表示还未使用,有三个相关进程(111.exe、aaa.exe、ddd.exe);内存管理有32个panel,蓝色表示空闲块,红色表示占用块,黄色表示运行块;设备管理包6个设备,初始状态为空闲,显示使用的进程和时间。 此时磁盘中没有任何文件,用户没有任何请求,所以I/O设备全部出于空闲状态,所有设备未被占用,此时内存中也没有任何进程数据,当然,cpu也处于空闲状态。

2、运行一个进程:

111.exe进程占用四个panel,从第二个块开始运行,占用A1设备,进入阻塞状态,等待IO输入输出,当前进程为闲逛进程;运行第三个块,开始执行111.exe进程,文件内容为:

i++ i++;i++;i++;!a8;i++;i++;i++;i++;!c5;i++;i++;end;

运行第四个块,进入到阻塞状态,然后等待IO的输入和输出;

执行结束以后,系统释放内存。

3、同时运行多个进程

此时,111.exe进程占用设备A1,aaa.exe进程占用设备A2,他们在等待I/O输入输出。

四、I/O设备使用说明

在进程创建后,系统就为该进程分配了它所需要的各种资源,包括有些进程需要占用I/O设备进行输入输出,当用户进程发出I/O请求时,如果此时有空闲的设备,那么进程就可以直接占用该设备,直到该进程运行完释放所有的资源为止。如果所有的设备都被占用,此时还有进程进行I/O请求时,那么此进程就会等待资源的分配。

                             

总 结

这次的课程设计我们是四个人组成一个小组,每个人负责一个模块。当然,无论是哪一个模块,我们都先商量好怎么做,之后我们单独进行自己模块的编写,最后我们四个人将程序归并到一起,完成了此次操作系统的课程设计。在这次实践中,我感受到了一个团队团结起来做事是多么的强大,在一个好的团队了,分工明确,大家相互交流,做出的事就会更好。

课程设计是我们专业课程知识综合应用的实践训练,这是我们迈向社会,从事职业工作前一个必不少的过程.”千里之行始于足下”,通过这次课程设计,我深深体会到这句千古名言的真正含义.

本模拟操作系统由四人组合完成:

李帮(进程管理模块)、耿雪涛(设备管理模块)

杨新宇(存储管理模块)、郁海涛(文件管理模块)

 

第二篇:嵌入式操作系统课程设计实验报告

华中科技大学

嵌入式操作系统课程设计实验报告

 

院    系:     计算机科学与技术学院

专    业:           物联网       

班    级:           1102        

姓    名:           吴斌        

           报告时间:        20##-06-25    

计算机科学与技术学院

                            目 录

1.课程设计目的…………………………………………………………3

2.课程设计环境搭建……………………………………………………3

3.内容一:熟悉和理解Linux编程环境

   (1)内容要求……………………………………………………………………4

    (2)计过程及实现………………………………………………………………4

4.内容三:掌握添加设备驱动程序的方法

   (1) 内容要求 …………………………………………………………………7

   (2) 设计过程及实现 …………………………………………………………7

5.课设感想 ………………………………………………………………………12

 

 

 

 

实验内容

1 .课程设计目的

(1)掌握Linux操作系统的使用方法;

(2)了解Linux系统内核代码结构;

(3)掌握实例操作系统的实现方法。

2 .课程设计环境搭建

(1)安装linux

(2)更改root登录:

    在现阶段Ubuntu的系统中,是不允许直接以root身份登录系统的,但是在做课设的过程中,需要大量的使用root权限来进行命令的操作。如果以普通用户登录ubuntu,会连编辑一个文件都非常周折。为此,我找到了一种修改系统文件,以达到直接使用root身份登录的方法:

    ◎开始的时候,只能以普通用户登录,用Ctrl+Alt+T打开终端:

    初始化/修改root密码

           sudo passwd root       

    用vi编辑器修改这个文件:

           sudo vi /etc/lightdm/lightdm.conf

    在文件最后加入这么一行代码:

           greeter-show-manual-login=true

    然后保存退出,sudo reboot 重启系统。之后就可以输入root用户登录。

(3)在添加系统调用中用到的其他内核包:

    ◎下载和当前实验环境最为接近的系统版本(这点很重要)

       使用apt-get install linux-source-3.0.0 命令,

    ◎下载结果是linux-source-3.0.0.tar.bz2

    ◎解压命令:tar –xjvf linux-source-3.0.0.tar.bz2 –C /usr/src

    ◎解压后,在/usr/src目录下得到内核文件夹linux-source-3.0.0

(4)在调用linux图形库时需要安装GTK环境:

    ◎安装gcc/g++/gdb/make 等基本编程工具
           apt-get install build-essential

    Tip:如果提示由于依赖项不能安装,需要使用apt的强化版aptitude,这个工具可以自动分析软件包依赖,系统一般不自带,需要先安装,具体过程是:

           apt-get install aptitude

           aptitude install build-essential

    aptitude这个工具很强大,对于解决软件包安装时的依赖问题很有帮助。

    ◎安装 libgtk2.0-dev libglib2.0-dev 等开发相关的库文件:

           apt-get install gnome-core-devel

    ◎安装GTK核心组件:
           apt-get install libgtk2.0-dev

       这个安装完成后,GTK环境就基本搭建成功,网上有些教程说要安装其他配置文件,经我亲测,发现只要安装libgtk2.0-dev这个包就能搞定。

3.熟悉和理解Linux编程环境

(1)实验内容:实现三个进程之间的并发程序:

(2)计过程及实现:这里需要用到课程实验时的fork( )程序以及GTK的图形显示。

  ①基本fork()程序,调用显三个进程的结构如下:

   #include <stdio.h>

   #include <unistd.h>

   #include <sys/types.h>

   #include <sys/wait.h>

   void main(int argc,char argv[]){

   pid_t p1,p2,p3;

   pid_t t1,t2,t3;

   int status;

  if ((p1=fork()) == 0){/*创建第一个子进程*/

    execv("./pa",argv);

  }

  else if ((p2=fork()) == 0) {/*创建第二个子进程*/

    execv("./pb",argv);

  }

  else if((p3=fork()) == 0){/*创建第三个子进程*/

    execv("./pc",argv);    

  }

  else{

  t1=waitpid(p1,&status,0);

  t2=waitpid(p2,&status,0);

  t3=waitpid(p3,&status,0);

  }

  }

②调用GTK显示窗体函数模块的结构:

    void show(int argc,char *argv[ ],char *title )  {

    gtk_init (&argc, &argv);                  //初始化工具包并且获取命令行参数;

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL); //创建新的窗口;

       //设定窗口的位置;

    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

   //监听窗口的destroy事件;

g_signal_connect (G_OBJECT (window),

                     "destroy", G_CALLBACK (destroy_progress), NULL);

    gtk_window_set_title (GTK_WINDOW (window), title);//用来设定更改窗口标题;

    gtk_container_set_border_width (GTK_CONTAINER (window), 20);//设定宽度;

       //使用gtk_vbox_new函数建立纵向组装盒;

       //为了显示构件,必须将构件放入组装盒中,并将组装盒放在窗口内;

    vbox = gtk_vbox_new (FALSE, 10);

    gtk_container_set_border_width (GTK_CONTAINER (vbox), 100);//设定宽度;

    gtk_container_add (GTK_CONTAINER (window), vbox);

    gtk_widget_show (vbox);

  

       //使用gtk_box_pack_start函数将构件放到组装盒中;

    sprintf (id_char, "%s ,My ID:%d", title,getpid ());            //显示PID号

    label = gtk_label_new (id_char);

    gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 10);

    gtk_widget_show (label);

   

    sprintf (id_char, "父进程ID:%d", getppid ());                 //显示PPID号

    label = gtk_label_new (id_char);

    gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 10);

    gtk_widget_show (label);

    button = gtk_button_new_with_label ("close");                  //关闭窗口按钮

   

   //信号登记函数,监听按钮的clicked事件。

   //当窗口clicked时, gtk_widget_destroy 就会被调用。

       //而 gtk_widget_destroy 函数又调用 gtk_main_quit() 结束程序运行。

    g_signal_connect_swapped (G_OBJECT (button), "clicked", G_CALLBACK (gtk_widget_destroy), window);

    gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 10);

    GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);

    gtk_widget_grab_default (button);

       //函数显示窗口中的组件

    gtk_widget_show (button);

    gtk_widget_show (window);

       //准备将窗口和所有的组件显示在屏幕上,函数必须在GTK程序的最后调用.

    gtk_main ();

}

  ③编译代码main.c,运行;

    编译命令为:gcc -o a main.c `pkg-config --cflags --libs gtk+-2.0`

    程序运行结果如下图3-3所示:

4.内容三:掌握添加设备驱动程序的方法

1 内容要求

(1)采用模块方法,添加一个新的设备驱动程序。

(2)要求添加字符设备的驱动。

(3)编写一个应用程序,测试添加的驱动程序

2 设计过程及实现

(1)Linux内核中的设备驱动程序是一组常驻内存的具有特权的共享库,是低级硬件处理例程。对用户程序而言,设备驱动程序隐藏了设备的具体细节,对各种不同设备提供了一致的接口,一般来说是把设备映射为一个特殊的设备文件,用户程序可以象对其它文件一样对此设备文件进行操作。

    Linux支持3种设备:字符设备、块设备和网络设备。

    设备由一个主设备号和一个次设备号标识。主设备号唯一标识了设备类型,即设备驱动程序类型,它是块设备表或字符设备表中设备表项的索引。次设备号仅由设备驱动程序解释,一般用于识别在若干可能的硬件设备中,I/O请求所涉及到的那个设备。

    一个典型的驱动程序,大体上可以分为这么几个部分:

    ①注册设备:

    在系统初启,或者模块加载时候,必须将设备登记到相应的设备数组,并返回设备的主设备号;

    ②定义功能函数:

    对于每一个驱动函数来说,都有一些和此设备密切相关的功能函数。以最常用的块设备或者字符设备来说,都存在着诸如 open()、read()这一类的操作。当系统调用这些调用时,将自动的使用驱动函数中特定的模块。来实现具体的操作;

    ③卸载设备:

    在不用这个设备时,可以将它卸载,主要是从/proc 中取消这个设备的特殊文件。 

(2)编写Makefile文件如下:

# If KERNELRELEASE is defined, we've been invoked from the

# kernel build system and can use its language.

ifeq ($(KERNELRELEASE),)

    # Assume the source tree is where the running kernel was built

    # You should set KERNELDIR in the environment if it's elsewhere

    KERNELDIR ?= /lib/modules/$(shell uname -r)/build

    # The current directory is passed to sub-makes as argument

    PWD := $(shell pwd)

modules:

  $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:

  $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:

  rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

.PHONY: modules modules_install clean

else

    # called from kernel build system: just declare what our modules are

    obj-m := devDrv.o

endif

      调用Makefile文件之后,其具体过程如下:

      ①KERNELRELEASE是在内核源码的顶层Makefile中定义的一个变量,在第一次读取执行此Makefile时,KERNELRELEASE没有被定义,所以make将读取执行else之后的内容;

      ②如果make的目标是clean,直接执行clean操作,然后结束。

    ③当make的目标为all时,-C $(KDIR)指明跳转到内核源码目录下读取那里的Makefile;M=$(PWD) 表明然后返回到当前目录继续读入、执行当前的Makefile。

      ④当从内核源码目录返回时,KERNELRELEASE已被定义,内核的build程序Kbuild也被启动去解析kbuild语法的语句,make将继续读取else之前的内容。     ⑤else之前的内容为kbuild语法的语句,指明模块源码中各文件的依赖关系,以及要生成的目标模块名

(3)编写设备功能函数:(devDrv.mod.c)

      函数框架如下所示:

#include <linux/module.h>

#include <linux/vermagic.h>

#include <linux/compiler.h>

MODULE_INFO(vermagic, VERMAGIC_STRING);

__visible struct module __this_module

__attribute__((section(".gnu.linkonce.this_module"))) = {

    .name = KBUILD_MODNAME,

    .init = init_module,

#ifdef CONFIG_MODULE_UNLOAD

    .exit = cleanup_module,

#endif

    .arch = MODULE_ARCH_INIT,

};

static const struct modversion_info ____versions[]

__used

__attribute__((section("__versions"))) = {

    { 0x59caa4c3, __VMLINUX_SYMBOL_STR(module_layout) },

    { 0x6bc3fbc0, __VMLINUX_SYMBOL_STR(__unregister_chrdev) },

    { 0xbb2c2e33, __VMLINUX_SYMBOL_STR(__register_chrdev) },

    { 0x839fb39, __VMLINUX_SYMBOL_STR(try_module_get) },

    { 0x4f8b5ddb, __VMLINUX_SYMBOL_STR(_copy_to_user) },

    { 0x4f6b400b, __VMLINUX_SYMBOL_STR(_copy_from_user) },

    { 0x6d9da20b, __VMLINUX_SYMBOL_STR(module_put) },

    { 0x27e1a049, __VMLINUX_SYMBOL_STR(printk) },

    { 0xbdfb6dbb, __VMLINUX_SYMBOL_STR(__fentry__) },

};

static const char __module_depends[]

__used

__attribute__((section(".modinfo"))) =

"depends=";

MODULE_INFO(srcversion, "EC04EE455AF92F2C3E445E0");

(4)设备加载,安装过程如下:

      ①进入Makefile文件和devDrv.mod.c文件所在目录,清除make产生的残留文件。命令为:

           make  clean

    ②删除先前可能加载过的模块,命令为:

           rmmod  /dev/devDrv  

      ③卸载设备:

           rm  /dev/devDrv

      ④编译设备文件,产生模块文件

           make

      ⑤加载模块

           sudo insmod  devDrv.ko

     ⑥ 查询设备号

           cat /proc/devices

     截图:

     

      ⑦加载设备,分配设别号

            sudo mknod /dev/test c 250 0

      ⑧更改用户对设备的操作权限为可读、可写

            sudo chmod 666 /dev/test

(5)测试结果:

       执行sa文件

       

运行test这个设备

输入123451234512345后的结果

5.课设感想

课程设计是培养学生综合运用所学知识,发现,提出,分析和解决实际问题,锻炼实践能力的重要环节,是对学生实际工作能力的具体训练和考察过程。这对我们这些学习有关网络以及计算机专业的学生来说既是一种挑战,又是一个难得的机会。通过这次嵌入式的课程设计让我们对linux系统有了更深的了解和应用。

实验一,这次是我的一次使用gtk这个编程环境,所以刚开始我上网查询了很多也学习了很多,终于在同学的帮助下,完成了相关代码的编写。

实验三,总体上来说,相对比较简单,编程简单,代码容易写。但是拿到题目的时候,却无从下手,不知道是干什么用的,怎么处理。通过在网上查阅相应资料,跟同学讨论,明白了编写字符设备驱动程序,其实格式是固定的,只需要写几个简单函数就可以了。 

一开始,不是很明白每个函数的作用,从网上下载的代码,看的也不是很明白,运行那些代码,发现可以正确运行。 

后来,经过仔细分析,研究,知道了每个函数的功能,就按照模板,编写自己的字符设备驱动程序。并且取得了成功。

相关推荐