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

时间:2024.4.21

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

姓名:**

学号:**

班级:**

地点:**

20xx年**月**日

任务说明

共完成四个任务,任务一:I/O系统调用开销比较;任务二:实现一个简单的shell;任务三:进程/线程同步;任务四:文件内容的并行搜索。其中任务一,完成了标准c和unix下的实验,mmap没有完成,任务三完成了线程同步,进程同步没有完成。

任务一要求

在LINUX平台用C编程逆序一个文本文件,注意先是逆转的结果必须是原文件名。

请分别使用 :

(1)标准C的I/O库函数:fopen、fread、fwrite

(2)Unix的I/O函数:open、read、write

(3)open和mmap

要求尽量考虑效率,比较三种方法的性能。

任务二要求

实现一个简单的shell(命令行解释器),类似于sh,bash,csh等。你的shell必须支持以下内部命令: cd<目录>更改当前的工作目录到另一个<目录>。如果<目录>未制定,输出当前工作目录。如果<目录>不存在,应当有适当的错误信息提示,制革命令应该也能改变PWD的环境变量;

enbiron 列出所有环境变量字符串的设置(类似于Unix系统下的env命令);

echo<内容> 显示echo后的内容且换行;

help 简短概要的输出你的shell的使用方法和基本功能;

jobs 输出shell当前的一系列子进程,必须提供子进程的命名和PID号;

quit,exit,bye 退出shell。

所有的内部命令应当优先于在$PATH中同名的程序。

任何非内部命令必须请求shell创建一个新进程,且该子进程执行指定的程序。这个新进程必需继承shell的环境变量和制定的命令行参数。

任务三要求

编程实现下图的效果,要求分别使用进程和线程

1. 进程+SYS V信号量

2. 线程实现+Posix同步操作API

任务四要求

在阅读大型项目代码时,经常要搜索某个标识符,找出该标识符的声明、定义或引用的地方(某文件的哪一行)。本任务要求实现一个程序idfind,其使用格式如下:

idfind [-j n] id dirname

选项 -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) 使用UINX I/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_t size =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)<size)

…….

}while(fseek(src,-size*2,SEEK_CUR) ==0);

使用fseek定位函数从文件尾部以此-size,-2*size,-3*size …位置读取数据到buffer 中,在buffer 中置反数据内容,并写入输出文件中。直到文件所有数据都处理完毕。也就是说fseek从当前位置向前偏移发送错误时,即文件数据都处理完毕。

if (rename(template,argv[1]) !=0)…… 重命名输出文件并自动删除重复的文件。

2. UNIX I/O读写方式,函数接口的不同。其他算法和标准I/O 相同。

3.mmap 文件映射方式

do

{

…_src=mmap(NULL,size,PROT_READ,MAP_SHARED,src,position-size)

…s_dest=mmap(NULL,size,…,dest,statbuf.st_size-position))

size_t count=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(用户命令为内部命令)

直接处理;

else if(用户命令为外部命令)

创建子进程执行命令;

else

提示错误信息;

}

创建子进程要使用fork()函数,执行新的命令要使用exec()系列函数,通常shell是等待子进程结束后在接受用户新的输入,所以这里应该使用waitpid()函数。

fork()原型为:pid_t fork(void);

fork建立一个子进程,父进程继续运行,子进程在同样的位置执行同样的程序。对于父进程,fork()返回子进程的pid,对于子进程,fork()返回0。出错时返回-1。

exec系列函数用新的进程映像置换当前的进程映像,这些函数的第一个参数是待执行程序的路径名(文件名)。这些函数调用成功后不会返回,其进程的正文(text),数据(data),bss和堆栈(stack)段被待执行程序覆盖。但是进程的pid和所有打开的文件描述符没有改变,同时悬挂信号被清除,信号重置为缺省行为。

wait(),waitpid()可用来等待子进程结束。

函数原型为:

#include<sys/wait.h>

