基于linux与线程池实现文件管理
项目要求
1.基本
用线程池实现一个大文件夹的拷贝,大文件夹嵌套很多小文件;实现复制到指定文件夹的全部文件夹。
2.扩充功能
显示进度条;拷贝耗时统计;类似linux的tree,不能直接用system()与exec()等函数;根据文件类型拷贝;搜索文件;删除文件等。(暂时加了这么一些功能)
8月17日补:移动、复制到的目录已包含该文件则选择覆盖或者加命名。
实现思路
先完成基本,逐步完成扩展再优化重构代码。
实现过程
基本功能
基于linux,通过线程池实现的。核心就是线程池的三大基本功能--线程例程、添加线程、销毁线程池。由这三个为基础,对项目进行展开。基本功能,即通过递归读取目录,通过strucr dirent *p这个结构体来实现判断文件类型。如果是普通文件,直接在新目录用文件IO的读写实现拷贝功能(包括标准IO、系统IO,还有共享内存也可以实现),拷贝那里用“添加线程”,保证可以多线程实现拷贝;如果是目录文件,就先创建文件夹--mkdir(),再sprintf拼接字符串以及函数的递归实现子级目录的拷贝。
->拷贝代码
void *myregcp(void *myarg) { struct copypath *mypath=(struct copypath *)myarg; //系统IO的复制 int fd1,fd2; fd1=open(mypath->oldpath,O_RDONLY); fd2=open(mypath->newpath,O_CREAT|O_TRUNC|O_RDWR,0777); if(fd1==-1) { perror("打开1失败\n"); return NULL; } if(fd2==-1) { perror("打开2失败\n"); return NULL; } char buf[SIZE]; int nread,nwrite; while(1) { bzero(buf,SIZE); nread=read(fd1,buf,SIZE); if(nread==0) break; cs=cs+nread; write(fd2,buf,nread); } close(fd1); close(fd2); return NULL; }
->递归读取全部目录
int myreaddir(struct copypath *pp,struct threadpool *pool) { DIR *dirp=opendir(pp->oldpath); if(dirp==NULL) { perror("失败:\n"); return -1; } struct dirent *p; while((p=readdir(dirp))!=NULL) { if(p->d_type==DT_REG) //普通文件 { struct copypath *mypath=malloc(sizeof(struct copypath)); sprintf(mypath->oldpath,"%s/%s",pp->oldpath,p->d_name); sprintf(mypath->newpath,"%s/%s",pp->newpath,p->d_name); add_task(myregcp,mypath,pool); //实现 } if(p->d_type==DT_DIR) //文件夹 { if(strcmp(p->d_name,".")!=0 && strcmp(p->d_name,"..")!=0) { struct copypath *mydirpath=malloc(sizeof(struct copypath)); sprintf(mydirpath->oldpath,"%s/%s",pp->oldpath,p->d_name); sprintf(mydirpath->newpath,"%s/%s",pp->newpath,p->d_name); mkdir(mydirpath->newpath,0777); myreaddir(mydirpath,pool); } } } closedir(dirp); return 1; }
这样就大概完成基本功能,用多线程实现大文件夹的拷贝。
扩充功能
->进度条
定义两个全局变量,一个用于计算总字节数,另一个计算每次复制的字节数,再用一个显示函数实现进度条的显示。这里要注意缓冲区的问题,所以我用了"fflush(NULL)"这个函数,让它每打印一个'|'的时候,就刷新一次缓冲区。计算总的字节数直接递归全部目录,用"struct stat info"这个结构体里面的"info.st_size"累加,即可得到总的字节数;拷贝功能函数里面有个"cs"变量,就是存放拷贝字节数。显示进度条就用简单的判断,加相除实现。因为是显示20个|,所以我乘20。
num=(float)cs; //正在复制字节数 k=(num/s)*20;
->耗时
有三种思路,用clock()、time()、sleep(1)等都可以实现计时,直接在拷贝前和拷贝后加赋值,然后相减,即可。起初自己是用clock()这个函数,但是每次都是三秒。。。然后转到sleep(),让它自己while()累加实现。
->代码实现树
还是递归的思想,递归如果是普通文件就打印,是目录文件夹就字符串拼接再递归打印子文件夹下的子文件。
void dirtree(char dirpath[],int level) { int i; char *dirname=NULL; int dirlen; DIR *dp=opendir(dirpath); if(dp==NULL) { perror("失败:\n"); return; } struct dirent *ep; while((ep=readdir(dp))!=NULL) { if(strncmp(ep->d_name, ".", 1) == 0) continue; for(i=0;i) { printf("|"); printf(" "); } printf("|--- "); printf("\033[1;32;40m %s \033[0m\n",ep->d_name); if(ep->d_type==DT_DIR) { //当前目录长度 dirlen=strlen(dirpath)+1; //备份 dirname=(char *)malloc(dirlen); memset(dirname,0,dirlen); memcpy(dirname,dirpath,dirlen); strcat(dirpath,"/"); strcat(dirpath,ep->d_name); dirtree(dirpath,level+1); //递归实现树效果 //恢复之前的目录名 memcpy(dirpath,dirname,dirlen); free(dirname); dirname = NULL; } } closedir(dp); }
->按类型拷贝文件
也比较简单,另建一个递归读目录的函数,在普通文件加个if条件判断就可以。
if(strstr(p->d_name,ftype)!=NULL)
->搜索文件
类似于win的文件检索功能,在输入那个文件下面实现相似文件名的检索,并打印相关路径。也是递归读取目录,核心的改变代码也就一句。用"strstr()"寻找相应的子字符串,打印。
if(strstr(p->d_name,filename)!=NULL)
->删除文件夹
删除稍微要注意一下如果文件夹下面还有其他文件,就不能直接用"rmdir"删除文件夹。先用remove()删除文件,之后再删除子文件夹。也是用递归思想实现的。
实现效果
现在还传不了,后期再传吧。
全部代码
#include "myhead.h" #define SIZE 1024*1024 long int s=0; //总字节数 long int cs=0; //计算每次复制的字节数 int tm=0; //计时--以秒为单位 int flag; //结束标志位 /***************************相关结构体的定义******************************/ struct copypath { char oldpath[256]; char newpath[256]; char target[50]; }; //创建任务链表结构体 struct tasklist { void *(*taskp)(void *); void *taskarg; struct tasklist *next; }; //创建任务链表表头 struct tasklist *myhead; //初始化任务链表 struct tasklist* task_init() { struct tasklist *mytask=malloc(sizeof(struct tasklist)); mytask->taskp=NULL; mytask->taskarg=NULL; mytask->next=NULL; return mytask; } //创建线程池 struct threadpool { int threadnum;//统计当前线程数量 pthread_t *threadid;//存放当前线程的ID号 struct tasklist *taskhead;//保存任务链表的头结点 pthread_mutex_t threadmutex;//互斥锁 int tasknum;//统计任务链表中的数量 pthread_cond_t threadcond;//条件变量 bool threadflag;//用于判断线程池是否开启 }; //线程的多任务函数 void *routine(void *arg) { struct threadpool *pool=(struct threadpool *)arg; //负责从任务链表的头结点的下一个位置取出任务然后处理 struct tasklist *p; while(1) { //上锁 pthread_mutex_lock(&(pool->threadmutex)); //判断数量是否为0 while(pool->threadflag==true && pool->tasknum==0) { //printf("%ld线程阻塞--wait\n",pthread_self()); pthread_cond_wait(&(pool->threadcond),&(pool->threadmutex)); } if(pool->threadflag==false&&pool->tasknum==0) { pthread_mutex_unlock(&(pool->threadmutex)); pthread_exit(NULL); } //取出节点处理 p=pool->taskhead->next; pool->taskhead->next=p->next; p->next=NULL; //更新任务数量 pool->tasknum--; //printf("%ld线程正在执行任务\n",pthread_self()); //解锁 pthread_mutex_unlock(&(pool->threadmutex)); (p->taskp)(p->taskarg); free(p); } } //初始化线程池结构体 struct threadpool *thread_init(int num) { struct threadpool *mythread=malloc(sizeof(struct threadpool));//申请堆空间 mythread->threadnum=num; mythread->threadid=malloc(num*sizeof(pthread_t)); //初始化链表的表头 mythread->taskhead=myhead; //锁初始化 pthread_mutex_init(&(mythread->threadmutex),NULL); //条件变量初始化 pthread_cond_init(&(mythread->threadcond),NULL); mythread->tasknum=0; mythread->threadflag=true; for(int i=0;i) pthread_create(&(mythread->threadid[i]),NULL,routine,mythread); return mythread; } //添加任务函数 int add_task(void *(*p)(void *),void *newarg,struct threadpool *pool) { //找到尾部 struct tasklist *q=pool->taskhead; while(q->next!=NULL) q=q->next; //准备新结点 struct tasklist *newnode=malloc(sizeof(struct tasklist)); newnode->taskp=p; newnode->taskarg=newarg; newnode->next=NULL; //上锁 pthread_mutex_lock(&(pool->threadmutex)); //尾插 q->next=newnode; //更新任务数量 pool->tasknum++; pthread_mutex_unlock(&(pool->threadmutex)); //唤醒条件 pthread_cond_signal(&(pool->threadcond)); return 0; } /***************************************************************************/ // 功能函数 /***************************************************************************/ //主目录名 void *mycopyname(void *myarg) { struct copypath *mypath=(struct copypath *)myarg; char fpath[15]; //求出主目录的名字 char temp[100]; strcpy(temp,mypath->oldpath); char *p=strtok(temp,"/"); //获取文件名 while(p!=NULL) { bzero(fpath,15); strcpy(fpath,p); p=strtok(NULL,"/"); } strcpy(((struct copypath *)myarg)->target,fpath); } //普通文件复制 void *myregcp(void *myarg) { struct copypath *mypath=(struct copypath *)myarg; //系统IO的复制 int fd1,fd2; fd1=open(mypath->oldpath,O_RDONLY); fd2=open(mypath->newpath,O_CREAT|O_TRUNC|O_RDWR,0777); if(fd1==-1) { perror("打开1失败\n"); return NULL; } if(fd2==-1) { perror("打开2失败\n"); return NULL; } char buf[SIZE]; int nread,nwrite; while(1) { bzero(buf,SIZE); nread=read(fd1,buf,SIZE); //cs=cs+nread; if(nread==0) break; cs=cs+nread; write(fd2,buf,nread); } close(fd1); close(fd2); return NULL; } //遍历读目录 int myreaddir(struct copypath *pp,struct threadpool *pool) { DIR *dirp=opendir(pp->oldpath); if(dirp==NULL) { perror("失败:\n"); return -1; } struct dirent *p; while((p=readdir(dirp))!=NULL) { if(p->d_type==DT_REG) //普通文件 { struct copypath *mypath=malloc(sizeof(struct copypath)); sprintf(mypath->oldpath,"%s/%s",pp->oldpath,p->d_name); sprintf(mypath->newpath,"%s/%s",pp->newpath,p->d_name); add_task(myregcp,mypath,pool); //实现 } if(p->d_type==DT_DIR) //文件夹 { if(strcmp(p->d_name,".")!=0 && strcmp(p->d_name,"..")!=0) { struct copypath *mydirpath=malloc(sizeof(struct copypath)); sprintf(mydirpath->oldpath,"%s/%s",pp->oldpath,p->d_name); sprintf(mydirpath->newpath,"%s/%s",pp->newpath,p->d_name); mkdir(mydirpath->newpath,0777); myreaddir(mydirpath,pool); } } } closedir(dirp); return 1; } //递归算字节数 int size_sum(struct copypath *pp) { DIR *dirp=opendir(pp->oldpath); if(dirp==NULL) { perror("失败:\n"); return -1; } struct dirent *p; struct stat info; while((p=readdir(dirp))!=NULL) { if(p->d_type==DT_REG) //普通文件 { struct copypath *mypath=malloc(sizeof(struct copypath)); sprintf(mypath->oldpath,"%s/%s",pp->oldpath,p->d_name); //计算字节数 stat(mypath->oldpath,&info); s=s+info.st_size; } if(p->d_type==DT_DIR) //文件夹 { if(strcmp(p->d_name,".")!=0 && strcmp(p->d_name,"..")!=0) { struct copypath *mydirpath=malloc(sizeof(struct copypath)); sprintf(mydirpath->oldpath,"%s/%s",pp->oldpath,p->d_name); size_sum(mydirpath); } } } closedir(dirp); return 1; } //实现进度条 void *pro_bar(void *myarg) { int i,j=0; float k,num; printf("进度:"); while(1) { j=k; num=(float)cs; //正在复制字节数 k=(num/s)*20; // k=(int)k; if(k-j>=1) { for(i=0;i ) { printf("\033[1;31;42m | \033[0m"); } fflush(NULL); } usleep(10000); if(s==cs) { printf("\033[1;31;42m | \033[0m"); fflush(NULL); usleep(200); break; } } printf("\n任务完成\n"); return NULL; } //lseep计时 void *my_time(void *myarg) { while(1) { sleep(1); tm++; if(s==cs) { break; } } } //显示选择拷贝类型界面 int showdow() { int n; printf("**********************\n"); printf(" 请输入你要拷贝的文件 \n"); printf(" 1.复制全部文件; \n"); printf(" 2.选择类型复制; \n"); printf(" 3.原目录文件树; \n"); printf(" 4.查找某个文件; \n"); printf(" 5.删除某个文件. \n"); printf("**********************\n"); scanf("%d",&n); return n; } //递归读取文件类型的字节数 int ftypesize_sum(struct copypath *pp,char *ftype) { DIR *dirp=opendir(pp->oldpath); if(dirp==NULL) { perror("失败:\n"); return -1; } struct dirent *p; struct stat info; while((p=readdir(dirp))!=NULL) { if(p->d_type==DT_REG) //普通文件 { if(strstr(p->d_name,ftype)!=NULL) //文件类型 { struct copypath *mypath=malloc(sizeof(struct copypath)); sprintf(mypath->oldpath,"%s/%s",pp->oldpath,p->d_name); //计算字节数 stat(mypath->oldpath,&info); s=s+info.st_size; } } if(p->d_type==DT_DIR) //文件夹 { if(strcmp(p->d_name,".")!=0 && strcmp(p->d_name,"..")!=0) { struct copypath *mydirpath=malloc(sizeof(struct copypath)); sprintf(mydirpath->oldpath,"%s/%s",pp->oldpath,p->d_name); ftypesize_sum(mydirpath,ftype); } } } closedir(dirp); return 1; } //递归读取文件类型的目录 int myftyper(struct copypath *pp,struct threadpool *pool,char *ftype) { DIR *dirp=opendir(pp->oldpath); if(dirp==NULL) { perror("失败:\n"); return -1; } struct dirent *p; while((p=readdir(dirp))!=NULL) { if(p->d_type==DT_REG) //普通文件 { if(strstr(p->d_name,ftype)!=NULL) { struct copypath *mypath=malloc(sizeof(struct copypath)); sprintf(mypath->oldpath,"%s/%s",pp->oldpath,p->d_name); sprintf(mypath->newpath,"%s/%s",pp->newpath,p->d_name); add_task(myregcp,mypath,pool); //实现 } } if(p->d_type==DT_DIR) //文件夹 { if(strcmp(p->d_name,".")!=0 && strcmp(p->d_name,"..")!=0) { struct copypath *mydirpath=malloc(sizeof(struct copypath)); sprintf(mydirpath->oldpath,"%s/%s",pp->oldpath,p->d_name); sprintf(mydirpath->newpath,"%s/%s",pp->newpath,p->d_name); mkdir(mydirpath->newpath,0777); myftyper(mydirpath,pool,ftype); } } } closedir(dirp); return 1; } //树实现 void dirtree(char dirpath[],int level) { int i; char *dirname=NULL; int dirlen; DIR *dp=opendir(dirpath); if(dp==NULL) { perror("失败:\n"); return; } struct dirent *ep; while((ep=readdir(dp))!=NULL) { if(strncmp(ep->d_name, ".", 1) == 0) continue; for(i=0;i ) { printf("|"); printf(" "); } printf("|--- "); printf("\033[1;32;40m %s \033[0m\n",ep->d_name); if(ep->d_type==DT_DIR) { //当前目录长度 dirlen=strlen(dirpath)+1; //备份 dirname=(char *)malloc(dirlen); memset(dirname,0,dirlen); memcpy(dirname,dirpath,dirlen); strcat(dirpath,"/"); strcat(dirpath,ep->d_name); dirtree(dirpath,level+1); //递归实现树效果 //恢复之前的目录名 memcpy(dirpath,dirname,dirlen); free(dirname); dirname = NULL; } } closedir(dp); } //递归查找相似文件-查找文件 int sc_file(struct copypath *pp,char *filename) { DIR *dirp=opendir(pp->oldpath); if(dirp==NULL) { perror("失败:\n"); return -1; } struct dirent *p; while((p=readdir(dirp))!=NULL) { if(p->d_type==DT_REG) //普通文件 { if(strstr(p->d_name,filename)!=NULL) //文件类型 { struct copypath *mypath=malloc(sizeof(struct copypath)); sprintf(mypath->oldpath,"%s/%s",pp->oldpath,p->d_name); printf("已找到文件路径:%s\n",mypath->oldpath); free(mypath); } } if(p->d_type==DT_DIR) //文件夹 { if(strcmp(p->d_name,".")!=0 && strcmp(p->d_name,"..")!=0) { if(strstr(p->d_name,filename)!=NULL) //文件类型 { struct copypath *mypath1=malloc(sizeof(struct copypath)); sprintf(mypath1->oldpath,"%s/%s",pp->oldpath,p->d_name); printf("已找到文件路径:%s\n",mypath1->oldpath); free(mypath1); } struct copypath *mydirpath=malloc(sizeof(struct copypath)); sprintf(mydirpath->oldpath,"%s/%s",pp->oldpath,p->d_name); sc_file(mydirpath,filename); } } } closedir(dirp); return 1; } //删除--文件\文件夹 int alldet(struct copypath *pp) { DIR *dirp=opendir(pp->oldpath); struct dirent *p; while((p=readdir(dirp))!=NULL) { if(p->d_type==DT_REG) //普通文件 { struct copypath *mypath=malloc(sizeof(struct copypath)); sprintf(mypath->oldpath,"%s/%s",pp->oldpath,p->d_name); remove(mypath->oldpath); } if(p->d_type==DT_DIR) //文件夹 { if(strcmp(p->d_name,".")!=0 && strcmp(p->d_name,"..")!=0) { struct copypath *mydirpath=malloc(sizeof(struct copypath)); sprintf(mydirpath->oldpath,"%s/%s",pp->oldpath,p->d_name); alldet(mydirpath); rmdir(mydirpath->oldpath); } } } closedir(dirp); rmdir((pp->oldpath)); return 1; } /***************************************************************************/ /***************************************************************************/ // 简化主程序 /***************************************************************************/ int simcopy_all(struct threadpool *mypool) //简化主程序--复制全部 { tm=s=cs=0; struct copypath paths; //新旧路径结构体 printf("请输入源文件\n"); scanf("%s",paths.oldpath); printf("请输入目标文件\n"); scanf("%s",paths.newpath); mycopyname(&paths); sprintf(paths.newpath,"%s/%s",paths.newpath,paths.target); mkdir(paths.newpath,0777); size_sum(&paths); //计算字节数 usleep(20); printf("总字节数:%ld\n",s); add_task(my_time,NULL,mypool); add_task(pro_bar,NULL,mypool); myreaddir(&paths,mypool); } void simcopy(struct threadpool *mypool) //简化主程序--按文件类型复制 { tm=s=cs=0; char ftype[6]; //文件类型变量 struct copypath paths; printf("请输入源文件\n"); scanf("%s",paths.oldpath); printf("请输入目标文件\n"); scanf("%s",paths.newpath); mycopyname(&paths); sprintf(paths.newpath,"%s/%s",paths.newpath,paths.target); mkdir(paths.newpath,0777); printf("请输入要复制的文件类型,如.txt等\n"); scanf("%s",ftype); ftypesize_sum(&paths,ftype); printf("总字节数:%ld\n",s); usleep(20); add_task(my_time,NULL,mypool); add_task(pro_bar,NULL,mypool); myftyper(&paths,mypool,ftype); } void simtree() //简化主程序--文件树 { char direntName[256]; struct copypath paths; printf("请输入源文件\n"); scanf("%s",paths.oldpath); memset(direntName, 0, sizeof(direntName)); strcat(direntName, paths.oldpath); printf("%s\n",paths.oldpath); dirtree(direntName, 0); } void myscfile() //简化主程序--查找 { char fname[256]; struct copypath paths; printf("请输入查找文件夹名\n"); scanf("%s",paths.oldpath); printf("请输入要查找的文件名\n"); scanf("%s",fname); sc_file(&paths,fname); usleep(10); } void mydel() //简化主程序--删除 { tm=s=cs=0; char delfile[100]; struct copypath paths; printf("请输入要删除的文件夹\n"); scanf("%s",paths.oldpath); size_sum(&paths); //计算字节数 printf("size is: %ld\n",s); alldet(&paths); usleep(10); } void endshow() //简化主程序--结束 { printf("请输入 \n"); printf(" 1--继续 \n"); printf(" 2--退出 \n"); scanf("%d",&flag); } /***************************************************************************/ //销毁线程池 int pool_destroy(struct threadpool *pool) { int i; //改变标志位,让线程退出死循环 pool->threadflag=false; //唤醒所有线程 pthread_cond_broadcast(&(pool->threadcond)); //回收所有线程 for(i=0;i threadnum;i++) { pthread_join(pool->threadid[i],NULL); //printf("%ld线程已被回收\n",pool->threadid[i]); } return 0; } int main() { struct copypath paths; while(1) { flag=0; system("clear"); myhead=task_init(); //初始化任务链表的表头 struct threadpool *mypool=thread_init(10); //创建并初始化线程池 int ret=showdow(); switch(ret) { case 1: //拷贝全部 simcopy_all(mypool); pool_destroy(mypool); //线程池的销毁 printf("拷贝花费:%d seconds\n",tm); break; case 2: //拷贝部分 simcopy(mypool); pool_destroy(mypool); printf("拷贝花费:%d seconds\n",tm); break; case 3: //显示文件树 simtree(); break; case 4: //查找某个文件--显示相似的文件 myscfile(); break; case 5: //删除某个文件夹 mydel(); printf("delete done\n"); break; default: break; } endshow(); if(flag==1) continue; else if(flag==2) break; } system("clear"); usleep(20); return 0; }
码源:
http://pan-yz.chaoxing.com/share/info/b54be58ad063e1e9
有什么建议,欢迎联系。邮箱:2460576606@qq.com