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

目录

功能实现

main.c

 copy.c

filetrue.c

isDir.c

isFile.c

isFiletoDir.c

thread_pool.c

my_head.h

thread_pool.h

Makefile 


功能实现

1)实现文件与文件的拷贝                     
2)实现目录与目录的拷贝                      
3)文件与文件的复制 目标文件是否可以绝对/相对路径复制  
4)目录与目录的复制 目标目录是否可以绝对/相对路径复制  
5)能根据实际情况实现特定要求下的拷贝操作  

main.c

#include "my_head.h"
#include "thread_pool.h"

int main(int argc,char *argv[])/* ./copy dir1 dir2  ./copy 1.txt 2.txt*/
{
	//初始化线程池,创建10条线程
	thread_pool *pool = malloc(sizeof(thread_pool));
	init_pool(pool, 10);
	
	if(argc != 3)
	{
		/*输入格式有误*/
		printf("Incorrect input format ,Usage: %s getcwd<src> getcwd<dst>\n",argv[0]);
		return -1;
	}
	
	file_name fileName;
	strcpy(fileName.src,argv[1]);
	strcpy(fileName.dst,argv[2]);
	
	/*判断是否存在这个文件*/
	filetrue((char *)&fileName.src);
	
	/*定义一个stat结构体指针源文件source_file,用来存放指定文件的属性,并且申请空间*/
	struct stat *source_file = malloc(sizeof(struct stat));
	stat(fileName.src,source_file);//获取源目录的数据(类型、权限、大小)
	
	/*定义一个stat结构体指针目标文件target_file,用来存放指定文件的属性,并且申请空间*/
	struct stat *target_file = malloc(sizeof(struct stat));
	stat(fileName.dst,target_file);//获取源目录的数据(类型、权限、大小)
	
	/*判断源文件的是目录还是文件*/
	/*  ./copy 1.txt 2.txt 文件拷贝文件 */
	Isfile(source_file,target_file,pool,(char *)&fileName.src,(char *)&fileName.dst);
	
	/*  ./copy dir1 dir2 目录拷贝目录 */
	Isdir(source_file,target_file,pool,(char *)&fileName.src,(char *)&fileName.dst);
	
	/*  ./copy 1.txt dir1/2.txt  */
	IsfileTodir(source_file,target_file,pool,(char *)&fileName.src,(char *)&fileName.dst);

	//销毁线程池
	destroy_pool(pool);
	
	return 0;
}

 copy.c

#include "my_head.h"

void copyfile(int src,int dst)
{
	char buf[PATHSIZE];	
	int ret;
	
	while(1)
	{
		ret = read(src,buf,PATHSIZE);
		
		if(ret == 0)
		{
			break;
		}
		
		write(dst,buf,ret);
	}
}

void *mytask(void *arg)
{
	//复制文件	
	int *fd = (int *)arg;
	
	copyfile(fd[0],fd[1]);
}

bool copydir(thread_pool *pool,char *src,char *dst,struct stat *info)
{
	/*判断目标目录是否存在,不存在就创建*/
	if(access(dst,F_OK))
	{
		mkdir(dst,info->st_mode);
	}
	
	/*定义一个stat结构体指针源文件fileinfo,用来存放指定文件的属性,并且申请空间*/
	struct stat *fileinfo = malloc(sizeof(struct stat));
	stat(dst,fileinfo);//让目标目录获取源目录的数据(类型、权限、大小)
	
	if(!S_ISDIR(fileinfo->st_mode))
	{
		printf("%s Not a directory!\n",dst);
		return false;
	}
	
	char current_path[PATHSIZE] = {0};//当前路径
	char from_path[PATHSIZE] = {0};//源目录路径
	char dst_path[PATHSIZE] = {0};//目标目录路径
	
	//获取当前的路径
	getcwd(current_path, PATHSIZE);// /mnt/hgfs/Desktop/share/9_阶段2项目/thread_pool/test_demo
	
	//获取源文件的路径存在buf里面	
	chdir(src);           
	getcwd(from_path, PATHSIZE);// /mnt/hgfs/Desktop/share/9_阶段2项目/thread_pool/test_demo
	
	chdir(current_path);        
	chdir(dst);               
	getcwd(dst_path, PATHSIZE);// /mnt/hgfs/Desktop/share/9_阶段2项目/thread_pool/test_demo
	
	//打开目录
	DIR *dp = opendir(from_path);
	
	//切换到目录里面去
	chdir(from_path);
	
	//通过读目录将文件名读出来
	while(1)
	{
		struct dirent *ep = readdir(dp);
		if(ep == NULL)
		{
			printf("readdir end!\n");
			break;
		}
		
		//条件判断(如果读取的文件是'.'不打印)
		if(strcmp(ep->d_name,".") == 0 || strcmp(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);
		
		//1.如果目录里面是文件
		//add_task(bool,mytask,(void *)fd);
		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|O_TRUNC,fileinfo->st_mode);
						
			add_task(pool,mytask,(void *)fd); 	
		}
		
		//2.如果目录里面是目录
		//copydir();
		if(S_ISDIR(fileinfo->st_mode))
		{
			chdir(dst_path);    
			mkdir(ep->d_name,fileinfo->st_mode);
			chdir(ep->d_name);
			
			char new_path[PATHSIZE];
			getcwd(new_path,PATHSIZE);
			
			chdir(from_path);
			copydir(pool,ep->d_name,new_path,fileinfo);
		}
	}	
}