Pid_t wait(int *stat)loc);

Pid_t waitpid(pid_t pid,int *stat_loc,int options);

当进程调用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或这几者的组合。

任务实施

char command(char *s) {return “xxx”} 命令处理,返回命令处理后的字符串

help(){

printf( "帮助。。。。。。。”)

}// 显示帮助

fgets(n,100,stdin); 从命令行获得命令

switch(b)// 分析命令处理后的返回字符

{

} case 1:if(chdir(n+3)!=0) //执行相应的命令 help();// 命令不存在时,显示帮助 case 0:printf(" 没有此命令,请重新输入正确的命令。\n", getpid()); break;

详细代码见备注二

任务三 进程/线程同步

任务分析

依据题目需求,采用三个线程互斥对象,创建6个线程,6个线程运行时的函数。 线程一,由于没有和其他线程并发执行,所以先创建后,加入到主线程,等其执行完后再创建其他线程。 线程二,线程三,并发执行,且同时创建,同时当线程二执行结束后,线程四可以执行,同时,和线程三又控制着线当线程二执行结束后,取消对互斥对象一、二的锁定,而线程四这时可以锁定互斥对象一,所以线程四开始执行,同程五的执行时间,所以线程二锁定两个线程互斥对象,互斥对象一和互斥对象二。线程三锁定线程互斥对象三。 时线程四锁定互斥对象一。当线程三和线程线程二都执行结束后,互斥对象二、三都释放了。所以线程五可以开始执行,同时线程五锁定互斥对象二、三。当线程四,五都执行结束后,释放了互斥对象一、二和三,所以线程六可以锁定互斥对象一、二、三,这时线程开始执行。

任务实施

pthread_mutex_t mymutex1 = PTHREAD_MUTEX_INITIALIZER;// 创建互斥对象1.2.3

void *thread_function2(void *arg)// 线程处理函数

{

}

pthread_t mythread1; 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("error joining thread2."); abort(); int i = 0; pthread_mutex_lock(&mymutex1); pthread_mutex_lock(&mymutex2); …… pthread_mutex_unlock(&mymutex1); pthread_mutex_unlock(&mymutex2); return NULL;

详细代码见备注三

任务四 文件内容的并行搜索

任务分析

本任务要求搜索目录下所有文件的内容,显然我们需要通过API 来获取本目录中的文件名,从而通过得到的文件名来读取该文件,进而在文件中搜索标识符。但是,目录中有可能有子目录,按照任务要求,子目录下的文件同样需要进行搜索,而子目录又有可能有子目录。

显然这是一个典型的递归问题。

这里在主线程中使用了一个递归函数:

void travel_file(void);

该函数会遍历整个目录,如果当前搜索到的是一个文件,如果线程数量(信号量)没有达到上限值,则创建一个线程,并把该文件名传递给空闲的线程,如果线程数达到上限值,则主线程则必须等待直到某个线程结束。

这里便涉及到线程同步的问题,通常处理Posix线程同步可采用Posix信号量、互斥对象或条件对象。在本程序中采用的是一个Posix信号量来处理线程同步,因为使用Posix信号量简单、方便,非常容易编程实现。采用一个互斥信号量来处理文件名的复制,并且保证创建的线程先

运行,并保证文件名的正确复制。

子线程只需从主线程中获取主线程travel_file() 中所得到的路径名,所以这里使用了一个名为current_thread信号量,顾名思义,主线程遍历到一个新的文件,检测信号量的值,如果小于最大值,则主线程创建一个子线程,启动子线程开始当前文件的搜索任务的时候,搜索

完成后,便释放这个信号量,,由于存储路径名的是一个全局变量,所以子线程必须将travel_file() 所得到的正则文件路径拷贝到自身的私有空间中,为了防止线程对该变量的竞争,还必须设置一个copied 互斥量,拷贝之前至拷贝结束期间都必须阻塞该信号量。

任务实施

主线程代码如下:

void travel_file(void)

{

if (S_ISREG(statbuf.st_mode))

{

if (sem_wait(&current_thread)<0)

pthread_mutex_lock(&mutex);

if (pthread_create(&tid,NULL,compare,(void*)pathname)!=0)

pthread_mutex_lock(&mutex);

pthread_mutex_unlock(&mutex);

}

else

if(S_ISDIR(statbuf.st_mode))

{

while((dirent=readdir(dir))!=NULL)

{

if ((strcmp(dirent->d_name,".")==0)||(strcmp(dirent->d_name,"..")==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(&current_thread)<0) … 用于信号量的可用资料,如果有资源可用使用,则

if (pthread_create(&tid,NULL,compare,(void*)pathname)!=0) … 创建一个新的线程,

pthread_mutex_lock(&mutex);用于保证文件名目录的正确复制。如果是文件夹,

while((dirent=readdir(dir))!=NULL)… travel_file()

则readdir(dir)得到当前目录下的文件,并进行递归。知道当前目录下的所有文件都被遍历。

子线程:

void *compare(void *args)

{

strcpy(fp,(char*)args);

pthread_mutex_unlock(&mutex); …

while(fgets(buf,512-1,src)!=NULL)

{

if (strstr(buf,id)!=NULL)

{

total++;

printf("%s:line %d/t%s",fp,line,buf);

}

}

if (sem_post(&current_thread)<0)…

pthread_detach(pthread_self());

return NULL;

}

子线程得到文件路径名,复制到自己私有空间后。就释放信号量。子线程同过文件路径打开文件while(fgets(buf,512-1,src)!=NULL)…依次读取文件中的一行,并判断文件中是否有指定的内容。直到文件尾部。

if (sem_post(&current_thread)<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的文件。请求数据块越大,一次所处理的数据越大,相对总时间会减少。

对于UINX I/O 读写方式,由于不存在数据缓冲区,所以请求数据块越大,一次所处理的数据越大,相对总时间会减对于mmap 内容映射方式。将文件内容映射到用户内存空间。操作内存内容就相当于操作文件内容。大大减少了文通过实验结果分析可知对于小于4K标准I/O,UNIX I/O ,mmap几乎时间开销差不多。但标准I/O 由于存在输入少。尤其是对于大文件特别明显。如64MB文件1B与256B 竟然相差147倍。 件操作的复杂度。对于大文件在时间上存在一定的优势。 缓冲区,存在一点点优势。对于处理大文件,UNIX I/O 比它两个存在相对的优势。对于mmap方便处理大文件,但在时间效率上远远没有其它两种方式块。

任务二,通过对一些linux下系统接口的调用,实现了一个简单的shell,支持一些简单的操作命令,由于是直接对系统函数的调用,所以整个任务相对比较简单,linux下的shell就相当于windows下的批处理文件。

任务三,进程的同步,由于时间有限,翻阅资料查询了相关函数的使用,但是,时间有限,就没有完成,线程的同步,使用了线程的互斥锁,从而实现线程的并发互斥操作。

任务四,是对线程操作的综合运用,当搜索很多数据时,可以采用很多线程并发的形式,从而减少搜索需要的时间,提高效率。

备注

任务一代码

Create.c(创建指定大小文件)

#include <stdio.h>

#include <stdlib.h>

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

{

FILE *dest;

char src[] = "12345678";

int size = 8;

if(argc<3) {

printf("请输入:%s filename filesize(K)\n",argv[0]);

exit(0);

}

int filesize = atoi(argv[2]) * 128;

if((dest = fopen(argv[1],"w"))==NULL) {

printf("open file failed.\n");

exit(0);

}

int i = 0;

for(i = 0;i < filesize; i++) {

if(fwrite(src,size,1,dest)<1) {

printf("write error.\n");

}

}

fclose(dest);

}

Stardard.c(标准c)

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#include <unistd.h>

#include <sys/stat.h>

#include <fcntl.h>

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

{

FILE *src,*dest;

char template[]="template-XXXXXX";

if (argc !=3)

{

printf("Usage:%s <soucrefile> <copy size>\n",argv[0]);

exit(EXIT_FAILURE);

}

size_t size =atoi(argv[2]); //得到块的大小

char buffer[size+1]; //申请缓冲区

if (mktemp(template)==NULL) //创建唯一文件名在当前目录下 {

printf("create temp file failure!\n");

exit(EXIT_FAILURE);

}

if (((src =fopen(argv[1],"r"))==NULL)||((dest =fopen(template,"w"))==NULL)) { //打开输入输出文件

printf("open file failure!\n");

remove(template);

exit(EXIT_FAILURE);

}

fseek(src,-size,SEEK_END); //偏移文件指针到末尾

do

{

if (fread(buffer,1,size,src)<size) //读取一个文件块

{

printf("read file failure!\n");

remove(template);

exit(EXIT_FAILURE);

}

size_t i;

char tmp;

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)<size) //写入输出文件

{

printf("write file failure!\n");

remove(template);

exit(EXIT_FAILURE);

}

}while(fseek(src,-size*2,SEEK_CUR) ==0); //移动块,直到文件结束 fclose(src);fclose(dest); //关闭输入输出文件

if (rename(template,argv[1]) !=0) //重命名该文件

{

printf("remove old file failure\n");

remove(template);

exit(EXIT_FAILURE);

}

return EXIT_SUCCESS;

}

Unix.c(unix接口)

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#include <unistd.h>

#include <sys/stat.h>

#include <fcntl.h>

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

{

int src,dest;

char template[]="template-XXXXXX";

if (argc !=3) //判断参数是否合理

{

printf("Usage:%s <soucrefile> <copy size>\n",argv[0]); exit(EXIT_FAILURE);

}

size_t size =atoi(argv[2]); //得到块的大小

char buffer[size+1]; //申请缓冲区

if (((src =open(argv[1],O_RDONLY))<0)||((dest =mkstemp(template))<0)) { //打开输入文件,创建唯一文件名并打开作为输出

printf("open file failure!\n");

exit(EXIT_FAILURE);

}

lseek(src,-size,SEEK_END); //偏移文件指针到末尾

do

{

if (read(src,buffer,size)<size) //读取一个文件块

{

printf("read file failure!\n");

remove(template);

exit(EXIT_FAILURE);

}

size_t i;

char tmp;

for(i =0; i<size/2; i++) //在缓冲区中置反数据内容

{

tmp = buffer[i];

buffer[i] = buffer[size-i-1];

buffer[size-i-1] = tmp;

}

if (write(dest,buffer,size)<size) //写入输出文件

{

printf("write file failure!/n");

remove(template);

exit(EXIT_FAILURE);

}

}while(lseek(src,-size*2,SEEK_CUR) !=-1); //移动块,直到文件结束 close(src);close(dest); //关闭输入输出文件

if (rename(template,argv[1]) !=0) //重命名该文件

{

printf("remove old file failure\n");

remove(template);

exit(EXIT_FAILURE);

}

return EXIT_SUCCESS;

}

Mmap.c

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#include <unistd.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <sys/mman.h>

#include <errno.h>

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

{

int src,dest;

char template[]="template-XXXXXX";

struct stat statbuf;

char *s_src,*s_dest;

if (argc !=3) //判断参数是否合理

{

printf("Usage:%s <soucrefile> <copy size>\n",argv[0]);

exit(EXIT_FAILURE);

}

size_t size =atoi(argv[2]); //得到快的大小,注意只能处理4KB的倍数大小 if (((src =open(argv[1],O_RDONLY))<0)||((dest =mkstemp(template))<0))

{ //打开输入文件流,在当前目录下创建一个唯一文件名的空文件,并打开 printf("open file failure!\n");

exit(EXIT_FAILURE);

}

if (fstat(src,&statbuf)<0)//的到输入文件的属性

{

printf("fstat source!");

remove(template);

return 0;

}

if (ftruncate(dest,statbuf.st_size)<0)//将输出文件截断为输入文件的大小

{

printf("ftruncate failure!\n");

remove(template);

exit(EXIT_FAILURE);

}

size_t position=statbuf.st_size; //得到输入文件的大小

do

{

if ((s_src=mmap(NULL,size,PROT_READ,MAP_SHARED,src,position-size))==MAP_FAILED) { //映射输入文件每个块到用户空间

printf("src mmap failure!\n");

remove(template);

exit(EXIT_FAILURE);

}

if ((s_dest=mmap(NULL,size,PROT_WRITE,MAP_SHARED,dest,statbuf.st_size-

position))==MAP_FAILED)

{ //映射输出文件到用户空间

printf("dest mmap failure!\n");

remove(template);

exit(EXIT_FAILURE);

}

size_t count=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); //改变块的的地址

close(src);close(dest);

if (rename(template,argv[1]) !=0) //重命名输出文件,并删除输入文件

{

printf("remove old file failure\n");

remove(template);

exit(EXIT_FAILURE);

}

return EXIT_SUCCESS;

}

任务二代码

Shell.c

#include <stdio.h>

#include <string.h>

#include <unistd.h>

#include <sys/wait.h>

#include <stdlib.h>

#include <sys/types.h>

#include <errno.h>

#include <sys/stat.h>

char command(char *s)

{

if(!strncasecmp(s,"exit",4)) return 'q';

else if(!strncasecmp(s,"quit",4)) return 'q';

else if(!strncasecmp(s,"bye",4)) return 'q';

else if(!strncasecmp(s,"cd",2)) return 1;

else if(!strncasecmp(s,"ls",2)) return 2;

else if(!strncasecmp(s,"rm",2)) return 3;

else if(!strncasecmp(s,"mkdir",5)) return 4;

else if(!strncasecmp(s,"echo",4)) return 5;

else if(!strncasecmp(s,"help",4)) return 6;

else if(!strncasecmp(s,"env",3)) return 7;

else if(!strncasecmp(s,"jobs",4)) return 8;

else return 0;

}

void help(){

printf( " 帮助\n"

" 命令 功能\n"

" cd 更改当前的工作目录到另一个<目录>\n"

" environ 列出所有环境变量字符串的设置\n"

" echo 显示echo后面的内容并换行\n"

" help 显示帮助信息\n"

" jobs 输出shell当前的一系列子进程,包括子进程的命名和PID号\n" " ls 显示当前目录的所有文件\n"

" rm 删除当前目录下的文件或目录\n"

" mkdir 在当前目录下创建新目录\n"

" quit|exit|bye 退出shell\n");

}

int main(void)

{

char n[100],n1[100];

char b;

pid_t pid;

help();

while(1)

{

memset(n,0,100);

printf(" 请输入命令:");

fgets(n,100,stdin);

n[strlen(n)-1]=0;

b=command(n);

if(b=='q') break;

switch(b)

{

case 1:if(chdir(n+3)!=0)

printf(" 打开工作目录(%s)失败!\n",n+3);

printf(" 当前工作目录:'%s'\n",getcwd(n1,100));

break;

case 2:if((pid=fork())<0)

{

printf(" fork error\n");

exit(EXIT_FAILURE);

}

else if(pid==0)

{

if(execl("/bin/ls","ls",NULL)<0);

printf(" execl error\n");

exit(EXIT_FAILURE);

}

waitpid(pid,0,0);

break;

case 3:remove(n+3);

printf(" 文件已删除!\n");

break;

case 4:mkdir(n+6,S_IRWXU);

printf(" 文件创建成功!\n");

break;

case 5:printf(" %s\n",n+5);

break;

case 6:help();

break;

case 7:if((pid=fork())<0)

{

printf(" fork error\n");

exit(EXIT_FAILURE);

}

else if(pid==0)

{

if(execl("/bin/env","env",NULL)<0)

printf(" execl error\n");

exit(EXIT_FAILURE);

}

waitpid(pid,0,0);

break;

case 8:system("ps");

break;

case 0:printf(" 没有此命令,请重新输入正确的命令。\n", getpid()); help();

break;

}

}

}

任务三代码

Thread.c

#include <pthread.h>

#include <stdlib.h>

#include <unistd.h>

#include <stdio.h>

pthread_mutex_t mymutex1 = PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_t mymutex2 = PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_t mymutex3 = PTHREAD_MUTEX_INITIALIZER;

void *thread_function1(void *arg)

{

int i = 0;

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

printf("I am thread 1\n");

fflush(stdout);

sleep(1);

}

return NULL;

}

void *thread_function2(void *arg)

{

int i = 0;

pthread_mutex_lock(&mymutex1);

pthread_mutex_lock(&mymutex2);

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

printf("I am thread 2\n");

fflush(stdout);

sleep(1);

}

pthread_mutex_unlock(&mymutex1);

pthread_mutex_unlock(&mymutex2);

return NULL;

}

void *thread_function3(void *arg)

{

int i = 0;

pthread_mutex_lock(&mymutex3);

for(i = 0;i < 5;i++){

printf("I am thread 3\n");

fflush(stdout);

sleep(1);

}

pthread_mutex_unlock(&mymutex3);

return NULL;

}

void *thread_function4(void *arg)

{

int i = 0;

pthread_mutex_lock(&mymutex1);

for(i = 0;i < 5;i++){

printf("I am thread 4\n");

fflush(stdout);

sleep(1);

}

pthread_mutex_unlock(&mymutex1);

return NULL;

}

void *thread_function5(void *arg)

{

int i = 0;

pthread_mutex_lock(&mymutex2);

pthread_mutex_lock(&mymutex3);

for(i = 0;i < 5;i++){

printf("I am thread 5\n");

fflush(stdout);

sleep(1);

}

pthread_mutex_unlock(&mymutex2);

pthread_mutex_unlock(&mymutex3);

return NULL;

}

void *thread_function6(void *arg)

{

int i = 0;

pthread_mutex_lock(&mymutex1);

pthread_mutex_lock(&mymutex2);

pthread_mutex_lock(&mymutex3);

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

printf("I am thread 6\n");

fflush(stdout);

sleep(1);

}

pthread_mutex_unlock(&mymutex1);

pthread_mutex_unlock(&mymutex2);

pthread_mutex_unlock(&mymutex3);

return NULL;

}

