操作系统课程设计报告范例

操作系统课程论文

院    系:  计算机学院 

班    级:  20##计算机科学技术1   

姓    名:    曹作西    

学    号:    2007123456   

指导教师:    xxx    

完成时间:    2010.01     

东莞理工学院

摘   要

本文分析面向对象教学操作系统EOS的系统结构和代码构成,通过源代码分析学习该系统的进程有关数据结构,掌握其进程创建过程、线程创建过程和上下文切换方法,理解其进程管理的机理,学习其面向对象实现机制,锻炼大型软件源代码阅读技能,以加深对操作系统原理知识点的理解和掌握,了解系统程序的实现方法,学习EOS面向对象程序设计技术。

一、  课程设计任务和分工

本次课程设计的任务是从进程管理代码的阅读与改进(包括进程创建、进程同步和进程调度四个实验)、设备驱动代码的阅读与改进、内存管理代码的阅读与改进、系统启动代码的阅读与改进、文件系统代码的阅读与改进等五个题目中任选一个,分组完成,每组人数为1—5人。课程设计内容是对代码通过阅读源代码,理解EOS操作系统的结构、设计技术,分析其执行流程,并对其功能进行改进和增强,以达到更好地掌握操作系统原理与技术的目的。

       本文分工是阅读EOS进程创建过程,并画出系统数据结构和主要流程图。

二、实现原理和数据结构

1.实验原理

       阅读EOS进程创建源代码,通过EOS实验环境的程序调试过程理解其执行流程,在此基础上画出EOS进程创建的源代码。

2.数据结构所在源文件

ki.h:内核支撑模块的内部函数及变量的声明

mi386.h:i386 内存相关。

obp.h: 对象管理模块的内部头文件。

psp.h:进程管理模块的内部头文件。

kdb.h: 内核调试器接口声明。

ke.h:系统支撑模块对外接口的头文件,供内核其它模块使用。

3.数据结构名称和位置

进程对象结构体:PROCESS,psp.h

线程对象结构体:THREAD,ps.h

对象类型结构体:OBJECT_TYPE, obp.h

对象头结构体:OBJECT_HEADER, obp.h

上下文环境结构体:CONTEXT

4. 数据结构

1)进程结构体

typedef struct _PROCESS {

       BOOLEAN System; // 是否系统进程

       UCHAR Priority;     // 进程的优先级

       PMMPAS Pas;  // 进程地址空间

       PHANDLE_TABLE ObjectTable;进程的的内核对象句柄表

       LIST_ENTRY ThreadListHead; // 进程线程链表头

       PTHREAD PrimaryThread; // 主线程指针

       LIST_ENTRY WaitListHead;

// 等待队列,所有等待进程结束的线程都在此队列等待。

       PSTR ImageName;  // 二进制映像文件名称

       PSTR CmdLine; // 命令行参数

       PVOID ImageBase; // 可执行映像的加载基址

       PPROCESS_START_ROUTINE ImageEntry;   

// 可执行映像的入口地址

       HANDLE StdInput;

       HANDLE StdOutput;

       HANDLE StdError;

       ULONG ExitCode;  // 进程退出码

} PROCESS;

2)线程结构体

typedef struct _THREAD {

PPROCESS Process; // 线程所属进程指针

LIST_ENTRY ThreadListEntry; // 进程的线程链表项

UCHAR Priority; // 线程优先级

UCHAR State; // 线程当前状态

ULONG RemainderTicks; // 剩余时间片,用于时间片轮转调度

STATUS WaitStatus; // 阻塞等待的结果状态

KTIMER WaitTimer; // 用于有限等待唤醒的计时器

LIST_ENTRY StateListEntry;    // 所在状态队列的链表项

LIST_ENTRY WaitListHead;

// 所有等待线程结束的线程都在此队列等待。

为了结构简单,EOS没有对内核进行隔离保护,所有线程都运行在内核状态,所以目前线程没有用户空间栈//

PVOID KernelStack; // 线程位于内核空间的栈

CONTEXT KernelContext; // 线程执行在内核状态的上下文环境状态

PMMPAS AttachedPas; // 线程在执行内核代码时绑定进程地址空间。

PTHREAD_START_ROUTINE StartAddr; // 线程的入口函数地址

PVOID Parameter; // 传递给入口函数的参数

ULONG LastError; // 线程最近一次的错误码

ULONG ExitCode; // 线程的退出码

} THREAD;

4对象头结构体

typedef struct _OBJECT_HEADER {

PSTR Name; // 对象名称字符串指针

ULONG Id; // 对象ID,全局唯一

POBJECT_TYPE Type; // 对象类型指针

ULONG PointerCount; // 对象指针计数器

ULONG HandleCount; // 对象句柄计数器

LIST_ENTRY TypeObjectListEntry;             // 所属类型的对象链表项

LIST_ENTRY GlobalObjectListEntry;   // 全局的对象链表项

QUAD Body;     // 对象体的起始域

} OBJECT_HEADER;

