目录
1. 串行查找 job10/sfind.c
1.1 功能
程序 sfind 在文件或者目录中查找指定的字符串,并打印包含该字符串的行,示例如下:
- 在文件 file.c 中查找字符串 main
- 找到包含字符串 main 的行
- 打印文件名和该行
- 在目录 test 中查找字符串 main
- 假设目录 test 下存在文件
- test/hello/hello.c
- test/world/world.c
- 对目录 test 下的所有文件进行查找
- 找到包含字符串 main 的行
- 打印文件名和该行
1.2 实现思路
前置知识:操作系统实践 job4_bulibuli蛋的博客-CSDN博客
就是比较简单的文件目录遍历。
需要用到DIR,dirent,stat这三个结构体,具体见job4和注释。
- 主函数用stat判断文件类型
- 子函数用DIR打开目录,用dirent遍历目录下文件
- strstr函数,判断子串。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<dirent.h>
#include<sys/stat.h>
#include<unistd.h>
void find_file(char *path,char *target)
{
//printf("enter find file fun\n");
FILE *file=fopen(path,"r");
char line[256];
while(fgets(line,sizeof(line),file)){
if(strstr(line,target))
printf("%s:%s",path,line);
}
fclose(file);
}
//search the dir and find the file
void find_dir(char *path,char *target)
{
//printf("enter find dir fun\n");
DIR *dir=opendir(path);//open the dir
struct dirent *entry;
while(entry=readdir(dir))//read the dir & point to next obj
{
if(strcmp(entry->d_name,".")==0)
continue;
if(strcmp(entry->d_name,"..")==0)
continue;
char base[80]={0};
strcpy(base,path);
strcat(base,"/");
strcat(base,entry->d_name);//fill the path
//printf("base %s\n",base);
if(entry->d_type==DT_DIR)//file type is dir
{
//printf("dir %s\n",entry->d_name);
find_dir(base,target);
}
else if(entry->d_type==DT_REG)
{
//printf("file %s\n",entry->d_name);
find_file(base,target);
}
}
closedir(dir);
}
int main(int argc,char *argv[])
{
if(argc!=3)
{
puts("usage: sfind file string");
return 0;
}
char *path = argv[1];
char *string = argv[2];
struct stat info;
stat(path,&info);
if(S_ISDIR(info.st_mode))
find_dir(path,string);
else
find_file(path,string);
return 0;
}
1.3 结果展示
生成如下文件树:
hello.c和world.c和find.c是从sfind.c里复制过去的.
2. 并行查找 job10/pfind.c
2.1 功能
功能与 sfind 相同
- 要求使用多线程完成
- 主线程创建若干个子线程
- 主线程负责遍历目录中的文件,遍历到目录中的叶子节点时,将叶子节点发送给子线程进行处理
- 两者之间使用生产者消费者模型通信,主线程生成数据,子线程读取数据
2.2 图示
- 主线程创建 2 个子线程
- 主线程遍历目录 test 下的所有文件 把遍历的叶子节点 path 和目标字符串 string,作为任务,发送到任务队列
- 子线程 不断的从任务队列中读取任务 path 和 string 在 path 中查找字符串 string
2.3 参考代码
2.4 实现思路
换句话说就是
- 主线程负责递归遍历目录找到叶子节点,并将路径加入任务队列中(任务队列类似于生产者消费者问题,主线程类似于生产者)
- 子线程负责从任务队列取出任务,并处理子串问题,类似于消费者。
- 特殊任务:is_end属性,如果子任务结束,就要产生特殊任务,告诉进程结束任务。
生产者消费者问题我这用了信号量请看job9 操作系统实践 job9 基于信号量实现线程同步_bulibuli蛋的博客-CSDN博客
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<string.h>
#include<dirent.h>
#include<sys/stat.h>
#include<stdlib.h>
#define WORKER_NUMBER 4
#define CAPACITY 100
int in=0,out=0;
//任务队列
struct Task{
int is_end;
char path[128];
char string[128];
}buffer[CAPACITY];
//取任务
struct Task get_item()
{
struct Task item;
item=buffer[out];
out=(out+1)%CAPACITY;
return item;
}
//放任务
void put_item(struct Task item)
{
buffer[in]=item;
in=(in+1)%CAPACITY;
}
//信号量
typedef struct{
int value;
pthread_mutex_t mutex;
pthread_cond_t cond;
}sema_t;
sema_t mutex_sema;//互斥信号量
sema_t empty_buffer_sema;//空信号量
sema_t full_buffer_sema;//满信号量
//信号量初始化
void sema_init(sema_t *sema,int value)
{
sema->value=value;
pthread_mutex_init(&sema->mutex,NULL);
pthread_cond_init(&sema->cond,NULL);
}
//p操作
void sema_wait(sema_t *sema)
{
pthread_mutex_lock(&sema->mutex);
while(sema->value<=0)
{
pthread_cond_wait(&sema->cond,&sema->mutex);
}
sema->value--;
pthread_mutex_unlock(&sema->mutex);
}
//v操作
void sema_signal(sema_t *sema)
{
pthread_mutex_lock(&sema->mutex);
++sema->value;
pthread_cond_signal(&sema->cond);
pthread_mutex_unlock(&sema->mutex);
}
//消费者
void *worker_entry(void *arg)
{
struct Task worker;
while(1)
{
sema_wait(&full_buffer_sema);
sema_wait(&mutex_sema);
worker=get_item();
if(worker.is_end)
{
break;
}
find_file(worker.path,worker.string);
sema_signal(&mutex_sema);
sema_signal(&empty_buffer_sema);
}
if(worker.is_end)
{
sema_signal(&mutex_sema);
sema_signal(&empty_buffer_sema);
}
}
//找子串函数
void find_file(char *path,char *target)
{
//printf("enter find file fun\n");
FILE *file=fopen(path,"r");
char line[256];
while(fgets(line,sizeof(line),file)){
if(strstr(line,target))
printf("%s:%s",path,line);
}
fclose(file);
}
//生产者
void find_dir(char *path,char *target)
{
//printf("enter find dir fun\n");
DIR *dir=opendir(path);//open the dir
struct dirent *entry;
while(entry=readdir(dir))//read the dir & point to next obj
{
if(strcmp(entry->d_name,".")==0)
continue;
if(strcmp(entry->d_name,"..")==0)
continue;
char base[80]={0};
strcpy(base,path);
strcat(base,"/");
strcat(base,entry->d_name);//fill the path
//printf("base %s\n",base);
if(entry->d_type==DT_DIR)//file type is dir
{
//printf("dir %s\n",entry->d_name);
find_dir(base,target);
}
//将叶子路径放入任务队列
//put the leaf path into task list
else if(entry->d_type==DT_REG)
{
//printf("file %s\n",entry->d_name);
sema_wait(&empty_buffer_sema);
sema_wait(&mutex_sema);
struct Task item;
item.is_end=0;
strcpy(item.path,base);
strcpy(item.string,target);
put_item(item);
sema_signal(&mutex_sema);
sema_signal(&full_buffer_sema);
}
}
closedir(dir);
}
int main(int argc,char *argv[])
{
char *path=argv[1];
char *target=argv[2];
if(argc!=3)
{
puts("usage: sfind file string");
return 0;
}
struct stat info;
stat(path,&info);
//如果是目录,就是生产者消费者问题,
if(S_ISDIR(info.st_mode))
{
//初始化信号量
pthread_t consumer_tid[WORKER_NUMBER];
sema_init(&mutex_sema,1);
sema_init(&empty_buffer_sema,CAPACITY-1);
sema_init(&full_buffer_sema,0);
find_dir(path,target);
//开子线程
for(int i=0;i<WORKER_NUMBER;i++)
{
pthread_create(&consumer_tid[i],NULL,worker_entry,NULL);
}
//结束工作
for(int i=0;i<WORKER_NUMBER;i++)
{
sema_wait(&empty_buffer_sema);
sema_wait(&mutex_sema);
struct Task item;
item.is_end=1;
put_item(item);
sema_signal(&mutex_sema);
sema_signal(&full_buffer_sema);
}
//让没运行完的运行完
for(int i=0;i<WORKER_NUMBER;i++)
{
pthread_join(consumer_tid[i],NULL);
}
return 0;
}
//如果是文件,就直接找子串
else find_file(path,target);
return 0;
}