int main(void)

{

pthread_t mythread1;

pthread_t mythread2;

pthread_t mythread3;

pthread_t mythread4;

pthread_t mythread5;

pthread_t mythread6;

pthread_create(&mythread1,NULL,thread_function1,NULL); if(pthread_join(mythread1,NULL)) {

printf("error joining thread1.");

abort();

}

pthread_create(&mythread2,NULL,thread_function2,NULL); pthread_create(&mythread3,NULL,thread_function3,NULL); pthread_create(&mythread4,NULL,thread_function4,NULL); pthread_create(&mythread5,NULL,thread_function5,NULL); pthread_create(&mythread6,NULL,thread_function6,NULL); if(pthread_join(mythread2,NULL)) {

printf("error joining thread2.");

abort();

}

if(pthread_join(mythread3,NULL)) {

printf("error joining thread3.");

abort();

}

if(pthread_join(mythread4,NULL)) {

printf("error joining thread4.");

abort();

}

if(pthread_join(mythread5,NULL)) {

printf("error joining thread5.");

abort();

}

if(pthread_join(mythread6,NULL)) {

printf("error joining thread6.");

abort();

}

exit(0);

}

任务四代码

Idfinid.c

#include <stdio.h>

#include <dirent.h>

#include <sys/stat.h>

