西安交大操作系统课内实验指导书免费.docx
《西安交大操作系统课内实验指导书免费.docx》由会员分享,可在线阅读,更多相关《西安交大操作系统课内实验指导书免费.docx(37页珍藏版)》请在冰豆网上搜索。
西安交大操作系统课内实验指导书免费
操作系统原理课内实验指导书
实验一:
用户接口实验
准备知识
注意:
内核编译和系统调用添加部分请参考共享的其他相关资料,实验指导书的内容多有错谬之处。
亦可自行网上搜索相关教程。
为了使用户通过操作系统完成各项管理任务,操作系统必须为用户提供各种接口来实现人机交互。
经典的操作系统理论将操作系统的接口分为控制台命令和系统调用两种。
前者主要提供给计算机的操作人员对计算机进行各种控制;而后者则提供个程序员,使他们可以方便地使用计算机的各种资源。
1.控制台命令接口
操作系统向用户提供一组控制台命令,用户可以通过终端输入命令的方式获得操作系统的服务,并由此来控制自己作业的运行。
一般来讲,控制台命令应该包含:
一组命令、终端处理程序以及命令解释程序。
1)bash的由来
当登录Linux或者打开一个xterm时,当前默认的shell就是bash。
Bash是GNUProject的shell。
GNUProject是自由软件基金会(FreeSoftwareFoundation)的一部分。
它对Linux下的许多编程工具负责。
Bash(BourneAgainShell)是自由软件基金会发布的Bourneshell的兼容程序。
它包含了其他有些shell的许多良好的特性,功能非常的全面。
很多Linux版本都供bash。
2)bash的大致原理
bash处理自己的脚本时,先找到需要处理的命令名称,进而在当前用户的默认命令目录中找到对应的命令,这些默认目录一般是/usr/bin、/bin或/sbin。
在执行这些命令时,先使用进程创建系统调用fork(),在使用exex()来执行这些命令。
3)建立bash脚本
Ø编辑文件
可以用最熟悉的编辑器来编辑这个文本文件,比如文件名为script,在shell下输入:
$viscript
进入vi编辑器,在编辑器中输入以下内容
#!
/bin/bash
echoHelloWorld!
然后保存,退出。
Ø测试脚本。
使用指令:
$$sourcescript
Ø更改脚本属性
使用指令:
$chmoda+xscript
将脚本程序设置为可执行。
Ø执行脚本
使用指令:
$./script
4)关键字参考
echo在终端上显示
bash特殊变量1~9,保存当前进程或脚本的前9个参数。
ls列举目录
wc统计数量
function定义函数
2.系统调用
系统调用是操作系统为程序员提供的接口服务。
使用系统调用,程序员可以更充分的利用计算机资源,使编写的程序更加灵活,功能更加强大。
程序员在对系统充分了解的情况下甚至可以订做系统调用,实现那些非专业程序员所难以实现的功能。
1)添加源代码
第一个任务是编写添加到内核的源程序,即添加到内核文件中的一个函数。
该函数的名称应该是在新的系统调用名称之间前加上sys_标志。
假设新加的系统调用为foo(),功能为原值返回输入的整型数。
格式为intfoo(intiNumber),返回的值就是出入的参数。
在/usr/src/linux/kernel/sys.c文件中添加源代码,如下所示:
asmlinkageintsys_foo(intx)
{printf(“%d\n”,x);
}
注意:
目录“/usr/src/linux“是linux各个版本的统称,它因系统内核的版本不同而名称不同。
例如,当前操作系统是Linux7.1器内核四Linux-2.4.2,所以在”usr/src”目录下有两个文件:
Linux-2.4和Linux-2.4.2,其中Linux-2.4是Linux-,程序员可以进入任何一个目录,它对内核的修改都是一样的。
2)连接新的系统调用
添加新的系统调用之后,下一个任务是让Linux内核的其余部分知道该程序的存在。
为了从已有的内核程序中增加新函数的链接,需要进行下面的操作:
(1)进入目录/usr/src/linux/include/asm-i386/,打开文件unistd.h。
这个文件包含了系统调用的清单,用来给每个系统调用分配一个唯一的号码。
系统调用号的定义格式如下:
#define_NR_nameNNN
其中,name以系统调用名称代替,而NNN是该系统调用对应的号码,应该将新的系统调用名称放到清单的最后,并给它分配已经用到的系统调用号后面的一个号码,比如:
#define_NR_foo222
以上的系统调用号便是222。
Linux内核自身用的系统调用号已经用到了221了。
如果读者还要自行增加系统调用,就必须从223开始。
(2)进入/usr/src/linux/arche/i386/kernel/,打开文件entry.S。
该文件中有类似下面的清单:
ENTRY(sys_call_table)
.longSYSMBOL_NAME(sys_ni_syscall)
.longSYSMBOL_NAME(sys_exitl)
.longSYSMBOL_NAME(sys_fork)
….
在该表的最后加上:
.longSYSMBOL_NAME(sys_foo)
3)重新编译内核
为了使新的系统调用生效,需要重建Linux的内核,首先必须以root的身份登录。
进入目录:
/usr/src/linux/,重建内核:
[root@linuxserverroot]#makemenuconfig//配置新内核
[root@linuxserverroot]#makedep//创建新内核
[root@linuxserverroot]#makemoduless_install//加入模块
[root@linuxserverroot]#makeclean//清楚多余创建的文件
[root@linuxserverroot]#makebzImage//生成可执行内核引导文件
4)使用新编译的内核
cp–a/usr/src/linux-
5)重新配置/etc/lilo.conf文件
使用vi编辑器编辑/etc/lilo.conf文件:
vi/etc/lilo.conf
在其中加入如下几行:
Image=/boot/bzImage#启动内核的位置,即自己
#新配置的内核所在目录
label=xhlinux#给内核起一个名称,配
#置完成,重新启动的时候,#会显示这个名称
read_only#定义新的内核为只读
root=/dev/hda5#定义硬盘的启动位置为
#/dev/hda5,在该设计中没有变
#仿照以前内核引导位置,不
#用修改,用以前的就可以了
6)重启系统
完成以上配置后,重新启动系统进入自己的新系统
实验指导
1.控制台命令接口实验指导
1)查看bash版本
在shell提示符下输入:
$echo$BASH_VERSION
2)编写bash脚本:
统计/my目录下c语言文件的个数
通过bash脚本,可以有多种方式实现这个功能,而使用函数是其中个一个选择。
在使用函数之前,必须先定义函数。
(1)进入自己的工作目录,用vi编写名为count的文件
cd/home/student#在home/student目录下编程
vicount
下面是脚本程序:
#!
/bin/bash
functioncount
{
echo–n"Numberofmatchesfor$1:
"#接收程序的第一个参数
ls$1|wc–l#对子程序的第一个参数所在的目录进行操作
}
(2)执行
将count文件复制到当前目录下,然后在当前目录下建立文件夹my:
mkdirmy
3)cdmy
vi1.c#在my目录下建立几个c文件,以便用来进行测试
...
cd...
chmod+xcount
./count‘./my/*.c’(单引号)
2.系统调用实验指导
1)编程调用一个系统调用fork()
在应用程序中调用系统调用fork()非常简单,下面的程序可以很清楚的显示出有fork()系统调用生成了子进程,而产生的分叉作用:
#include
intmain()
{
intiUid;
iUid=fork();
if(iUid==0)
for(;;){printf("Thisisparent.\n");
sleep
(1);
}
if(iUid>0)
for(;;){
printf("Thisischild.\n");
sleep
(1);
}
if(iUid<0)printf("Cannotusesystemcall.\n");
return0;
}
下面是可能得到的一种结果:
thisischild.
thisisparent.
thisischild.
thisisparent.
thisisparent.
thisischild.
thisischild.
thisisparent.
thisisparent.
thisischild.
thisischild.
thisisparent.
thisisparent.
thisischild.
2)编程调用创建的系统调用foo()
foo()是本实验系统设计者自行添加的一个简单的系统调用。
其实现过程在前面一进介绍了,它的功能很简单,就是向标准输出一个特定的整数。
程序test.c如下:
#include
#include
_syscall1(char*,foo,int,ret)
main()
{
intI,J;
I=100;
J=0;
J=foo(I);
printf("Thisistheresultofnewkernel\n");
printf("%d",j);
}
程序编译:
gcc–o–I/usr/src/linux-test.c
注意:
由于需要引入内核头文件unistd.h,不能简单的用普通的编译命令,而应该这样设置参数。
运行测试程序
./test
解释:
当函数还没有定义在内核中时,如果运行以上程序,系统将显示一个未定义的值-1,而在内核中定义了以后,系统将显示100.说明我们内核添加系统调用已经成功。
3)创建系统调用mycall(),实现功能:
显示字符串到屏幕上。
(1)编写系统调用相应的函数
在/usr/src/linux/kernel/sys.c文件中添加如下代码:
asmlinkagevoidmycall(char*str)
{
printk("%s\n",str);
}
注意:
在上面的例子中,有一个很少见的特殊函数printk(),它的功能与printf()类似。
之所以不用printf()的原因在于,它不是内核的函数。
要在内核实现在控制台显示字符串的功能,就必须以内核所提供的printk()函数来代替。
Printk()不同于printf()的地方是:
printk()会把输出的结果送到内核的缓冲区里面,最终调用控制台的写函数将其打印出来。
(2)添加系统调用号
打开文件/usr/src/linux/include/asm-i386/unistd.h,添加如下一行:
#include__NR_mycall22