4)对象类型结构体

typedef struct _OBJECT_TYPE{

PSTR Name; // 类型名称字符串指针

SINGLE_LIST_ENTRY TypeListEntry;

LIST_ENTRY ObjectListHead; 

ULONG ObjectCount; // 对象计数器,所有属于此类型的对象的总数

ULONG HandleCount;

} OBJECT_TYPE;

5EOS对象结构

三、各函数详细说明及程序流程图

1.进程创建应用程序测试函数(newproc.c

2. 创建进程函数:CreateProcess("A:\\Hello.exe",

                  NULL, 0, &StartupInfo, &ProcInfo)

功能:产生PROCESSTHREAD对象

3. 创建进程运行环境:

PspCreateProcessEnvironment

(8, ImageName, CmdLine,

&ProcessObject)

4. 创建系统进程PsCreateSystemProcess()

(1)创建进程环境PspCreateProcessEnvironment

过程:

Þ  创建进程对象ObCreateObject:对象类型PspProcessType

Ý  计算对象头及对象体存储需求及分配存储器

Ý  初始化对象头,type= PspProcessType,将对象头插入对象链表及全局对象链表

Ý  返回对象体指针

Þ  为进程分配句柄表

Þ  初始化进程控制块:优先级:24exit_code:0

Þ  返回变量值: PspSystemProcess

(2)创建进程的主线程  PspCreateThread

设置:PspSystemProcess->PrimaryThread

参数:进程PspSystemProcess,启动地址: KiSystemProcessRoutine

过程:

Þ  创建线程对象ObCreateObject ,类型PspThreadType ,大小:THREAD大小

Ý  计算对象头及对象体存储需求并分配内存

Ý  初始化对象头,type=PspThreadType, 对象头插入对象链表及全局对象链表

Ý  返回对象体指针

Þ  栈大小8KB

Þ  初始化线程控制块:process= PspSystemProcess, priority,  startaddr, state=0, RemainderTicks =6

Þ  初始化线程上下文:PspInitializeThreadContext  

Ý  初始化所有通用寄存器为0,设置段寄存器

Ý 

Ý  有线程都从线程启动函数PspThreadStartup()开始执行

Ý  初始化内核模式栈;初始化程序状态字,仅仅设置了一位:允许中断

Þ  使线程进入就绪状态。

Þ  执行调度:KeSchedule ()

Þ  线程启动函数PspThreadStartup()

Ý  PspCurrentThread->StartAddr(PspCurrentThread->Parameter)

Ý  PsExitThread(ExitCode);

四、功能改进和增强

五、运行结果分析

       多次调用CreateProcess函数创建Hello.exe应用程序的多个进程

六、结论和心得体会

     

六、参考文献

1. 北京海西慧学科技有限公司. EOS实验指南. 2009.

2. 北京海西慧学科技有限公司. EOS应用指南. 2009.

3. 汤晓丹. 现代操作系统. 电子工业出版社. 2008.

 

第二篇:操作系统课程设计报告upload

一、 课程设计目的

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

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

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

二、 课程设计要求

1、 掌握Linux操作系统的使用方法,包括键盘命令、系统调用;掌握在

Linux下的编程环境。

? 编一个C程序,其内容为实现文件拷贝的功能;

? 编一个C程序,其内容为分窗口同时显示三个并发进程的运行结果。要求用到Linux下的图形库。

掌握系统调用的实现过程,通过编译内核方法,增加一个新的系统调用。另编写一个应用程序,调用新增加的系统调用。

实现的功能是:文件拷贝;

掌握增加设备驱动程序的方法。通过模块方法,增加一个新的设备驱动程序,其功能可以简单。

实现字符设备的驱动;

了解和掌握/proc文件系统的特点和使用方法

? 了解/proc文件的特点和使用方法

? 监控系统状态,显示系统中若干部件使用情况

? 用图形界面实现系统监控状态。

设计并实现一个模拟的文件系统(选作)

2、 3、 4、 5、

三、 课程设计系统平台

操作系统课程设计报告upload

操作系统课程设计报告upload

四、 课程设计内容一

1、 编一个C程序,其内容为实现文件拷贝的功能

要实现文件拷贝功能,主要用到的函数是open、write、read。 以前在windows下写C语言打开文件常用的fopen,此时不能用,因为fopen是ANSIC标准中的C语言库函数,在不同的系统中应该调用不同的内核api ;所以应该直接使用linux中的系统函数open。

主要用到的头文件:

Unistd.h \\包含了许多Linux系统服务的函数原型,如:read、write Fcntl.h \\定义了很多宏和open,fcntl函数原型

Stdio.h \\标准输入输出头文件

sys/types.h \\此头文件包含适当时应使用的多个基本派生类型

sys/stat.h \\包含了获取文件属性的一些函数

errno.h \\用于调试错误代码是所需要的一些errno变量

string.h \\包含了处理字符串的一些函数

设计思路:由命令行参数获取2个文件名,根据其文件名和路径分别打开该2个文件,设置一个循环,从源文件复制N个字节到目的文件,直到源文件指针到文件尾,最后关闭2个文件。

在可能出错的地方需要加上相应的报错代码和中断,并输出错误信息,以方便调试或是往后应用在第2小题中可能发生的错误。

理清楚设计思路后,根据需求写出相应的源代码见后页源程序代码scopy.c ;在Linux终端使用编译命令 gcc –o scopy scopy.c将程序编译并生产exe可执行文件。

然后手动创建一个测试文件test.txt ,在终端输入命令

./scopy test.txt target.txt

这样就能将源文件test.txt复制到目标文件target.txt

程序源代码 scopy.c:

#include <unistd.h>

#include <fcntl.h>

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <errno.h>

#include <string.h>

#define BUFFER_SIZE 1024 //缓冲区大小

int main(int argc,char **argv)

{

int from_fd,to_fd;

int bytes_read,bytes_write;

char buffer[BUFFER_SIZE]; //设定一个缓冲区

char *ptr;

if(argc!=3) //三个参数

{

fprintf(stderr,"Usage:%s fromfile tofile\n\a",argv[0]); return(-1);

}

/* 打开源文件 */

if((from_fd=open(argv[1],O_RDONLY))==-1)

{

fprintf(stderr,"Open %s Error:%s\n",argv[1],strerror(errno)); return(-1);

}

/* 创建目的文件 */

if((to_fd=open(argv[2],O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))==-1) {

fprintf(stderr,"Open %s Error:%s\n",argv[2],strerror(errno)); return(-1);

}

while(bytes_read=read(from_fd,buffer,BUFFER_SIZE))

{

/* 一个致命的错误发生了 */

if((bytes_read==-1)&&(errno!=EINTR)) break; else if(bytes_read>0)

{

ptr=buffer;

while(bytes_write=write(to_fd,ptr,bytes_read)) {

/* 一个致命错误发生了 */

if((bytes_write==-1)&&(errno!=EINTR))break;

/* 写完了所有读的字节 */

else if(bytes_write==bytes_read) break; /* 只写了一部分,继续写 */ else if(bytes_write>0)

{

ptr+=bytes_write;

bytes_read-=bytes_write; }

}

/* 写的时候发生的致命错误 */

if(bytes_write==-1)break;

}

}

close(from_fd);

close(to_fd);

return(1);

}

2、 编一个C程序,其内容为分窗口同时显示三个并发进程的运行结果。要求用到Linux下的图形库。 本次我选用的图形库是GTK+ 首先要在Linux下载GTK+相关库文件并安装。 在终端输入sudo apt-get install gnome-core-devel ,然后根据提示操作,就会安装 libgtk2.0-dev libglib2.0-dev 等开发所需的相关库文件。 编译GTK+代码时需要包含的头文件是gtk/gtk.h,此外,还必须连接若干库;比如编译test.c时用以下命令。 gcc –o test test.c `pkg-config --cflags --libs gtk+-2.0` 在编写代码时需要用到的控件、窗口等视窗物件形态,用类GtkWidget定义其为指针类型。 编写一个GTK+程序的基本步骤如下: ? 初始化Gtk ? 建立控件 ? 登记消息与消息处理函数 ? 执行消息循环函数gtk_main() 之后所设计的3个进程,基本上都是以这样的方式编写代码的,因为之前曾用过OpenGL,所以在这方面掌握的比较快。 初始化主要使用的函数有

gtk_init(&argc,&argv); //启动GTK

gtk_window_new(GTK_WINDOW_TOPLEVEL); //创建窗口 gtk_window_set_title(GTK_WINDOW(window),"标题名"); //设置窗口标题名

gtk_widget_set_usize(window, 200, 200); //设置窗口大小 gtk_widget_show(window); //显示窗口 建立控件的一般流程 /*创建表格准备封装*/ gtk_table_new ( //创建多少列

gint rows, //创建多少栏

gint columns, //用来决定表格如何来定大小

gint homogeneous);