#include <string.h>

#include <stdlib.h>

#include <pthread.h>

#include <semaphore.h>

char *id;// 查询的字符

char pathname[512];

int total = 0;// 查询到的目标字符的总数

static pthread_mutex_t copied = PTHREAD_MUTEX_INITIALIZER;//copied 互斥信号量 static sem_t current_thread;

void *compare(void *args)//子线程文件内部标志比较函数

{

char fp[512];

strcpy(fp,(char*)args);// copy文件路径到子线程私有空间

pthread_mutex_unlock(&copied);//释放copied信号量,主线程可用改变和子线程并行执行 FILE *src;

if ((src = fopen(fp,"r")) == NULL)

{

printf("fopen error!\n");

exit(EXIT_FAILURE);

}

int line =0;

char buf[512];

while(fgets(buf,512-1,src) != NULL)//每次读取一行数据到buf

{

line ++;//记录行数

if (strstr(buf,id) != NULL)//判断该行中是否存在字串为标示串

{

total ++;

printf("%s:line %d\t%s",fp,line,buf);//输出找到的标示串详细信息

}

}

fclose(src);

if (sem_post(&current_thread) < 0)//释放信号量

{

printf("sem_post error!\n");

exit(EXIT_FAILURE);

}

pthread_detach(pthread_self());//释放当前子线程所占用的资源

return NULL;

}

