基于线程池的目录拷贝项目

目录

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.txt
	
	2、目录拷贝目录
	绝对路径:./project /home/gec/test /hoem/gec/copy_abs_dir
	相对路径:./project ./test ./copy_rel_dir
	
	
	3、文件到目录   
	绝对路径 /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
#include
#include
#include
#include
#include
#include
 
#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
#include
#include
#include
#include
#include
 
#define MAX_WAITING_TASKS	100000
#define MAX_ACTIVE_THREADS	2000
 
struct 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;
		
		//当前任务个数-1
		pool->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. 为线程池任务队列的头节点的指针域赋值NULL
	pool->task_list->next = NULL;
	
	//7. 设置线程池最大任务个数为1000
	pool->max_waiting_tasks = MAX_WAITING_TASKS;
	
	//8. 当前需要处理的任务为0
	pool->waiting_tasks = 0;
	
	//9. 初始化线程池中线程的个数
	pool->active_threads = threads_number;

	//10. 创建线程
	int i;
	for(i=0; iactive_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. 如果当前需要处理的任务个数>=1000
	if(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. 当前需要处理的任务+1
	pool->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         -  8

	remaining_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; iactive_threads; i++)
	{
		errno = pthread_join(pool->tids[i], NULL);

		if(errno != 0)
		{
			printf("join tids[%d] error: %s\n",
					i, strerror(errno));
		}
	
		else
			printf("[%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 = -lpthread

SRC = $(wildcard src/*.c)
MAIN  = main/main.c
RM = rm

CC = gcc

$(TAR):$(MAIN) $(SRC)
	$(CC) $^ -o $@ $(INC) $(LIB)

run:
	./$(TAR)

.PHONY:clean

clean:
	$(RM) $(TAR)

你可能感兴趣的:(系统编程,c语言,linux)