/*这个函数是将表格table,结合到窗口window里*/

gtk_container_add(GTK_CONTAINER(window),table);

gtk_widget_show(table);

// 显示该表格

/*要把物件放进box中,可用以下函数*/

void gtk_table_attach_defaults (

GtkTable*table,

GtkWidget*widget,

gintleft_attach,

gintright_attach,

ginttop_attach,

gintbottom_attach);

//参数("table")是选定某表格 //("child")是想放进去的物件 //以下参数是指定把物件放在哪里, 及用多少个boxes

本次我所设计的3个进程主要使用了下列控件:进度条、按钮、文本框、滚动条,分别用在3个进程里。

进度条函数:

progress_bar=gtk_progress_bar_new(); //创建进度条

按钮函数:

gtk_button_new_with_label(“LABEL”); //创建带有"LABEL"的字在上面的按钮

文本框:

gtk_text_new(NULL,NULL); //创建文本构件

滚动条:

gtk_vscrollbar_new(GTK_TEXT(text)->vadj); //创建滚动条并设置其与文本同步

其他函数:

gint gtk_timeout_add (

guint32 interval,

GtkFunction function,

gpointer data);

//每间隔interval 毫秒呼叫一次指定函数 //被呼叫的函数 //要传给该函数的资料

gtk_signal_connect (

GtkObject *object,

gchar *name,

GtkSignalFunc func,

gpointer func_data); //送出信号的物件 //希望接取的信号名称 //送给该函数的资料