void travel_file(void)//主线程递归搜索目录所有文件

{

struct stat statbuf;

struct dirent *dirent;

DIR *dir;

pthread_t tid;

if (lstat(pathname,&statbuf) < 0)// 得到文件属性

{

printf("%s lstat error!\n",pathname);

exit(EXIT_FAILURE);

}

if (S_ISREG(statbuf.st_mode))//如果是正则文件

{

if (sem_wait(&current_thread) < 0)//等待可用的线程

{

printf("sem_wait error!\n");

exit(EXIT_FAILURE);

}

pthread_mutex_lock(&copied); //给路径加锁

if (pthread_create(&tid,NULL,compare,(void*)pathname)!=0) //创建子线程

{

printf("pthread_create errro!\n");

exit(EXIT_FAILURE);

}

pthread_mutex_lock(&copied);//该加锁保证子线程在pathname修改前,完成pathname的复制 pthread_mutex_unlock(&copied);

} else if(S_ISDIR(statbuf.st_mode))//如果该项是目录

{

if ((dir = opendir(pathname)) == NULL)//打开该目录

{

printf("opendir error!\n");

exit(EXIT_FAILURE);

}

while((dirent = readdir(dir)) != NULL)//递归该目录

{

if ((strcmp(dirent->d_name,".") == 0)||(strcmp(dirent->d_name,"..") == 0))

continue ;//如果该目录是...则忽略

strcat(pathname,"/");

strcat(pathname,dirent->d_name);//修改pathname

travel_file();//递归

pathname[strlen(pathname)-strlen(dirent->d_name)-1] = '\0';//改回pathname

}

if (closedir(dir) < 0)//关闭该目录

{

printf("closedir error!\n");

exit(EXIT_FAILURE);

}

}

}

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