//计算大小
void cal_file_size(char *src_dir)
{
	char srcpath[PATHSIZE] = {0};
	//打开源目录
	DIR *dp = opendir(src_dir);
	if(dp == NULL)
	{
		printf("%s opendir() failed",src_dir);
		return;
	}
	//读取目录项
	while(1)
	{
		struct dirent *ep = readdir(dp);
		if(ep == NULL)    //读完
		{
			break;
		}
		
		//去除'.' '..'
		if(strncmp(ep->d_name,".",1) == 0 || strcmp(ep->d_name,"..")==0)
			continue;
		bzero(srcpath,PATHSIZE);
		sprintf(srcpath,"%s/%s",src_dir,ep->d_name);  
		if(ep->d_type == DT_DIR)    //如果是目录,则递归
		{
			cal_file_size(srcpath);
		}
		else if(ep->d_type == DT_REG)	//如果是文件,则计算文件大小
		{
			struct stat statBuf;
			stat(srcpath,&statBuf);
			total_size += statBuf.st_size;
		}
	}
	printf("FileSize:%ld\n",total_size);
}

filetrue.c

#include "my_head.h"

//判断源文件是否存在
int filetrue(char *src_file)
{
	struct stat st;//获取文件的元数据结构体
	memset(&st,0,sizeof(st));
	if(stat(src_file,&st) == 0)
	{
		printf("文件存在,可以进行拷贝......\n");
		return 0;
	}
	else
	{
		perror("文件不存在,不可以进行拷贝,即将退出程序.....\n");
		exit(0);
		return -1;
	}
}

isDir.c

#include "my_head.h"
#include "thread_pool.h"

void Isdir(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("源文件是目录\t目标文件是目录\n");
		printf("开始拷贝目录:%s ----> %s\n",src_file,dst_file);
		//计算文件的总大小
		cal_file_size(src_file);	
		copydir(pool,src_file,dst_file,source_file);
	}
}

isFile.c

#include "my_head.h"
#include "thread_pool.h"

void Isfile(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("源文件是普通文件\t目标文件不是目录\n");
		printf("开始拷贝文件:%s ----> %s\n",src_file,dst_file);
		/*为了方便把文件描述符传参到任务列表,直接定义两个int*指针*/
		int *fd = calloc(2,sizeof(int));
		
		/*打开源文件*/
		fd[0] = open(src_file,O_RDONLY);
		if(fd[0] == -1)
		{
			perror("open fd[0] fail\n");
		}
		
		/*目标文件权限:只写、没有就创建,存在就清空文件数据*/
		fd[1] = open(dst_file,O_WRONLY|O_CREAT|O_TRUNC,source_file->st_mode);
		if(fd[1] == -1)
		{
			perror("open fd[1] fail\n");
		}
		
		//打印源文件的大小
		printf("FileSIZE:%ld\n",source_file->st_size);
		
		/*添加任务---增加节点*/
		add_task(pool,mytask,(void *)fd);
		
	}
}

isFiletoDir.c

#include "my_head.h"
#include "thread_pool.h"

void IsfileTodir(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("源文件是普通文件\t目标文件是目录\n");
		int *fd = calloc(2, sizeof(int));
		
		fd[0] = open(src_file,O_RDONLY);
		if(fd[0] == -1)
		{
			perror("open fd[0] fail\n");
		}
		
		chdir(dst_file);
		fd[1] = open(dst_file,O_WRONLY|O_CREAT|O_TRUNC,source_file->st_mode);
		if(fd[1] == -1)
		{
			perror("open fd[1] fail\n");
		}
		
		printf("FileSIZE:%ld\n",source_file->st_size);
		
		add_task(pool,mytask,(void *)fd);

	}
}