根据上述重要的函数分别编写4个源代码,分别编译后,输入./Stest运行Stest结果如截图所示:

下面附上4个进程的源代码:

1) Stest.c

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>

#include <sys/types.h>

#include <linux/sem.h>

int semid;

char *finish;

int p1,p2;

int main (void){

if((p1=fork())==0){//创建新进程

操作系统课程设计报告upload

execv("./S1",NULL);

}

else{

if((p2=fork())==0){

execv("./S2",NULL);

}

else{

execv("./S3",NULL);

}

}

return 0;

}

2) S1.c

#include <gtk/gtk.h>

#include<string.h>

GtkWidget *window;

GtkWidget *table;

GtkWidget *button;

GtkWidget *progress_bar;

// 更新进度条,这样就能够看到进度条的移动

gint progress_timeout( gpointer data ){

gdouble value;

int v;

char text[20]="0%";

//使用在调整对象中设置的取值范围计算进度条的值

value=gtk_progress_bar_get_fraction(GTK_PROGRESS_BAR

(progress_bar))+0.01;

if (value>1.0)

value=0.0;

v=(int)(value*100);

strcpy(text," ");

sprintf(text,"%d",v);

strcat(text,"% ");

// 设置进度条的新值

gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress_bar),value);

gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress_bar),text); //这是一个timeout函数,返回 TRUE,这样它就能够继续被调用 //如果想要结束,可以在进度条到100%时,return 0;这样回调函数就会结束

return TRUE;

}

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

int timer;

gpointer data;

gtk_init(&argc,&argv);//在任何构件生成之前完成

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

gtk_window_set_title(GTK_WINDOW(window),"Swindow1");//设置窗口标题

gtk_widget_set_usize(window, 200, 200);//设置窗口大小

gtk_container_set_border_width(GTK_CONTAINER(window),5);//设置窗口边框宽度

gtk_widget_show(window);//显示窗口

gtk_signal_connect(GTK_OBJECT(window),"destroy",GTK_SIGNAL_FUNC(gtk_main_quit),NULL);

table=gtk_table_new(5,11,TRUE);//创建表格3行*5列

gtk_widget_show(table);//显示表格

gtk_container_add(GTK_CONTAINER(window),table);//将table1装进窗口 /* 创建进度条 */

progress_bar=gtk_progress_bar_new();

gtk_table_attach_defaults(GTK_TABLE(table),progress_bar,0,11,1,2);//进度条装进表格

gtk_widget_show(progress_bar);

/* 加一个定时器(timer),以更新进度条的值 */

timer=gtk_timeout_add(150,progress_timeout,data);

gtk_main ();

return 0;

}

3) S2.c

#define GTK_ENABLE_BROKEN

#include<gtk/gtk.h>

#include<string.h>

GtkWidget *window;

GtkWidget *table;

GtkWidget *label;

GtkWidget *button;

GtkWidget *button1;

int i=0;

gint hello(void ){

label=gtk_label_new("Hello World!"); //创建标题

gtk_table_attach_defaults(GTK_TABLE(table),label,3,8,0,1);

gtk_widget_show (label);

return TRUE;

}n

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

GtkWidget *vscrollbar;

int timer;

gpointer data;

gtk_init(&argc,&argv);//在任何构件生成之前完成

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

gtk_window_set_title(GTK_WINDOW(window),"Swindow2");//设置窗口标题

gtk_widget_set_usize(window, 200, 200);//设置窗口大小

gtk_container_set_border_width(GTK_CONTAINER(window),5);//设置窗口边框宽度

gtk_window_set_position(GTK_WINDOW(window),

GTK_WIN_POS_MOUSE);//设置窗口位置

gtk_widget_show(window);//显示窗口

gtk_signal_connect(GTK_OBJECT(window),"destroy",GTK_SIGNAL_FUNC(gtk_main_quit),NULL);