{

int thread_num;// 线程数

char *dirname;// 目录名或文件名

if ((argc!=3 && argc!=5)||((argc ==5)&&(strcmp(argv[1],"-j")!=0)))

{//判断程序参数是否合理

printf("Usage :%s [-j n] id dirname\n",argv[0]);

return EXIT_FAILURE;

}

if (argc == 3)// 处理参数,单线程模式

{

thread_num =1;

id =argv[1];

dirname =argv[2];

} else {// 多线程模式

thread_num =atoi(argv[2]);

id =argv[3];

dirname =argv[4];

}

if (sem_init(&current_thread,0,thread_num) < 0)//信号量初始化最大值为线程数

{

printf("sem_init error!\n");

return EXIT_FAILURE;

}

if (dirname[strlen(dirname) - 1] == '/')

dirname[strlen(dirname) - 1] = '\0';//处理输入文件末尾的/

strcpy(pathname,dirname);

travel_file();

int value=0;

while(value != thread_num)// 等待所有子线程都结束

sem_getvalue(&current_thread,&value);

printf("total :%d\n",total);

return EXIT_SUCCESS;

}

更多相关推荐:
操作系统课程设计实验报告(附代码)

操作系统课程设计报告题目银行家算法设计学院专业班级学生学号指导教师1摘要银行家算法是一个用来预防系统进入死锁状态的算法用它可以判断系统的安全性如果系统当前处于安全状态则可以为申请资源的进程分配资源如果不是安全状...

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

