操作系统Linux课程实验报告.docx
《操作系统Linux课程实验报告.docx》由会员分享,可在线阅读,更多相关《操作系统Linux课程实验报告.docx(34页珍藏版)》请在冰豆网上搜索。
![操作系统Linux课程实验报告.docx](https://file1.bdocx.com/fileroot1/2023-4/24/8265a60a-541c-43a1-85ca-f2db15cfbf64/8265a60a-541c-43a1-85ca-f2db15cfbf641.gif)
操作系统Linux课程实验报告
实验1.1、1.2LinuxUbuntu的安装、创建新的虚拟机VMWare
实验1.3Shell编程
1.实验目的与容
通过本实验,了解Linux系统的shell机制,掌握简单的shell编程技巧。
编制简单的Shell程序,该程序在用户登录时自动执行,显示某些提示信息,如“WelcometoLinux”,并在命令提示符中包含当前时间、当前目录和当前用户名等基本信息。
2.程序源代码清单
#include
#include
intmain(){
printf("HelloLinux\n");
intpid;
intstate;
intpfd[2];
pipe(pfd);
if(fork()==0){
printf("Inthegrepprogress\n");
dup2(pfd[0],0);
close(pfd[0]);
close(pfd[1]);
execlp("grep","grep","sh",0);
perror("exelpgreperror");
}
esleif(fork()==0){
printf("Inthepsprogress\n");
dup2(pfd[1],1);
close(pfd[0]);
close(pfd[1]);
execlp("ps","ps","-ef",0);
perror("execlpps-ef");
}
close(pfd[1]);
close(pfd[0]);
wait(&state);
wait(&state);
}
实验2.3核模块
实验步骤:
(1).编写核模块
文件中主要包含init_clock(),exit_clock(),read_clock()三个函数。
其中init_clock(),exit_clock()负责将模块从系统中加载或卸载,以及增加或删除模块在/proc中的入口。
read_clock()负责产生/proc/clock被读时的动作。
(2).编译核模块Makefile文件
#Makefileunder2.6.25
ifneq($(KERNELRELEASE),)
#kbuildsyntax.dependencyrelationshsipoffilesandtargetmodulesarelistedhere.
obj-m:
=proc_clock.o
else
PWD:
=$(shellpwd)
KVER?
=$(shelluname-r)
KDIR:
=/lib/modules/$(KVER)/build
all:
$(MAKE)-C$(KDIR)M=$(PWD)modules
clean:
rm-rf.*.cmd*.o*.mod.c*.ko.tmp_versions*.symvers*.order
endif
编译完成之后生成proc_clock.ko模块文件。
(3).核模块源代码clock.c
#include
#include
#include
#include
#include
#include
#defineMODULE
#defineMODULE_VERSION"1.0"
#defineMODULE_NAME"clock"
structproc_dir_entry*my_clock;
intread_clock(char*page,char**start,off_toff,intcount,int*eof,
void*data){
intlen;
structtimevalxtime;
do_gettimeofday(&xtime);
len=sprintf(page,"%d%d\n",xtime.tv_sec,xtime.tv_usec);
printk("clock:
read_func()\n");
returnlen;
}
structproc_dir_entry*clock_proc_file;
intinit_clock(void)
{
clock_proc_file=create_proc_read_entry("clock",0,NULL,read_clock,NULL);
return0;
}
voidexit_clock(void)
{
remove_proc_entry("clock",clock_proc_file);
}
module_init(init_clock)
module_exit(exit_clock)
MODULE_LICENSE("GPL");
(4).编译核模块
#make
(5).加载核模块
在系统root用户下运行用户态模块命令装载核模块
#insmodproc_clock.ko
(6).测试
在终端中输入以下命令:
#cat/proc/clock
(7).卸载核模块
在系统root用户下运行用户态模块命令卸载核模块
#rmmodproc_clock.ko
实验2.4系统调用
实验步骤:
(1).添加新调用的源代码
在./linux-2.6.33.7/arch/x86/kernel/sys_i386_32.c中添加相应的调用代码
asmlinkageintsys_xwlcall(structtimeval*tv)
{
structtimevalktv;
do_gettimeofday(&ktv);
copy_to_user(tv,&ktv,sizeof(ktv));
printk(KERN_ALERT"PID%ldcalledsys_xwlcall()./n",(long)current->pid);
return0;
}
(2).连接系统调用
a、修改./linux-2.6.33.7/arch/x86/include/asm/unistd_32.h,
在系统调用列表后面相应位置添加一行,这样在用户空间做系统调用时就不需要知道系统调用号了,如果在用户空间指明了调用号,就可以省略这一步,实际上我就没写:
#define__NR_xwlcall338
新增加的调用号位338
b、修改./linux-2.6.33.7/arch/x86/kernel/syscall_table_32.S
在ENTRY(sys_call_table)清单最后添加一行,这步至关重要,338就是这里来的:
.longsys_xwlcall
(3).重建新的Linux核
先安装好编译核必要的软件包:
#sudoapt-getinstallbuild-essentialkernel-packagelibncurses5-dev
复制当前核的配置文件
#cp/boot/config-`uname-r`./.config
保存配置文件
#sudomakemenuconfig
使用debian的的核编译方法,要简单很多
#sudomake-kpkg-initrd--initrd--append-to-version=xwlcall
kernel_imagekernel-headers
运行以下deb包,安装核镜像和模块:
linux-image-2.6.33.7xwlcall_2.6.33.7xwlcall-10.00.Custom_i386.deb
运行以下deb包,安装核头文件:
linux-headers-2.6.33.7xwlcall_2.6.33.7xwlcall-10.00.Custom_i386.deb
运行以下命令,使核启动时能调用模块,比如硬件驱动:
#sudoupdate-initramfs-c-k2.6.33.7xwlcall
此次编译的核采用ubuntu默认配置文件,通用性非常好,可以拷贝到大部分x86机器上安装。
安装后系统自动会修改grub启动选单。
4.重建引导信息
a、安装deb包就自动重建引导信息了,无须另行处理。
b、如果仍然不放心,可以运行
#update-grub
5.重新引导从新的核进入
6.修改系统调用表
7.测试
实验3.3Shell编程实验(进程管理实验)
1、实验目的
通过编写shell程序,了解子进程的创建和父进程与子进程间的协同,获得多进程程序的编程经验。
2、实验容1
设计一个简单的shell解释程序,能实现基本的bsh功能。
3、实验原理
将每一条命令分子段压入argv栈。
然后再子进程中调用execvp()来实现该命令的功能。
4、代码(源代码清单)
#include
#include
#include
#defineBUFFERSIZE256
//最简单的shell,只是简单的执行命令调用,没有任何的其他功能
intmain()
{
charbuf[BUFFERSIZE],*cmd,*argv[100];
charinchar;
intn,sv,buflength;
intresult;
buflength=0;
for(;;){
printf("=>");
//处理过长的命令;
inchar=getchar();//读取命令
while(inchar!
='\n'&&buflengthbuf[buflength++]=inchar;
inchar=getchar();
}
if(buflength>BUFFERSIZE){
printf("Commandtoolong,pleaseenteragain!
\n");
buflength=0;
continue;
}
else
buf[buflength]='\0';
//解析命令行,分成一个个的标记
//char*strtok(char*s,char*delim)
//分解字符串为一组字符串。
s为要分解的字符串,delim为分隔符字符串。
cmd=strtok(buf,"\t\n");
if(cmd){
if(strcmp(cmd,"exit")==0)exit(0);
n=0;
argv[n++]=cmd;
while(argv[n++]=strtok(NULL,"\t\n"));
if(fork()==0){
execvp(cmd,argv);
fprintf(stderr,"sxh:
%s:
commandnotfound.\n",buf);//如果子进程顺利执行,这段话是不会执行的
exit
(1);
}
wait(&sv);
buflength=0;
}
}
}
实验容2
编写一个带有重定向和管道功能的Shell
1.设计思路
通过fork()创建子进程,用execvp()更改子进程代码,用wait()等待子进程结束。
这三个系统调用可以很好地创建多进程。
另一方面,编写的Shell要实现管道功能,需要用pipe()创建管道使子进程进行通信。
2.源代码清单
#include
#include
#include
#include
#defineBUFFERSIZE256
//具有输入输出重定向的功能和管道功能
int
main()
{
charbuf[256],*buf2,*cmd,*cmd2,*argv[64],*argv2[64],*infile,*outfile;
charinchar;
intn,sv,buflength,fd[2];
for(;;){
buflength=0;
printf("=>");
inchar=getchar();
while(inchar!
='\n'&&buflengthbuf[buflength++]=inchar;
inchar=getchar();
}
if(buflength>BUFFERSIZE){
fprintf(stderr,"Commandtoolong,pleaseenteragain!
\n");
buflength=0;
continue;
}
else
buf[buflength]='\0';
//检查是否具有管道操作符
//strstr()在字符串中查找指定字符串的第一次出现,buf2指向管道符号前端的命令
buf2=strstr(buf,"|");
if(buf2)
*buf2++='\0';
else{
//否则查看是否具有重定向的操作符
infile=strstr(buf,"<");
outfile=strstr(buf,">");
if(infile){
*infile='\0';
infile=strtok(infile+1,"\t\n");
}
if(outfile){
*outfile='\0';
outfile=strtok(outfile+1,"\t\n");
}
}
//解析命令行,分成一个个的标记
cmd=strtok(buf,"\t\n");
//执行管道命令
if(buf2){
if(strcmp(cmd,"exit")==0)exit(0);
if(!
cmd){
fprintf(stderr,"Commandtokenerror.\n");
exit
(1);
}
n=0;
//管道后端的命令
argv[n++]=cmd;
while(argv[n++]=strtok(NULL,"\t\n"));
//管道前端的命令
cmd2=strtok(buf2,"\t\n");
if(!
cmd2){
fprintf(stderr,"Commandtokenerror.\n");
exit
(1);
}
n=0;
argv2[n++]=cmd2;
while(argv2[n++]=strtok(NULL,"\t\n"));
pipe(fd);
if(fork()==0){
dup2(fd[0],0);//dup2复制文件句柄,将fd[0]复制到描述符0。
close(fd[0]);close(fd[1]);
execvp(cmd2,argv2);
fprintf(stderr,"**badcommand\n");exit
(1);
}elseif(fork()==0){
dup2(fd[1],1);
close(fd[0]);close(fd[1]);
execvp(cmd,argv);
fprintf(stderr,"**badcommand\n");exit
(1);
}
close(fd[0]);
close(fd[1]);
wait(&sv);
wait(&sv);
buflength=0;
}
//如果没有管道命令,如果有重定向就执行重定向操作,如果没有重定向就当作普通shell命令执行
else{
if(cmd){
if(strcmp(cmd,"exit")==0)exit(0);
n=0;
argv[n++]=cmd;
while(argv[n++]=strtok(NULL,"\t\n"));
if(fork()==0){
intfd0=-1,fd1=-1;
if(infile)fd0=open(infile,O_RDONLY);
if(outfile)fd1=open(outfile,O_CREAT|O_WRONLY,0666);
if(fd0!
=-1)dup2(fd0,0);//dup2复制文件句柄,将fd0复制到描述符0。
if(fd1!
=-1)dup2(fd1,1);//dup2复制文件句柄,将fd1复制到描述符1。
close(fd0);
close(fd1);
execvp(cmd,argv);
fprintf(stderr,"**Badcommand\n");
exit
(1);
}
wait(&sv);
buflength=0;
}
}
}//for
}
实验4.1观察实验(存储管理实验)
1.实验步骤
(1)、安装GDB
(2)、编写观测程序
(3)、按照指令手册进行观察操作
2.观测程序源代码
#include
#include
charstr[50]="HelloLinux.";
intmain()
{
intnum=10;
while(num--){
printf("%s\n",str);
}
}
//gcc-g-otestingtesting.c
3.实验结果及分析
(1).Gdb程序观察一个程序文件的容和结构
结果截图:
(2).GDB观察程序存映象的容和结构
(3).在Linux下,用free和vmstat命令观察存使用情况
(4).在Linux下,查看/proc与存管理相关的文件,并解释显示结果
实验5.1观察实验(进程通信)
在Linux下,用ipcs()命令观察进程通信情况,了解Linux基本通信机制
实验结果(截图):
实验6.3IO系统编程实验
1、实验目的
编写一个daemon进程,该进程定时执行ps命令,然后将该命令的输出写至文件F1尾部。
通过此实验,掌握LinuxI/O系统相关容。
2、实验容
编写一个daemon进程,该进程定时执行ps命令,然后将该命令的输出写至文件F1尾部。
3、实验原理
在这个程序中,首先fork一个子程序,然后,关闭父进程,这样,新生成的子进程被交给init进程接管,并在后台执行。
新生成的子进程里,使用system系统调用,将ps的输出重定向,输入到f1.txt里面。
4、实验步骤
编写daemon.c
代码如下:
#include
#include
intmain(intargc,char*argv[])
{
inti,p;
p=fork();
if(p>0){
exit(0);
}
elseif(p==0){
for(i=0;i<100;i++){
sleep(100);
system("ps>f1.txt");
}
}
else{
perror("Createnewprocess!
");
}
return1;
}
}
编译程序
#gcc-odaemondaemon.c
执行程序
#./daemon
实验7.1代码分析(文件系统管理实验)
1.实验目的
了解与文件管理有关的Linux核模块的代码结构。
2.实验结果(源代码分析)
A.创建文件模块分析
5780/*creatsystemcall*/
5781Creat()
5782{
5783resister*ip;
5784externuchar;
5785
5786ip=namei(&uchar,1);
5787if(ip==NULL){
5788if(u.u_error)
5789return;
5790ip=maknode(u.u_arg[1]&07777&(~ISVTX));
5791if(ip==NULL)
5792return;
5793open1(ip,FWRITE,2);
5794}else
5795open1(ip,FWRITE,1);
5796}
第5786:
“namei”(7518)将一路径名变换成一个“inode”指针。
“uchar”是一个过程的
名字,它从用户程序数据区一个字符一个字符地取得文件路径名。
5787:
一个空“inode”指针表示出了一个错,或者并没有具有给定路径名的文件存在。
5788:
对于出错的各种条件,请见UMP的CREAT(II)。
5790:
“maknode”(7455)调用“ialloc”创建一存“inode”,然后对其赋初值,并使
其进入适当的目录。
注意,显式地清除了“粘住”位(ISVTX)。
B.删除文件rm模块分析
3510unlink()
3511{
3512resister*ip,*pp;
3513externuchar;
3514
3515pp=namei(&uchar,2);
3516if(pp==NULL)
3517return;
3518prele(pp);
3519ip=iset(pp->dev,u.u_dent.u_ino);
3520if(ip==NULL)
3521panic(*unlink–iset*);
3522if((ip->i_mode%IFMT)==IFDIR&&!
suser())
3523gotoout;
3524u.u_offset[1]=-DIRSIZ+2;
3525u.ubase=&u.u_dent;
3526u.ucount=DIRSIZE+2;
3527u.u_dent.u_ino=0;
3528writei(pp);
3529ip->i_nlink--;
3530ip->i_flag=!
IUPD;
3531
3532out:
3533iput(pp);
3534iput(ip);
3535}
新文件作为永久文件自动进入文件目录。
关闭文件