table=gtk_table_new(11,10,TRUE);//创建表格11行*10列

gtk_container_add(GTK_CONTAINER(window),table);//将table1装进窗口

gtk_widget_show(table);//显示表格

/* 添加一个按钮,用来退出应用程序 */

button = gtk_button_new_with_label ("close");

gtk_table_attach_defaults(GTK_TABLE(table),button,3,8,6,8);//按钮装进表格

gtk_widget_show (button);

gtk_signal_connect(GTK_OBJECT(button),"clicked",GTK_SIGNAL_FUNC(gtk_main_quit),NULL);

gtk_main ();

return 0;

}

4) S3.c

#define GTK_ENABLE_BROKEN

#include<gtk/gtk.h>

#include<string.h>

GtkWidget *window;

GtkWidget *table;

GtkWidget *label;

GtkWidget *text;

int i=0;

gint text_timeout( gpointer data ){

char buf[20];

char temp[10];

sprintf(temp,"%d",i);

strcpy(buf,"text ");

strcat(buf,temp);

strcat(buf,"\n");

//将buf内容插入到文本构件中

gtk_text_insert(GTK_TEXT(text),NULL,NULL,NULL,buf,-1);

i++;

//这是一个timeout函数,返回 TRUE,这样它就能够继续被调用 //如果想要结束,return 0;这样回调函数就会结束

return TRUE;

}

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

GtkWidget *vscrollbar;

int timer;

gpointer data;

gtk_init(&argc,&argv);//在任何构件生成之前完成

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

gtk_window_set_title(GTK_WINDOW(window),"Swindow3");//设置窗口标题

gtk_widget_set_usize(window, 200, 400);//设置窗口大小

gtk_container_set_border_width(GTK_CONTAINER(window),5);//设置窗口边框宽度

gtk_window_set_position(GTK_WINDOW(window),

GTK_WIN_POS_CENTER); //设置窗口位置

gtk_widget_show(window);//显示窗口

gtk_signal_connect(GTK_OBJECT(window),"destroy",GTK_SIGNAL_FUNC(gtk_main_quit),NULL);

table=gtk_table_new(11,10,TRUE);//创建表格11行*10列

gtk_container_add(GTK_CONTAINER(window),table);//将table1装进窗口

gtk_widget_show(table);//显示表格

label=gtk_label_new("Text List"); //创建标题

gtk_table_attach_defaults(GTK_TABLE(table),label,0,10,0,1);

gtk_widget_show (label);

text=gtk_text_new(NULL,NULL);//创建文本构件

gtk_widget_show(text);//显示文本构件

//将文本构件装进表格

gtk_table_attach_defaults(GTK_TABLE(table),text,0,9,1,11);

//创建滚动条并设置其与文本同步

vscrollbar=gtk_vscrollbar_new(GTK_TEXT(text)->vadj);

gtk_widget_show (vscrollbar);//显示垂直滚动条

gtk_table_attach_defaults (GTK_TABLE (table), vscrollbar,9,10,1,11);//将滚动条装进表格

timer=gtk_timeout_add(1000,text_timeout,data);//调用回调函数,每隔1秒输出一行文本

gtk_main ();

return 0;

}

五、 课程设计内容二

Linux内核,简单来说就是一套用来控制计算机最底层的硬件设备,如处理器、内存、硬盘等的一种软件,一般称为操作系统,在Linux术语中称为内核。其中包含的模块有存储管理、CPU和进程管理、文件系统、设备管理和驱动、网络通信,以及系统的初始化(引导)、系统调用等。

这次题目就是要更改Linux内核中的”系统调用”模块,在其中添加自定义的函数,实现功能是文件拷贝。

本次我选用的Linux内核版本是

需要做的工作流程基本如下:

操作系统课程设计报告upload

1. 下载并解压内核

首先到官方网站/下载内核,不一定要最新的,以广大用户评定较稳定的内核为优先。我这次选用linux-2.6.37.4 其次打开终端,使用下列命令对其解压到目录/usr/src

sudo tar -xjvf linux-2.6.37.4.tar.bz2 -C /usr/src

该目录用来存放内核的源码。

2. 修改内核

首先要对系统调用模块的源码添加一个自定义函数,即对/usr/src/linux-2.6.37.4/kernel/sys.c进行修改,在该源码的最后添加下列函数的源码:

asmlinkage int sys_mycall(char* sourceFile,char* destFile)

{

int source=sys_open(sourceFile,O_RDONLY,0);

int dest=sys_open(destFile,O_WRONLY|O_CREAT|O_TRUNC,0600); char buf[4096];

mm_segment_t fs;

fs = get_fs();

set_fs(get_ds());

int i;

if(source>0 && dest>0)

{

do

{

i=sys_read(source,buf,4096);

sys_write(dest,buf,i);

}while(i);

}

else

{

printk("Error!");

}

sys_close(source);

sys_close(dest);

set_fs(fs);

return 10;

}