操作系统课程设计实验报告学部专业班级姓名学号指导教师摘要目录1概论2系统设计3系统实现4系统测试5结论参考文献附件摘要操作系统是计算机学科的核心课程对于计算机科学与技术专业的学生尤为重要此次课程设计旨在加强我们...

操作系统课程设计实验报告(以Linux为例)

操作系统课程设计实验报告学号姓名苏州大学计算机科学与技术学院20xx年9月操作系统课程设计实验报告目录目录1一实验环境2二实验报告总体要求2实验一编译LINUX内核3实验二观察LINUX行为7实验三进程间通信1...

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

武汉工程大学计算机科学与工程学院综合设计报告设计名称操作系统综合设计设计题目线程同步学生学号120xx80205专业班级学生姓名何洲学生成绩指导教师职称章瑾副教授完成时间至武汉工程大学计算机科学与工程学院制说明...

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

题目题目编号院系班级小组成员操作系统课程设计实验报告书售票员与乘客信号量操作2计算机科学与技术软件服务与外包学院11级9班组长杨扬学号111810059组员沈菲菲学号111810060组员学号20xx0630目...

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

操作系统课程设计实验报告nachos专业计算机科学与技术班级20xx级2班姓名李霜学号20xx00130082目录Laboratory3SynchronizationUsingSemaphores信号量实现同步...

