操作系统课程设计实验报告文档格式.docx
- 文档编号:18345490
- 上传时间:2022-12-15
- 格式:DOCX
- 页数:29
- 大小:28.34KB
操作系统课程设计实验报告文档格式.docx
《操作系统课程设计实验报告文档格式.docx》由会员分享,可在线阅读,更多相关《操作系统课程设计实验报告文档格式.docx(29页珍藏版)》请在冰豆网上搜索。
显示echo后的内容且换行;
help简短概要的输出你的shell的使用方法和基本功能;
jobs输出shell当前的一系列子进程,必须提供子进程的命名和PID号;
quit,exit,bye退出shell。
所有的内部命令应当优先于在$PATH中同名的程序。
任何非内部命令必须请求shell创建一个新进程,且该子进程执行指定的程序。
这个新进程必需继承shell的环境变量和制定的命令行参数。
任务三要求
编程实现下图的效果,要求分别使用进程和线程
1.进程+SYSV信号量
2.线程实现+Posix同步操作API
任务四要求
在阅读大型项目代码时,经常要搜索某个标识符,找出该标识符的声明、定义或引用的地方(某文件的哪一行)。
本任务要求实现一个程序idfind,其使用格式如下:
idfind[-jn]iddirname
选项-j指定并行线程数目。
如果省略该选项,则只启动一个线程。
id表示要查找的标识符,dirname表示项目所在目录。
实现过程
任务一I/O系统调用开销比较
任务分析
(1)stardard.c(fopen、fread和fwrite)使用标准C的I/O方式读写文件。
程序从命令行参数中取得测试文件名,和每次读写数据的大小size,然后程序打开测试文件输入流,和建立一个唯一名字的空文件在当前目录并打开它的文件流。
定位到文件末尾-size处,读取数据到buffer数组中。
将buffer内容置反。
并写入输出文件。
依次从文件-2*size读取size个数据到buffer,执行相同操作,直到文件内容全部处理完毕。
最后删除输入文件,并将输出文件重命名为输入文件的文件名。
(2)unix.c(open、read和write)使用UINXI/O方式读取文件,同stardard.c原理相同,但文件读写时调用的是UNIX接口。
(3)mmap.c(open和mmap)使用文件映射方式读写文件。
程序从参数中取得测试文件名,和每次读写数据的大小size,然后程序打开测试文件并从文件尾部-size位置映射size到用户空间,建立一个唯一名字的空文件,调节文件大小为输入文件大小。
并映射输出文件到用户空间,从输入文件内存开始复制数据到输出文件内存,直到size个数据处理完毕,再从输入文件-2*size处映射size文件到内存,重复直到整个文件都处理完毕。
最后删除输入文件,并将输出文件重命名为输入文件文件名。
特别注意,mmap映射文件的偏移只能为linux中一页大小的整数倍,也就是说4KB的整数倍。
任务实施
1.stardard.c标准I/O读写方式文件分析:
size_tsize=atoi(argv[2]);
从参数2中取得一次读写文件大小。
if(mktemp(template)==NULL)创建唯一文件名的文件在当前目录下。
do
{
if(fread(buffer,1,size,src)<
size)
……..
for(i=0;
i<
size/2;
i++)
tmp=buffer[i];
buffer[i]=buffer[size-i-1];
buffer[size-i-1]=tmp;
}
if(fwrite(buffer,1,size,dest)<
…….
}while(fseek(src,-size*2,SEEK_CUR)==0);
使用fseek定位函数从文件尾部以此-size,-2*size,-3*size…位置读取数据到buffer中,在buffer中置反数据内容,并写入输出文件中。
直到文件所有数据都处理完毕。
也就是说fseek从当前位置向前偏移发送错误时,即文件数据都处理完毕。
if(rename(template,argv[1])!
=0)……重命名输出文件并自动删除重复的文件。
2.UNIXI/O读写方式,函数接口的不同。
其他算法和标准I/O相同。
3.mmap文件映射方式
{
…_src=mmap(NULL,size,PROT_READ,MAP_SHARED,src,position-size)
…s_dest=mmap(NULL,size,…,dest,statbuf.st_size-position))
size_tcount=size;
char*psrc=s_src+size-1;
char*pdest=s_dest;
while(count--)*pdest++=*psrc--;
munmap(s_src,size);
munmap(s_dest,size);
}while(position-=size);
映射输入输出文件分块映射用户内存空间,while(count--)*pdest++=*psrc--;
从映射地址尾部将数据依次复制到输出文件内存,解除映射.再次读取另一块数据,执行重复操作。
直到所有数据都处理完毕。
详细代码见备注一
任务二实现一个简单的shell
shell的主体就是反复执行下面的循环过程:
while
(1)
接收用户输入的命令行;
解析命令行;
if(用户命令为内部命令)
直接处理;
elseif(用户命令为外部命令)
创建子进程执行命令;
else
提示错误信息;
}
创建子进程要使用fork()函数,执行新的命令要使用exec()系列函数,通常shell是等待子进程结束后在接受用户新的输入,所以这里应该使用waitpid()函数。
fork()原型为:
pid_tfork(void);
fork建立一个子进程,父进程继续运行,子进程在同样的位置执行同样的程序。
对于父进程,fork()返回子进程的pid,对于子进程,fork()返回0。
出错时返回-1。
exec系列函数用新的进程映像置换当前的进程映像,这些函数的第一个参数是待执行程序的路径名(文件名)。
这些函数调用成功后不会返回,其进程的正文(text),数据(data),bss和堆栈(stack)段被待执行程序覆盖。
但是进程的pid和所有打开的文件描述符没有改变,同时悬挂信号被清除,信号重置为缺省行为。
wait(),waitpid()可用来等待子进程结束。
函数原型为:
#include<
sys/wait.h>
Pid_twait(int*stat)loc);
Pid_twaitpid(pid_tpid,int*stat_loc,intoptions);
当进程调用wait函数时,它将进入睡眠状态知道有一个子进程结束。
wait函数返回子进程的进程id,stat_loc中返回子进程的退出状态。
waitpid的第一个参数pid的意义:
pid>
0:
等待进程id为pid的子进程;
pid==0:
等待与自己同组的任意子进程;
pid==-1:
等待任意一个子进程;
pid<
-1:
等待进程组号为-pid的任意子进程。
因此,wait(&
stat)等价于waitpid(-1,&
stat,0)。
waitpid第三个参数option可以是0,WNOHANG,WUNTRACED或这几者的组合。
charcommand(char*s){return“xxx”}命令处理,返回命令处理后的字符串
help(){
printf("
帮助。
”)
}//显示帮助
fgets(n,100,stdin);
从命令行获得命令
switch(b)//分析命令处理后的返回字符
{
case1:
if(chdir(n+3)!
=0)//执行相应的命令
case0:
printf("
没有此命令,请重新输入正确的命令。
\n"
getpid());
help();
//命令不存在时,显示帮助
break;
详细代码见备注二
任务三进程/线程同步
依据题目需求,采用三个线程互斥对象,创建6个线程,6个线程运行时的函数。
线程一,由于没有和其他线程并发执行,所以先创建后,加入到主线程,等其执行完后再创建其他线程。
线程二,线程三,并发执行,且同时创建,同时当线程二执行结束后,线程四可以执行,同时,和线程三又控制着线程五的执行时间,所以线程二锁定两个线程互斥对象,互斥对象一和互斥对象二。
线程三锁定线程互斥对象三。
当线程二执行结束后,取消对互斥对象一、二的锁定,而线程四这时可以锁定互斥对象一,所以线程四开始执行,同时线程四锁定互斥对象一。
当线程三和线程线程二都执行结束后,互斥对象二、三都释放了。
所以线程五可以开始执行,同时线程五锁定互斥对象二、三。
当线程四,五都执行结束后,释放了互斥对象一、二和三,所以线程六可以锁定互斥对象一、二、三,这时线程开始执行。
pthread_mutex_tmymutex1=PTHREAD_MUTEX_INITIALIZER;
//创建互斥对象1.2.3
void*thread_function2(void*arg)//线程处理函数
{
inti=0;
pthread_mutex_lock(&
mymutex1);
mymutex2);
……
pthread_mutex_unlock(&
returnNULL;
}
pthread_tmythread1;
pthread_create(&
mythread2,NULL,thread_function2,NULL);
//创建线程1.2.3.4.5.6
if(pthread_join(mythread2,NULL)){//把线程1.2.3.4.5.6分别加入到主线程中
printf("
errorjoiningthread2."
);
abort();
详细代码见备注三
任务四文件内容的并行搜索
本任务要求搜索目录下所有文件的内容,显然我们需要通过API来获取本目录中的文件名,从而通过得到的文件名来读取该文件,进而在文件中搜索标识符。
但是,目录中有可能有子目录,按照任务要求,子目录下的文件同样需要进行搜索,而子目录又有可能有子目录。
显然这是一个典型的递归问题。
这里在主线程中使用了一个递归函数:
voidtravel_file(void);
该函数会遍历整个目录,如果当前搜索到的是一个文件,如果线程数量(信号量)没有达到上限值,则创建一个线程,并把该文件名传递给空闲的线程,如果线程数达到上限值,则主线程则必须等待直到某个线程结束。
这里便涉及到线程同步的问题,通常处理Posix线程同步可采用Posix信号量、互斥对象或条件对象。
在本程序中采用的是一个Posix信号量来处理线程同步,因为使用Posix信号量简单、方便,非常容易编程实现。
采用一个互斥信号量来处理文件名的复制,并且保证创建的线程先
运行,并保证文件名的正确复制。
子线程只需从主线程中获取主线程travel_file()中所得到的路径名,所以这里使用了一个名为current_thread信号量,顾名思义,主线程遍历到一个新的文件,检测信号量的值,如果小于最大值,则主线程创建一个子线程,启动子线程开始当前文件的搜索任务的时候,搜索
完成后,便释放这个信号量,,由于存储路径名的是一个全局变量,所以子线程必须将travel_file()所得到的正则文件路径拷贝到自身的私有空间中,为了防止线程对该变量的竞争,还必须设置一个copied互斥量,拷贝之前至拷贝结束期间都必须阻塞该信号量。
主线程代码如下:
voidtravel_file(void)
if(S_ISREG(statbuf.st_mode))
if(sem_wait(&
current_thread)<
0)
…
mutex);
if(pthread_create(&
tid,NULL,compare,(void*)pathname)!
=0)
if(S_ISDIR(statbuf.st_mode))
while((dirent=readdir(dir))!
=NULL)
if((strcmp(dirent->
d_name,"
."
)==0)||(strcmp(dirent->
.."
)==0))
continue;
strcat(pathname,"
/"
strcat(pathname,dirent->
d_name);
travel_file();
pathname[strlen(pathname)-strlen(dirent->
d_name)-1]='
/0'
;
递归函数中if(S_ISREG(statbuf.st_mode))…用于判断当前文件的类型。
如果是文件的
if(sem_wait(&
0)…用于信号量的可用资料,如果有资源可用使用,则
if(pthread_create(&
=0)…创建一个新的线程,pthread_mutex_lock(&
用于保证文件名目录的正确复制。
如果是文件夹,
while((dirent=readdir(dir))!
=NULL)…travel_file()
则readdir(dir)得到当前目录下的文件,并进行递归。
知道当前目录下的所有文件都被遍历。
子线程:
void*compare(void*args)
strcpy(fp,(char*)args);
while(fgets(buf,512-1,src)!
if(strstr(buf,id)!
total++;
printf("
%s:
line%d/t%s"
fp,line,buf);
if(sem_post(&
0)…
pthread_detach(pthread_self());
子线程得到文件路径名,复制到自己私有空间后。
就释放信号量。
子线程同过文件路径打开文件while(fgets(buf,512-1,src)!
=NULL)…依次读取文件中的一行,并判断文件中是否有指定的内容。
直到文件尾部。
if(sem_post(&
0)之后线程释放信号量,
pthread_detach(pthread_self());
并回收线程资源。
子线程如何判断主线程travel_file()文件遍历结束,这里在
while(value!
=thread_num)
sem_getvalue(&
current_thread,&
value);
主线程在结束前,重复得到信号量的值,如果资源都可用则可保证,所有子线程都已结束,那么主线程可正常退出,否则等待。
详细代码见备注四
心得体会
在任务一中,对于标准I/O,由于库中库函数中实现存在4KB数据缓冲区,所以对于小于4KB的
输入文件,小于4KB的块请求。
它都只一次全部请求4KB。
对于其I/O请求的时间几乎是相同的。
而对于大于4KB的文件。
请求数据块越大,一次所处理的数据越大,相对总时间会减少。
对于UINXI/O读写方式,由于不存在数据缓冲区,所以请求数据块越大,一次所处理的数据越大,相对总时间会减少。
尤其是对于大文件特别明显。
如64MB文件1B与256B竟然相差147倍。
对于mmap内容映射方式。
将文件内容映射到用户内存空间。
操作内存内容就相当于操作文件内容。
大大减少了文件操作的复杂度。
对于大文件在时间上存在一定的优势。
通过实验结果分析可知对于小于4K标准I/O,UNIXI/O,mmap几乎时间开销差不多。
但标准I/O由于存在输入缓冲区,存在一点点优势。
对于处理大文件,UNIXI/O比它两个存在相对的优势。
对于mmap方便处理大文件,但在时间效率上远远没有其它两种方式块。
任务二,通过对一些linux下系统接口的调用,实现了一个简单的shell,支持一些简单的操作命令,由于是直接对系统函数的调用,所以整个任务相对比较简单,linux下的shell就相当于windows下的批处理文件。
任务三,进程的同步,由于时间有限,翻阅资料查询了相关函数的使用,但是,时间有限,就没有完成,线程的同步,使用了线程的互斥锁,从而实现线程的并发互斥操作。
任务四,是对线程操作的综合运用,当搜索很多数据时,可以采用很多线程并发的形式,从而减少搜索需要的时间,提高效率。
备注
任务一代码
Create.c(创建指定大小文件)
#include<
stdio.h>
stdlib.h>
voidmain(intargc,char*argv[])
FILE*dest;
charsrc[]="
12345678"
intsize=8;
if(argc<
3){
请输入:
%sfilenamefilesize(K)\n"
argv[0]);
exit(0);
intfilesize=atoi(argv[2])*128;
if((dest=fopen(argv[1],"
w"
))==NULL){
openfilefailed.\n"
for(i=0;
i<
filesize;
i++){
if(fwrite(src,size,1,dest)<
1){
printf("
writeerror.\n"
}
fclose(dest);
Stardard.c(标准c)
sys/types.h>
unistd.h>
sys/stat.h>
fcntl.h>
intmain(intargc,char*argv[])
FILE*src,*dest;
chartemplate[]="
template-XXXXXX"
if(argc!
=3)
Usage:
%s<
soucrefile>
<
copysize>
exit(EXIT_FAILURE);
size_tsize=atoi(argv[2]);
//得到块的大小
charbuffer[size+1];
//申请缓冲区
if(mktemp(template)==NULL)//创建唯一文件名在当前目录下
createtempfilefailure!
if(((src=fopen(argv[1],"
r"
))==NULL)||((dest=fopen(template,"
))==NULL))
{//打开输入输出文件
openfilefailure!
remove(template);
fseek(src,-size,SEEK_END);
//偏移文件指针到末尾
do
size)//读取一个文件块
readfilefailure!
size_ti;
chartmp;
i++)//在缓冲区中置反数据内容
size)//写入输出文件
writefilefailure!
//移动块,直到文件结束
fclose(src);
fclose(dest);
//关闭输入输出文件
if(rename(template,argv[1])!
=0)//重命名该文件
removeoldfilefailure\n"
returnEXIT_SUCCESS;
Unix.c(unix接口)
sys/stat.h
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 操作系统 课程设计 实验 报告
![提示](https://static.bdocx.com/images/bang_tan.gif)