修改完函数之后,接下来要修改系统调用号所对应的函数名,即修改/usr/src/linux-2.6.32.10/arch/x86/include/asm/unistd_32.h

该文件定义了系统调用号,我们只要找一没被使用的系统调用号,用该号给我们自定义函数使用,比如

#define __NR_sys_mycall 337

/*定义系统调用sys_mycall 的系统调用号为337*/

修改完系统调用号后,接下来要修改系统调用表,即/usr/src/linux-2.6.32.10/arch/x86/kernel/syscall_table_32.S,这个文件是用汇编语言编写的,因此要让自定义的系统调用相对于其他系统调用的顺序337个,写上.long sys_mycall /*337*/

基本修改完以上源码后,接下来对其进行默认的净化、设置等。

使用下列代码对其源码进行处理

sudo make mrproper 净化解压后的源代码

sudo make menuconfig 对内核选项进行配置

如果这一步有错误可能是正在使用的系统没有安装必要的库文件,

如ncurses、libncurses*,这时候需要输入如下指令来安装

首先回到系统根目录

sudo apt-get install ncurses

sudo apt-get install libncurses*

依照提示就能安装好必要的库文件了。然后再回到内核源码的目录

下尝试使用sudo make menuconfig对内核选项进行配置。

sudo make dep 建立模块间的依赖信息

sudo make clean 删除配置时留下的一些不用的文件

3. 编译内核

接下来是最费时间的环节,少则2个小时,多则3个小时的编译,

需要再三确保前面步骤是否正确后再进行下一步。

sudo make bzImage 编译内核

这个过程大概是20多分钟

sudo make modules 编译内核模块

这个过程大概要100分钟~150分钟左右,一般如果有错误,会在前

十几分钟就停止编译并报错。

4. 安装内核

比较简单,只需要两条指令

sudo make modules_install 安装内核模块

sudo make install 安装内核

安装完毕后,需要开机时选择使用新的Linux核心,要做下列修改:

1)复制内核到系统启动引导目录

cp /usr/src/linux-2.6.37.4/arch/i386/boot/bzImage /boot/vmlinuz-2.6.37.4-mykernel

2)创建初始RAM磁盘——initrd 在创建之前先安装必要的程序 apt-get install bootcd-mkinitramfs mkinitramfs -o /boot/initrd.img-2.6.37.4 3)更新grub

在/boot/grub/grub.cfg中, 复制一段旧的核心代码,并将里面linux和

initrd中的路径改为新增的,注意不能用update-grub2 在/boot中复制一个旧的config-xxxxxx做为自己的 4)cd /boot cp initrd.img-2.6.37.4 initrd-2.6.37.4.old 以上是备份initrid,下面是修改 depmod –a update-initramfs -k 2.6.37.4 –c cd /tmp gzip -dc /boot/initrd.img-2.6.37.4| cpio -id touch lib/modules/2.6.37.4/modules.dep find ./ | cpio -H newc -o > /boot/initrd.img-2.6.37.4.new gzip /boot/initrd.img-2.6.37.4.new cd /boot mv initrd.img-2.6.37.4.new.gz initrd.img-2.6.37.4 5)重开机测试

5. 测试功能

用C语言编写测试程序testsys.c,源代码如下: #include <linux/unistd.h>

#include <stdio.h>

#include <asm/unistd.h>

int main(int argc,char**argv)

{

int i=syscall(337,argv[1],argv[2]); /*337为系统调用号*/ printf("successfully!\r\n");

printf("%d",i);

return 1;

}

编译gcc –o testsys testsys.c

运行./testsys test.txt target.txt

系统调用337号功能,拷贝文件test.txt到target.txt 运行结果截图如下:

六、 课程设计内容三

本题是为了更深刻了解模块机制,这种机制可以动态的在内核中添加或者删除模块。模块一旦被插入内核,他就和内核其他部分一样了。

Unix系统将设备分为三种类型字符设备、块设备、网络接口;一般对应这三种模块字符模块、块模块、网络模块。

字符设备,是能够像字节流一样被访问的设备,由字符设备驱动程式来实现这种特性。下面是一个的字符设备驱动程式。

源代码sdev.c

#include <linux/init.h>

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/cdev.h>

#include <asm/uaccess.h>

#define DEFAULT_MSG "default dev data\n" /*默认字符设备数据*/

#define DEVICE_NAME "sdev" /*设备名*/

#define MAXBUF 20 /*设备数据缓冲区大小*/

操作系统课程设计报告upload

static unsigned char sdev_buf[MAXBUF]; /*设备内存数据缓冲区*/

static int sdev_open (struct inode *inode, struct file *file);

static int sdev_release (struct inode *inode, struct file *file);

