基于线程池的目录拷贝项目
1、实现的功能有:
2、main.c
3、project.h
4、pthread_pool.h
5、file_file.c
6、dir_dir.c
7、file_to_dir.c
8、judgement.c
9、copy.c
10、pthread_pool.c
11、makefile
1、实现的功能有:
1)实现文件与文件的拷贝
2)实现目录与目录的拷贝
3)实现文件与目录的拷贝
3)文件与文件的复制 目标文件是否可以绝对/相对路径复制
4)目录与目录的复制 目标目录是否可以绝对/相对路径复制
5)文件与目录的复制 目标文件是否可以绝对/相对路径复制
2、main.c
#include"project.h"
#include"pthread_pool.h"/*执行文件复制、路径切换可执行文件复制到目录 cp ./project /home/gec/切换家路径 cd /home/gec/切换测试文件路径切换进执行文件的目录收
*//* 测试 1、输入错误格式,并看运行后的结果2、输入的源文件不存在,并看运行后的结果
*//* 运行 1、文件拷贝文件绝对路径:./project /home/gec/test.txt /home/gec/copy_abs_file.txt相对路径:./project ./test.txt ./copy_rel_file.txt2、目录拷贝目录绝对路径:./project /home/gec/test /hoem/gec/copy_abs_dir相对路径:./project ./test ./copy_rel_dir3、文件到目录 绝对路径 /project /home/gec/test.txt /home/gce/test/copy_abs_filetodir.txt 相对路径 ./project ./test.rar ./test/copy_rel_filetodir.rar
*//* 成功运行之后使用diff指令进行文件分析,不报错则证明拷贝成功
*/ int main(int argc,char *argv[])
{//初始化线程池,创建3条线程thread_pool* pool = malloc(sizeof(thread_pool));init_pool(pool, 3);if(argc != 3){//输入格式错误会导致运行失败,提示用户重新输入printf("输入参数数目错误,请重新输入...\\n");return -1;}//创建结构体,获取源文件和目录文件file_name fileName;strcpy(fileName.src,argv[1]);strcpy(fileName.dst,argv[2]);//判断是否存在这个源文件judgement((char*)&fileName.src);//定义源文件的文件结构体指针指向源文件,并开辟堆区空间,用于存放内容struct stat* source_file = malloc(sizeof(struct stat));stat(fileName.src,source_file);//定义目的文件的文件结构体指针指向目的文件,并开辟堆区空间,用于存放内容struct stat* target_file = malloc(sizeof(struct stat));stat(fileName.dst,target_file);//对拷贝类型进行判断,从而使用不同的拷贝函数(文件到文件,目录到目录,文件到目录)file_file(source_file,target_file,pool,(char *)&fileName.src,(char *)&fileName.dst);dir_dir(source_file,target_file,pool,(char *)&fileName.src,(char *)&fileName.dst);file_to_dir(source_file,target_file,pool,(char *)&fileName.src,(char *)&fileName.dst);//销毁线程池destroy_pool(pool);return 0;
}
3、project.h
#include"pthread_pool.h"#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<sys/mman.h>
#include<dirent.h>
#include<unistd.h>
#include<string.h>#define SIZE 1024//目录的所有文件总大小
unsigned long total_size;typedef struct file_name
{char src[SIZE];char dst[SIZE];
}file_name;//判断源文件是否存在
int judgement(char* src_file);//判断输入参数所属的文件类型,进行相对应的拷贝
//判断文件
void file_file(struct stat* source_file,struct stat* target_file,thread_pool* pool,char* src_file,char* dst_file);//拷贝文件
void copyfile(int src, int dst);//任务列表
void* task(void* arg);//判断目录
void dir_dir(struct stat* source_file,struct stat* target_file,thread_pool* pool,char* src_file,char* dst_file);//拷贝目录
bool copydir(thread_pool* pool,char* src,char* dst,struct stat* info);//计算目录大小
void compute(char* srcDir);//判断文件到目录
void file_to_dir(struct stat* source_file,struct stat* target_file,thread_pool* pool,char* src_file,char* dst_file);//文件到指定目录
4、pthread_pool.h
#ifndef PTHREAD_POOL
#define PTHREAD_POOL#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<pthread.h>#define MAX_WAITING_TASKS 100000
#define MAX_ACTIVE_THREADS 2000struct task
{void *(*do_task)(void *arg);void *arg;struct task *next;
};typedef struct thread_pool
{pthread_mutex_t lock; //互斥锁,保护任务队列pthread_cond_t cond; //条件变量,同步所有线程bool shutdown; //线程池销毁标记struct task *task_list; //任务队列指针pthread_t *tids; //线程ID号存放位置unsigned max_waiting_tasks; //当前最大的等待任务个数unsigned waiting_tasks; //任务链队列中等待任务个数unsigned active_threads; //当前活跃的线程个数
}thread_pool;/*函数功能: 初始化线程池*/
bool init_pool(thread_pool *pool, unsigned int threads_number);/*投放任务到任务队列中*/
bool add_task(thread_pool *pool, void *(*do_task)(void *arg), void *task);/*在线程池中添加线程*/
int add_thread(thread_pool *pool, unsigned int additional_threads_number);/*删除当前线程池中的线程*/
int remove_thread(thread_pool *pool, unsigned int removing_threads_number);/*销毁线程池*/
bool destroy_pool(thread_pool *pool);/*线程的例程函数*/
void *routine(void *arg);#endif
5、file_file.c
#include"project.h"
#include"pthread_pool.h"void file_file(struct stat* source_file,struct stat* target_file,thread_pool* pool,char* src_file,char *dst_file)
{//源文件是文件,目标文件不是目录if(S_ISREG(source_file->st_mode) && !S_ISDIR(target_file->st_mode)){printf("源文件是普通文件,目标文件不是目录\\n");printf("开始拷贝文件:%s ----> %s\\n",src_file,dst_file);int* fd = calloc(2,sizeof(int));//打开源文件fd[0] = open(src_file,O_RDONLY);if(fd[0] == -1){printf("open fd[0] fail\\n");}//目标文件权限:只写、没有就创建fd[1] = open(dst_file,O_WRONLY|O_CREAT,source_file->st_mode);if(fd[1] == -1){printf("open fd[1] fail\\n");}//打印源文件的文件大小printf("Filesize: %ld 字节\\n",source_file->st_size);//打印源文件的文件类型if(S_ISREG(source_file->st_mode))printf("Filetype: - 文件\\n"); //普通文件else if(S_ISREG(source_file->st_mode))printf("Filetype: d 文件\\n"); //目录文件else if(S_ISCHR(source_file->st_mode))printf("Filetype: c 文件\\n"); //字符设备文件else if(S_ISBLK(source_file->st_mode))printf("Filetype: b 文件\\n"); //块设备文件else if(S_ISFIFO(source_file->st_mode))printf("Filetype: p 文件\\n"); //管道文件else if(S_ISLNK(source_file->st_mode))printf("Filetype: l 文件\\n"); //链接文件else printf("Filetype: s 文件\\n"); //套接字文件sleep(1);//添加任务add_task(pool,task,(void *)fd); }
}
6、dir_dir.c
#include"project.h"
#include"pthread_pool.h"void dir_dir(struct stat* source_file,struct stat* target_file,thread_pool* pool,char* src_file,char *dst_file)
{//判断是否是目录文件if(S_ISDIR(source_file->st_mode)){printf("源文件是目录,目标文件是目录\\n");printf("开始拷贝目录:%s ----> %s\\n",src_file,dst_file);//计算目录文件大小compute(src_file); //拷贝目录文件copydir(pool,src_file,dst_file,source_file);}
}
7、file_to_dir.c
#include"project.h"
#include"pthread_pool.h"void file_to_dir(struct stat* source_file,struct stat* target_file,thread_pool* pool,char* src_file,char* dst_file)
{//源文件是文件,目标文件是目录if(S_ISREG(source_file->st_mode) && S_ISDIR(target_file->st_mode)){printf("源文件是普通文件,目标文件是目录\\n");int* fd = calloc(2, sizeof(int));fd[0] = open(src_file,O_RDONLY);if(fd[0] == -1){printf("open fail\\n");}chdir(dst_file);fd[1] = open(dst_file,O_WRONLY|O_CREAT,source_file->st_mode);if(fd[1] == -1){printf("open fail\\n");}//打印源文件的文件大小printf("Filesize: %ld 字节\\n",source_file->st_size);//打印源文件的文件类型if(S_ISREG(source_file->st_mode))printf("Filetype: - 文件\\n"); //普通文件else if(S_ISREG(source_file->st_mode))printf("Filetype: d 文件\\n"); //目录文件else if(S_ISCHR(source_file->st_mode))printf("Filetype: c 文件\\n"); //字符设备文件else if(S_ISBLK(source_file->st_mode))printf("Filetype: b 文件\\n"); //块设备文件else if(S_ISFIFO(source_file->st_mode))printf("Filetype: p 文件\\n"); //管道文件else if(S_ISLNK(source_file->st_mode))printf("Filetype: l 文件\\n"); //链接文件else printf("Filetype: s 文件\\n"); //套接字文件//添加任务add_task(pool,task,(void *)fd);}
}
8、judgement.c
#include"project.h"//判断源文件是否存在
int judgement(char* src_file)
{struct stat st;memset(&st,0,sizeof(st));if(stat(src_file,&st) == 0){printf("源文件存在,可以拷贝...\\n");return 0;}else{printf("源文件不存在,不可以拷贝,即将退出程序...\\n");exit(0);return -1;}
}
9、copy.c
#include"project.h"
#include"pthread_pool.h" void copyfile(int src,int dst)
{char buf[SIZE] = { 0 }; int ret = 0;while(1){ret = read(src,buf,SIZE);if(ret == 0){break;}write(dst,buf,ret);}close(src);close(dst);
}void* task(void* arg)
{//调用复制文件的函数int* fd = (int *)arg;copyfile(fd[0],fd[1]);usleep(100);
}bool copydir(thread_pool* pool,char* src,char* dst,struct stat* info)
{//判断目标目录是否存在,不存在就创建if(access(dst,F_OK)){mkdir(dst,info->st_mode);}//定义一个stat结构体指针,开辟堆区空间struct stat* fileinfo = malloc(sizeof(struct stat));stat(dst,fileinfo);if(!S_ISDIR(fileinfo->st_mode)){printf("%s不是目录文件!\\n",dst);return false;}char current_path[SIZE] = {0};//当前路径char from_path[SIZE] = {0};//源目录路径char dst_path[SIZE] = {0};//目标目录路径//获取当前的路径getcwd(current_path,SIZE);//获取源文件的路径存在buf里面 chdir(src); getcwd(from_path,SIZE);chdir(current_path); chdir(dst); getcwd(dst_path,SIZE);//打开目录DIR* dp = opendir(from_path);//切换到目录里面去chdir(from_path);//通过读目录将文件名读出来while(1){struct dirent* ep = readdir(dp);if(ep == NULL){break;}//条件判断(如果读取的文件是'.'和'..'不打印)if(ep->d_name[0] == '.')continue;printf("ep->d_name(文件名字):%s\\n",ep->d_name);bzero(fileinfo,sizeof(struct stat));chdir(from_path);stat(ep->d_name,fileinfo);//目录里面是文件//使用文件拷贝方法if(S_ISREG(fileinfo->st_mode)){int* fd = calloc(2, sizeof(int));fd[0] = open(ep->d_name, O_RDONLY);chdir(dst_path); fd[1] = open(ep->d_name, O_WRONLY|O_CREAT,fileinfo->st_mode);usleep(100);add_task(pool,task,(void*)fd); }//目录里面是目录//使用目录拷贝方法if(S_ISDIR(fileinfo->st_mode)){chdir(dst_path); mkdir(ep->d_name,fileinfo->st_mode);chdir(ep->d_name);char new_path[SIZE];getcwd(new_path,SIZE);chdir(from_path);copydir(pool,ep->d_name,new_path,fileinfo);}}
}//计算大小
void compute(char* src_dir)
{char srcpath[SIZE] = {0};//打开源目录DIR *dp = opendir(src_dir);if(dp == NULL){printf("opendir failed\\n");return;}while(1){struct dirent *ep = readdir(dp);if(ep == NULL) //读完{break;}//跳过'.'和'..'if(ep->d_name[0] == '.')continue;bzero(srcpath,SIZE);sprintf(srcpath,"%s/%s",src_dir,ep->d_name); if(ep->d_type == DT_DIR) //如果是目录,则递归{compute(srcpath);}else if(ep->d_type == DT_REG) //如果是文件,则计算文件大小{struct stat statBuf;stat(srcpath,&statBuf);total_size += statBuf.st_size;}}printf("目录总大小: %ld 字节\\n",total_size);}
10、pthread_pool.c
#include"pthread_pool.h"//线程取消例程函数
void handler(void *arg) //arg = pool->lock
{thread_pool pool = *((thread_pool *)arg);//无论线程在什么状态下被取消,一定要先解锁,再响应取消。pool.active_threads--;pthread_mutex_unlock(&pool.lock);
}//线程例程函数
void *routine(void *arg)
{//1. 先接住线程池的地址thread_pool *pool = (thread_pool *)arg;struct task *p;while(1){//2. 线程取消例程函数//将来要是有人要取消我,请先把锁解开,然后再响应取消。pthread_cleanup_push(handler, (void *)&pool);//3. 上锁pthread_mutex_lock(&pool->lock);//如果当前线程池没有关闭,并且当前线程池没有任务做while(pool->waiting_tasks == 0 && !pool->shutdown){//那么就进去条件变量中睡觉。pthread_cond_wait(&pool->cond, &pool->lock); //自动解锁}//要是线程池关闭了,或者有任务做,这些线程就会运行到这行代码//判断当前线程池是不是关闭了,并且没有等待的任务if(pool->waiting_tasks == 0 && pool->shutdown == true){ //如果线程池关闭,又没有任务做//线程那么就会解锁pthread_mutex_unlock(&pool->lock); //线程走人pthread_exit(NULL); }//能运行到这里,说明有任务做//p指向头节点的下一个p = pool->task_list->next;//让头节点的指针域指向p的下一个节点pool->task_list->next = p->next;//当前任务个数-1pool->waiting_tasks--;//解锁pthread_mutex_unlock(&pool->lock);//删除线程取消例程函数pthread_cleanup_pop(0);//设置线程不能响应取消pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); //执行这个p节点指向的节点的函数(p->do_task)(p->arg);//设置线程能响应取消pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);//释放p对应的空间free(p);}pthread_exit(NULL);
}//初始化线程池
bool init_pool(thread_pool *pool, unsigned int threads_number)
{//1. 初始化线程池互斥锁pthread_mutex_init(&pool->lock, NULL);//2. 初始化条件变量pthread_cond_init(&pool->cond, NULL);//3. 初始化标志位为false,代表当前线程池正在运行。pool->shutdown = false;//4. 初始化任务队列的头节点pool->task_list = malloc(sizeof(struct task));//5. 为储存线程ID号申请空间。pool->tids = malloc(sizeof(pthread_t) * MAX_ACTIVE_THREADS);//第4步与第5步错误判断if(pool->task_list == NULL || pool->tids == NULL){perror("allocate memory error");return false; //初始化线程池失败}//6. 为线程池任务队列的头节点的指针域赋值NULLpool->task_list->next = NULL;//7. 设置线程池最大任务个数为1000pool->max_waiting_tasks = MAX_WAITING_TASKS;//8. 当前需要处理的任务为0pool->waiting_tasks = 0;//9. 初始化线程池中线程的个数pool->active_threads = threads_number;//10. 创建线程int i;for(i=0; i<pool->active_threads; i++){if(pthread_create(&((pool->tids)[i]), NULL,routine, (void *)pool) != 0){perror("create threads error");return false;}}//11. 线程池初始化成功return true;
}//添加任务
bool add_task(thread_pool *pool,void *(*do_task)(void *arg), void *arg)
{//1. 为新任务的节点申请空间struct task *new_task = malloc(sizeof(struct task));if(new_task == NULL){perror("allocate memory error");return false;}//2. 为新节点的数据域与指针域赋值new_task->do_task = do_task; new_task->arg = arg; new_task->next = NULL; //3. 在添加任务之前,必须先上锁,因为添加任务属于访问临界资源 -> 任务队列pthread_mutex_lock(&pool->lock);//4. 如果当前需要处理的任务个数>=1000if(pool->waiting_tasks >= MAX_WAITING_TASKS){//解锁pthread_mutex_unlock(&pool->lock);//打印一句话提示一下,太多任务了fprintf(stderr, "too many tasks.\\n");//释放掉刚刚准备好的新节点free(new_task);return false;}//5. 寻找任务队列中的最后一个节点(尾插)struct task *tmp = pool->task_list;while(tmp->next != NULL)tmp = tmp->next;//从循环中出来时,tmp肯定是指向最后一个节点//6. 让最后一个节点的指针域指向新节点tmp->next = new_task;//7. 当前需要处理的任务+1pool->waiting_tasks++;//8. 添加完毕,就解锁。pthread_mutex_unlock(&pool->lock);//9. 唤醒条件变量中的一个线程起来做任务(单播)pthread_cond_signal(&pool->cond);return true;
}//添加线程
int add_thread(thread_pool *pool, unsigned additional_threads)
{//如果说你想添加0条,则直接返回0。if(additional_threads == 0)return 0; unsigned total_threads = pool->active_threads + additional_threads;//total_threads = 原本 2条 + 现在再加2条int i, actual_increment = 0;// i = 2 ; i<4 && i<20; i++for(i = pool->active_threads; i < total_threads && i < MAX_ACTIVE_THREADS; i++) {if(pthread_create(&((pool->tids)[i]),NULL, routine, (void *)pool) != 0){perror("add threads error");if(actual_increment == 0) return -1;break;}actual_increment++; //真正创建线程的条数}pool->active_threads += actual_increment; //当前线程池线程个数 = 原来的个数 + 实际创建的个数return actual_increment; //返回真正创建的个数
}//删除线程。
int remove_thread(thread_pool *pool, unsigned int removing_threads)
{//1. 如果你想删0条,直接返回。if(removing_threads == 0)return pool->active_threads; //返回当前剩余的线程数int remaining_threads = pool->active_threads - removing_threads;// 3 = 5 - 2// 0 = 5 - 5// -3 = 5 - 8remaining_threads = remaining_threads > 0 ? remaining_threads : 1;int i; for(i=pool->active_threads-1; i>remaining_threads-1; i--){ errno = pthread_cancel(pool->tids[i]); //取消这些线程if(errno != 0)break;}if(i == pool->active_threads-1) //删除失败return -1;else{pool->active_threads = i+1; //当前实际线程个数return i+1; }
}//销毁线程池
bool destroy_pool(thread_pool *pool)
{printf("拷贝完成...\\n");printf("开始销毁线程池...\\n");//1. 设置线程池关闭标志为真pool->shutdown = true; pthread_cond_broadcast(&pool->cond); //目的: 就是让线程退出!//2. 接合所有的线程int i;for(i=0; i<pool->active_threads; i++){errno = pthread_join(pool->tids[i], NULL);if(errno != 0){printf("join tids[%d] error: %s\\n",i, strerror(errno));}elseprintf("[%u] is joined\\n", (unsigned)pool->tids[i]);}//3. 释放一些空间free(pool->task_list);free(pool->tids);free(pool);printf("销毁线程池成功...\\n");printf("退出程序...\\n");return true;
}
11、makefile
TAR = bin/project
INC = -I ./include
LIB = -lpthreadSRC = $(wildcard src/*.c)
MAIN = main/main.c
RM = rmCC = gcc$(TAR):$(MAIN) $(SRC)$(CC) $^ -o $@ $(INC) $(LIB)run:./$(TAR).PHONY:cleanclean:$(RM) $(TAR)