thread_pool.c

#include "thread_pool.h"

//线程取消例程函数
void handler(void *arg)  //arg = pool->lock
{
	// printf("[%u] is ended.\n",(unsigned)pthread_self());

	//无论线程在什么状态下被取消,一定要先解锁,再响应取消。
	pthread_mutex_unlock((pthread_mutex_t *)arg);
}

//线程的例程函数
void *routine(void *arg)
{
	//1. 先将传递过来的线程池的地址接住。
	thread_pool *pool = (thread_pool *)arg;
	struct task *p;

	while(1)
	{
		//2. 设置线程的取消例程函数。
		//   将来线程收到取消时,先执行handler,再响应取消。
		pthread_cleanup_push(handler, (void *)&pool->lock);
		
		//3. 上锁
		pthread_mutex_lock(&pool->lock);
		
		//4. 当任务队列中没有任务 并且 线程池为开启状态
		while(pool->waiting_tasks == 0 && !pool->shutdown)
		{
			//那么就会进入条件变量中睡眠。
			pthread_cond_wait(&pool->cond, &pool->lock);  //自动解锁
		}
			
		//5. 当执行到这个地方时:
		//要不就是线程池中有任务。
		//要不就是线程池为关闭的状态。
		//或者是两个都不成立。

		//6. 如果当前线程池关闭了,并且没有任务做,那么线程就会解锁退出。
		//   意味着如果线程池关闭了,但是还是任务,线程是不会退出,直到所有任务都做完为止。
		if(pool->waiting_tasks == 0 && pool->shutdown == true)
		{	
			//解锁
			pthread_mutex_unlock(&pool->lock);	
			
			//线程退出
			pthread_exit(NULL); 
		}
		
		//7. 让p指向头节点的下一个节点。
		p = pool->task_list->next;
		
		//8. 让头节点的指针域指向p的下一个节点。
		pool->task_list->next = p->next;

		//第7第8步就是为了将p节点取出来。
		
		//9. 当前等待的任务个数-1
		pool->waiting_tasks--;

		//10. 解锁
		pthread_mutex_unlock(&pool->lock);
		
		//11. 删除线程取消例程函数
		pthread_cleanup_pop(0); //0->不会执行
		
		//12. 设置线程当前为不能响应取消。
		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); 
		
		//13. 执行p指向节点的函数与参数,执行任务的过程中不能响应取消请求。
		(p->do_task)(p->arg);   
		
		//14. 设置线程当前能响应取消请求
		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);

		//15. p指向的任务已经做完了,所以可以释放掉p的空间。
		free(p);
	}
}

//函数功能: 初始化线程池
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. 初始化当前系统为开启状态。
	pool->shutdown = false;
	
	//4. 为任务队列的头节点申请空间。
	pool->task_list = malloc(sizeof(struct task));
	
	//5. 申请线程ID号储存空间
	pool->tids = malloc(sizeof(pthread_t) * MAX_ACTIVE_THREADS);  //20

	//6. 如果以上两个步骤执行失败,那么线程池就会初始化失败。
	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; 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. 如果当前需要处理的任务个数 >= 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. 寻找最后一个节点,从循环中出来时,tmp就是指向最后一个节点。
	struct task *tmp = pool->task_list;
	while(tmp->next != NULL)
		tmp = tmp->next;
	
	//6. 让最后一个节点的指针域指向新节点。
	tmp->next = new_task;
	
	//7. 当前任务个数+1
	pool->waiting_tasks++;

	//8. 解锁
	pthread_mutex_unlock(&pool->lock);

	//9. 唤醒条件变量中一个线程起来做任务。
	pthread_cond_signal(&pool->cond);
	
	//10. 任务添加成功
	return true;
}

//函数功能: 在线程池中添加线程。
int add_thread(thread_pool *pool, unsigned additional_threads)
{
	//1. 如果想添加0条线程,那么请你滚蛋。
	if(additional_threads == 0)
		return 0; 

	//2. 总的线程数 = 当前线程个数 + 新增条数。
	//		35		=	  5        +   30
	unsigned total_threads = pool->active_threads + additional_threads;
						
	int i, actual_increment = 0;
	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++;   //每创建成功一条线程,那么这个actual_increment就会+1
	}

	//3. 最终线程总条数 = 当前线程总条数 + 实际创建成功的线程的个数
	pool->active_threads += actual_increment; 

	//4. 返回实际创建线程的个数
	return actual_increment; 
}