static ssize_t sdev_read (struct file *file, char __user *buf,size_t count, loff_t *pos); static ssize_t sdev_write (struct file *file, const char __user *buf,size_t count, loff_t *pos);

static int sdev_open (struct inode *inode, struct file *file){ return 0;}

static int sdev_release (struct inode *inode, struct file *file){ return 0;}

/*从设备读取count个数据到用户数据区buf中*/

static ssize_t sdev_read (struct file *file, char __user *buf,size_t count, loff_t *pos) {

int size = count < MAXBUF ? count : MAXBUF; /*选择读取数量 count or 数据缓冲区*/

if (copy_to_user(buf, sdev_buf, size)) /*把设备数据copy到用户空间buf中,数量为size*/

return -ENOMEM; /*错误:内存不足*/

return size;

}

/*把buf中count个数据写入设备内存空间中*/

static ssize_t sdev_write (struct file *filp, const char __user *buf,size_t count, loff_t *pos)

{

int size = count < MAXBUF ? count : MAXBUF; /*选择写入数量 count or 数据缓冲区*/

memset(sdev_buf, 0, sizeof(sdev_buf)); /*将设备内存清空*/

if (copy_from_user(sdev_buf, buf, size)) /*把buf中的用户数据写到设备内存sdev_buf中,数量为size*/

return -ENOMEM;

return size;

}

/**/

static struct file_operations sdev_fops = {

.read = sdev_read,

.write = sdev_write,

.open = sdev_open,

.release = sdev_release,

};

static struct cdev *sdev_cdev; /*创建设备指针*/

static int __init sdev_init(void)

{

dev_t dev; /*设备号*/

int error;

/*模块初始化*/

error = alloc_chrdev_region(&dev, 0, 2, DEVICE_NAME);

号*/

if (error)/*返回值不为0表示分配失败*/

{

printk("动态分配设备号失败!\n");

return error;

} /*动态分配一个设备

sdev_cdev = cdev_alloc(); /*新分配一个字符设备对象*/

if (sdev_cdev == NULL)

{

printk("动态分配字符设备对象失败!\n");

unregister_chrdev_region(dev, 2); /*注销一个分配的设备号区域*/ return -ENOMEM;

}

sdev_cdev->ops = &sdev_fops; /*设定字符设备操作函数指针*/

sdev_cdev->owner = THIS_MODULE; /*设备的属主*/

error = cdev_add(sdev_cdev, dev, 1); /*将设备添加到内核中去*/

if (error)

{

printk("设备添加失败!\n");

unregister_chrdev_region(dev, 2); /*注销一个分配的设备号区域*/ cdev_del(sdev_cdev); /*删除字符设备对象*/

return error;

}

memset (sdev_buf, 0, sizeof(sdev_buf)); /*清空设备缓冲区数据*/

memcpy(sdev_buf, DEFAULT_MSG, sizeof(DEFAULT_MSG)); /*设定设备缓冲区默认数据*/

printk("设备缓冲区默认数据: default dev data\n");

return 0;

}

static void __exit sdev_exit(void) /*模块卸载*/

{

unregister_chrdev_region(sdev_cdev->dev, 2);

cdev_del(sdev_cdev);

printk("设备删除成功!\n");

}

module_init(sdev_init);

module_exit(sdev_exit);

MODULE_LICENSE("GPL");

除了编写字符设备驱动程序外,还需要编写一个通过makefile文件来描述hello.c源程序的相互关系并自动维护编译工作。

下面根据通用的Makefile做些微修改后的代码

源代码Makefile:

DEBFLAGS = -O2

EXTRA_CFLAGS += $(DEBFLAGS)

# CFLAGS += -I$(LDDINC)

ifneq ($(KERNELRELEASE),)

# call from kernel build system

xbrige-objs := sdev.o

obj-m := sdev.o

else

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

PWD := $(shell pwd)

modules:

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

# $(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINC=$(PWD)/../include modules

endif

clean:

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

depend .depend dep:

$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -M *.c > .depend

ifeq (.depend,$(wildcard .depend))

include .depend

endif

然后编写一个测试程序

源文件test.c

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#define MAXBUF 20

int main()

{

int testdev;

int i;

char buf[MAXBUF];

testdev = open("/dev/sdev",O_RDWR);

if ( testdev == -1 )

{

printf("Cann't open file \n");

return -1;

}

read(testdev,buf,MAXBUF);

for (i = 0; i < MAXBUF;i++)

printf("%c",buf[i]);

close(testdev);

}

一切文件准备就绪之后,接下来要编译并安装这个模块,具体操作如下:

1) 首先cd到上述3个文件的目录下

2) 先输入make,生成sdev.ko sdev.o sdev.mod.c 等文件﹔

3) 然后输入insmod hello.ko插入﹔

4) 之后我们可以输入lsmod 查看

5) 输入cat /proc/devices记下号码(251)