操作系统课程设计试验报告格式

四川大学操作系统课程设计报告学院专业年级组编号组成员软件学院第X组乔心轲姓名0743111340学号张雯姓名XXXXXXXX学号康小芳姓名XXXXXXXX学号提交时间20xx年月日指导教师评阅成绩XXX1XXX...

操作系统课程设计报告

西安郵電大學操作系统课程设计院系名称学生姓名专业名称班级学号时间报告书20xx年4月13日至20xx年4月24日1实验目的操作系统是控制和管理计算机硬件和软件资源的虚拟机其中的文件系统是对软件和设备进行管理的系...

操作系统课程设计报告

计算机科学技术学院操作系统原理课程设计报告题目模拟存储器专业网络工程班级网络131姓名徐策成学号20xx17030130指导老师李益民20xx年6月1操作系统原理课程设计任务书网络工程专业网络131班一课程设计...

操作系统课程设计-文件管理实验报告

操作系统课程实验报告20xx20xx年度第1学期院系学号姓名任课教师成绩评定实验一题目文件管理完成日期年月日1实验目的了解文件管理的功能和任务理解文件系统组成和特点熟悉文件系统的访问和操作实验要求用高级语言编写...

操作系统课程设计二级文件系统

操作系统课程设计报告专业计算机信息处理学号0820xx28姓名杨馨雨提交日期20xx714操作系统课程设计报告设计目的1课程设计目的是通过一个简单多用户文件系统的设计加深理解文件系统的内部功能和内部实现2结合数...

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

河北大学工商学院课程设计题目操作系统课程设计学部信息学部学科门类电气信息专业计算机学号20xx482370姓名耿雪涛指导教师朱亮20xx年6月19日主要内容一设计目的通过模拟操作系统的实现加深对操作系统工作原理...

操作系统课程设计实验报告(36篇)