操作系统课程设计实验报告
姓名:**
学号:**
班级:**
地点:**
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(¤t_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(¤t_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(¤t_thread)<0)…
pthread_detach(pthread_self());
return NULL;
}
子线程得到文件路径名,复制到自己私有空间后。就释放信号量。子线程同过文件路径打开文件while(fgets(buf,512-1,src)!=NULL)…依次读取文件中的一行,并判断文件中是否有指定的内容。直到文件尾部。
if (sem_post(¤t_thread)<0)之后线程释放信号量,
pthread_detach(pthread_self());并回收线程资源。
子线程如何判断主线程travel_file() 文件遍历结束,这里在
while(value !=thread_num)
sem_getvalue(¤t_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(¤t_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(¤t_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(¤t_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(¤t_thread,&value);
printf("total :%d\n",total);
return EXIT_SUCCESS;
}