6) 输入mknod /dev/sdev c 251 0 ﹐在dev目录下创建sdev文档

7) 输入gcc -o test test.c编译测试程序﹐再输入./test运行

8) 结果将输出sdev中的字符串default dev data,如截图所示

9) 输入echo xxxxxx>/dev/sdev把sdev中的数据变为xxxxxx﹐并可调用./test

操作系统课程设计报告upload

操作系统课程设计报告upload

观看

结果﹐此处xxxxxx为Hello HUST

10) 输入rmmod sdev﹐删除sdev﹔

11) 可输入lsmod 和cat /proc/devices查看﹐确认是否删掉﹔

12) 最后输入rm /dev/sdev﹐再输入y确定删掉dev目录下的sdev。

七、 课程设计内容四

本题未完成

八、 课程设计总结

这次课程设计使我对Linux有了深刻的了解,了解Linux下的图形编程、系统编译、系统调用、设备模块等。

首先是在Linux下编程,虽然使用了熟悉的C语言来编写程序,但是其中有些函数并不是能直接使用,比如fopen、fwrite等,需要使用更为低端的系统open、write等,此时就需要用到Linux里的头文件,进而需要了解Linux下的文件目录结构。之后使用Linux下的图形库GTK进行窗口编程,这是一个新接触的图形库,但是跟我们以往在图形学里所学的OpenGL相差不大,可以以类比的方式学习。首先要先寻找并下载相关的图形库。自己在网上寻找常用的GTK函数,然后做了三个简单的程序,再运用上学期所学的进程并发方法,对着3个程序进行并发,由于在较为漂亮的ubantu下运行的,所以呈现出来的窗口还比较赏心悦目,激发了对Linux编程的兴趣。

操作系统课程设计报告upload

其次是学习系统调用,本次学习只是对系统调用进行了解,所以并没有使用复杂的程序作为被调用的系统。想要自己添加新的系统调用,那就要对系统内核有所了解,了解其内核代码结构。此间了解了Linux系统版本号的含义2.6.xxx其中6是双数表示一个稳定的版本,假如是

2.5.xxx那就表示是一个测试的版本。本次作为学习选用了一个稳定的版本linux-2.6.37.4。虽然课程设计ppt里已讲述文件系统的目录结构,但凭借这些讯息就想要编译内核是远远不够的。因此而花了大量的时间上网搜寻相关资料来了解如何编译内核。在一步步的对内核进行查找、修改和编译的过程中,大致对内核的结构有所了解,发现内核源码的精炼,以及编译程序make的强大编译能力,能将几时MB的内核源码,编译成几GB的核心,当然这需要相当多的时间作为代价去编译,因此在编译之前只能小心翼翼的检查每一个步骤及其含义,尤其是修改syscall_table_32.S这个文件时,一不小心就会修改错误,导致编译错误,之后发现其为一个汇编语言所编写的文件,需要相对于文件行数来进行编写,就像枚举一个数组,需要按照顺序编写。最后编译完成,安装内核,修改启动引导,最后测试系统调用号。其结果只是一个非常简单的文件拷贝,但其中需要学习的过程非常之辛苦,要花费大量时间去理解和测试。

然后是添加一个设备驱动程序,在做完先前一个题目后,发现如果要添加一个系统调用,又要重新修改并编译一次内核,那所需要花费的时间实在是太多太多了,非常不方便,于是有了新的机制,模块机制。假设所需要的程序都已经编写好模块化了,那么从安装模块到使用该设备驱动程序就是几分钟的事,非常方便,如果不需要,随时也可以卸载驱动程序。刚开始看到这道题时,感觉会比较简单,但其实也不然。难点在于编写这些模块化的代码是相当复杂且麻烦的,我的感觉就是用复杂并规范的程序代码换取内核上的编译时间。编写一个驱动设备程序代码,其规划化要求很高,刚开始看到的时候非常抽象,很难一眼就看出来某些代码的含义是什么,后来就算一一解析,也无法完全掌握其含义,也许是涉及到太基层的关系。因此最后在一知半解下使用了通用的makefile来对自己的设备驱动源码编译。编译完后就是对模块的安装并操作,如同前面所说,这个过程是比较快的,通过几条指令就能将一个驱动安装好并使用。

最后通过这次课程设计我学到了很多关于Linux系统的知识,虽然都是比较基层的系统原理学习,但这跟以前常用的Windows系统很有区别,因此大致了解操作系统和操作系统之间的区别是在什么地方。除此之外,Linux系统是比较开放的,什么都是开源,因此在对学习系统原理上非常有帮助。另外我还了解到了Linux相比Windows还有很多优点,比如服务器的架设、灵活性、多平台等

九、 参考文献

操作系统原理 庞丽萍 华中科技大学出版社

Linux设备驱动(第二版,中文) 魏永明 中国电力出版社

相关推荐