//函数功能: 删除当前线程池中的线程。  
int remove_thread(thread_pool *pool, unsigned int removing_threads)
{
	//1. 如果想删除0条线程,那么就返回当前线程池中真实的条数。
	if(removing_threads == 0)
		return pool->active_threads; 

	//2.  线程池线程剩余条数 = 当前线程池中线程的个数 - 想删除的线程的个数
	//            7                   10              -     3
	//            0                   10              -     10
	//            -5                  10              -     15
	int remaining_threads = pool->active_threads - removing_threads;
	
	//3. 判断计算的结果,线程池中至少要存活一条线程。
	remaining_threads = remaining_threads > 0 ? remaining_threads : 1;

	//4. 发送取消请求给线程。
	int i;  
	for(i=pool->active_threads-1; i>remaining_threads-1; i--)
	{	
		errno = pthread_cancel(pool->tids[i]); 
		if(errno != 0)
			break;
	}

	//5. 如果一个都取消不了
	if(i == pool->active_threads-1) 
		//就会返回-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; 
	
	//2. 唤醒所有在条件变量中等待的线程
	pthread_cond_broadcast(&pool->cond);  
	
	//3. 接合所有的线程
	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));
		}
	
		// else
			// printf("[%u] is joined\n", (unsigned)pool->tids[i]);
		
	}

	//4. 释放所有申请过的空间。
	free(pool->task_list);
	free(pool->tids);
	free(pool);

	return true;
}

my_head.h

#ifndef _MY_HEAD_H_
#define _MY_HEAD_H_

#define PATHSIZE	1024

#include "thread_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>

typedef struct file_name
{
	char src[PATHSIZE];
	char dst[PATHSIZE];
}file_name;

unsigned long total_size;	//总文件大小

bool copydir(thread_pool *pool,char *src,char *dst,struct stat *info);//拷贝目录
void copyfile(int src, int dst);//拷贝文件
void *mytask(void *arg);//任务列表
void cal_file_size(char *srcDir);//计算目录大小
int filetrue(char *src_file);//判断文件是否存在
void Isfile(struct stat *source_file,struct stat *target_file,thread_pool *pool,char *src_file,char *dst_file);//文件拷贝
void Isdir(struct stat *source_file,struct stat *target_file,thread_pool *pool,char *src_file,char *dst_file);//目录拷贝
void IsfileTodir(struct stat *source_file,struct stat *target_file,thread_pool *pool,char *src_file,char *dst_file);//文件到指定目录

/*
struct stat 
{
	dev_t     st_dev;         // ID of device containing file 
	ino_t     st_ino;         // inode number
	mode_t    st_mode;        // protection     文件类型、文件权限
	nlink_t   st_nlink;       // number of hard links 
	uid_t     st_uid;         // user ID of owner 
	gid_t     st_gid;         // group ID of owner 
	dev_t     st_rdev;        // device ID (if special file) 
	off_t     st_size;        // total size, in bytes 文件大小
	blksize_t st_blksize;     // blocksize for filesystem I/O 
	blkcnt_t  st_blocks;      // number of 512B blocks allocated 
};

struct dirent{
               ino_t          d_ino;       //索引号
               off_t          d_off;       //偏移量
               unsigned short d_reclen;    //记录文件名长度
               unsigned char  d_type;      //文件类型
               char           d_name[256]; //文件名(重点掌握) 文件名最多可以设置256个字符。
};

d_type:
	DT_BLK      This is a block device.      ---> 块设备文件     //6
	DT_CHR      This is a character device.  ---> 字符设备文件   //2
	DT_DIR      This is a directory.         ---> 目录文件       //4
	DT_FIFO     This is a named pipe (FIFO). ---> 管道文件       //1
	DT_LNK      This is a symbolic link.     ---> 链接文件       //10
	DT_REG      This is a regular file.      ---> 普通文件       //8
	DT_SOCK     This is a UNIX domain socket.---> 套接字文件     //12
*/
#endif

thread_pool.h

#ifndef _THREAD_POOL_H_
#define _THREAD_POOL_H_

#define MAX_WAITING_TASKS	1000
#define MAX_ACTIVE_THREADS	20

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>


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

Makefile 

	
CC=gcc
TARGET = ./bin/copy
C_MAIN=$(wildcard ./main/*.c)
C_SOURCE=$(wildcard ./src/*.c)
INCLUDE_PATH = -I ./include
LIB_PATH = -lpthread

$(TARGET):$(C_SOURCE) $(C_MAIN)
	$(CC) $^ -o $@ $(INCLUDE_PATH) $(LIB_PATH)

.PHONY:clean

clean:
	$(RM) $(TARGET)
	$(RM) $(LIB_NAME)

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值