您的位置:首页 > 博客中心 > 网络系统 >

嵌入式Linux cp命令实现

时间:2022-04-03 09:21

   最近项目在数据导出的时候出现问题,基本思路是:

           首先vfork创建一个进程,子进程中创建2个线程,线程1负责界面显示,线程2负责数据拷贝;父进程保存配置文件并退出应用;

           其中,线程2中又创建一个进程用来调用系统cp命令,在线程1中又vfork使用类system命令函数发送kill命令杀死cp拷贝(kill -9 $(pgrep cp));

通过以上思路,可以知道,设计混乱,线程中不断创建进程,进程又线程的,好歹哥也编程几年了,怎么能容忍这么垃圾的代码存在,闲暇之时,改之;


新的思路:

自己实现cp命令,显示界面交给定时器处理;

       但是还有个问题就是我拷贝数据时怎么取消,如果一个文件过大怎么办,解决办法为:定义一个回调函数,在循环的条件中也使用该函数的返回值,该回调函数返回值为一个全局变量,即可解决。


以下是demo程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/time.h>
#include <errno.h>
#include <dirent.h>


#define MAX_PATH_LEN (256)


typedef enum{

	FALSE,
	TRUE
	
}BOOL;


typedef enum{

	CP_SKIP, 		
	CP_CONTINUE,	
	CP_CANCEL ,
	CP_ERROR,	
	
}STATUS_CP;

typedef signed long long   INT64S;
typedef unsigned long long INT64U;
typedef signed int         INT32S;
typedef unsigned int       INT32U;
typedef signed short       INT16S;
typedef unsigned short     INT16U;
typedef signed char        INT8S;
typedef unsigned char      INT8U;


typedef BOOL (*oops_func)(void);
typedef void (*sig_handler_t)(int);


char note_text[300];
char animate[4] = {'-', '/', '|', '\\'}; 
unsigned int animate_pos = 0;

signed long long total_size  = 0;
signed long long copied_size = 0;
signed long long percent 	 = 0;

void timer_handler(int signum)
{
	printf("\r %s  %c", note_text, animate[(animate_pos++)%4]);
	fflush(stdout);
}


/* 创建/销毁定时器 */
void install_timer(size_t sec, sig_handler_t  handler_func)
{
	struct sigaction act;
	struct itimerval tick;

	if(sec > 0)
	{
		act.sa_handler = handler_func;
	}
	else
	{
		act.sa_handler = SIG_DFL;
	}
	
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	sigaction(SIGALRM, &act, 0);
	
	memset(&tick, 0, sizeof(tick));
	tick.it_value.tv_sec 	 = sec;
	tick.it_value.tv_usec 	 = 0;
	tick.it_interval.tv_sec  = sec;
	tick.it_interval.tv_usec = 0;

	setitimer(ITIMER_REAL, &tick, 0);
}

int open_file(const char *_filename, int _flag , mode_t _mode)
{
    int fd = 0;
    fd = open(_filename, _flag, _mode);
    if(fd < 0)
    {
        perror("open:");
    } 
	
    return fd;
}

int copy_file(const char *src_file, const char *des_file, oops_func cancel_cp)
{
#define pre_FRAMESIZE_CP 1024

    char buffer[pre_FRAMESIZE_CP];
	char *ptr 		= NULL;
    int nbytes_rd  	= 0;//每次读取的字节个数
    int nbytes_wr 	= 0;//每次写入的字节个数
	int loop_times 	= 0;
	int src_fd 		= -1;
    int des_fd 		= -1;
	
	if(src_file == NULL || des_file == NULL)
	{
		return -1;
	}
	
    src_fd = open_file(src_file, O_RDONLY, 0666);
    des_fd = open_file(des_file, O_CREAT | O_WRONLY | O_TRUNC , 0666);
	if(src_fd < 0 || des_fd < 0)
	{
		return -2;
	}
	
    /* 以下代码是一个经典的拷贝文件的代码 */
    while( !cancel_cp() && (nbytes_rd = read(src_fd, buffer, pre_FRAMESIZE_CP)) ) /*每次从源文件读取1KB数据到buf*/
    {
   		if(loop_times%10 == 0) //每读取1MB,暂停拷贝避免影响其他进程或线程显示等操作
   		{
			//usleep(1); 
			loop_times = 0;
   		}
		
		if((nbytes_rd == -1) && (errno != EINTR))
		{
			printf("(BytesRead==-1)&&(errno!=EINTR)\n");
		 	goto COPY_ERR;
		}
	 	else if(nbytes_rd > 0)
		{
			ptr = buffer;
			while( !cancel_cp() && (nbytes_wr=write(des_fd, ptr, nbytes_rd)))
	        {
				//printu("nbytes_wr = %d\n", nbytes_wr);
	         	 
		         if((nbytes_wr == -1) && (errno != EINTR)) /* 一个致命错误发生了 */
		         {
		         	goto COPY_ERR;
		         }
		         else if(nbytes_wr == nbytes_rd)  /* 写完了所有读的字节 */
		         {
					break;
				 }
		         else if(nbytes_wr > 0) /* 只写了一部分,继续写 */
		         {
		         	ptr 	  += nbytes_wr;
		           	nbytes_rd -= nbytes_wr;
		         }
	        }
		   	
			if(nbytes_wr == -1) /* 写的时候发生的致命错误 */
		   	{
		       goto COPY_ERR;
		   	}
		}
		
		loop_times++;//循环执行次数统计,到一定次数执行某操作
    }
	
    close(src_fd);
    close(des_fd);
    //sync(); 数据拷贝完成时再延时 - 防止延时
    return 0;
    
COPY_ERR:
	src_fd > 0 ? close(src_fd):(src_fd = -1);
	des_fd > 0 ? close(des_fd):(des_fd = -1);
   // sync();
	
    return -3;
}

/*连接目录字符串,主要处理末尾/的问题,frt snd 两个参数不能同时为空那样没有意义*/
char* make_path(char *dest, const char *frt, const char *snd)
{
	if(NULL == frt || strlen(frt) == 0)
	{
		sprintf(dest, "%s", snd);
	}
	else if(NULL == snd || strlen(snd) == 0)
	{
		sprintf(dest, "%s", frt);
	}
	else
	{
		if(frt[strlen(frt) - 1] == '/')
		{
			sprintf(dest, "%s%s", frt, snd);
		}
		else
		{
			sprintf(dest, "%s/%s", frt, snd);
		}

	}
	
	return dest;
}


int cp_cmd(const char* path_from, const char* path_to, const char* path_tree, oops_func cancel_cp)
{	
	char path_tree_new[MAX_PATH_LEN];
	char path_from_full[MAX_PATH_LEN];
	char path_to_full[MAX_PATH_LEN];
	int  ret_val = CP_CONTINUE;
	struct stat st;
	struct dirent *entry = NULL;
	DIR *dir = NULL;

	if(errno == EROFS) // 只读文件系统,停止拷贝
	{
		//perror("");
		return CP_ERROR;
	}

	if(cancel_cp() == TRUE)
	{
		return CP_CANCEL;
	}
	
	/*  参数合法性检测 */
	if(path_from == NULL || path_to == NULL)
	{
		return CP_SKIP;
	}
	
	/* 获得拷贝源的属性*/
	make_path(path_from_full, path_from, path_tree);
	if(-1 == stat(path_from_full, &st))
	{
		fprintf(stderr, "can't access \"%s\".\n", path_from_full);
		return CP_SKIP;
	}
		
	/*  如果是目录则浏览,否则结束 */
	if(!S_ISDIR(st.st_mode))
	{
		return CP_CONTINUE;
	}

	/* 打开目录 */
	if(!(dir = opendir(path_from_full)))
	{
		fprintf(stderr, "can't open directory \"%s\".\n", path_from_full);
		return CP_SKIP;
	}
	
	/*  遍历目录 */
	while( !cancel_cp() && (entry = readdir(dir)) != NULL )
	{
		/* 构建目录path_tree_new */
		make_path(path_tree_new, path_tree, entry->d_name);
		make_path(path_from_full, path_from, path_tree_new);
	
		/* 无法访问则skip */
		if(-1 == stat(path_from_full, &st))
		{
			fprintf(stderr, "skip, can't access %s.\n", path_from_full);
			continue;
		}

		/*  忽略 . 和 .. */
		if(S_ISDIR(st.st_mode) && (strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0))
		{
			continue;
		}
		

		if(S_ISDIR(st.st_mode))
		{
			make_path(path_to_full, path_to, path_tree_new);
			
			/*try to make a new directory*/
			if(0 == mkdir(path_to_full, st.st_mode))
			{
				chmod(path_to_full, st.st_mode);
			}
			else
			{
				perror("mkdir:");
				//fprintf(stderr, "skip, \"%s\" mkdir failed.\n", path_to_full);
				continue;
			}

			
		    /* 递归处理子目录  */
			if(cp_cmd(path_from, path_to, path_tree_new, cancel_cp) == CP_CANCEL)
			{
				ret_val = CP_CANCEL;
				break;
			}
		}
		else if(S_ISREG(st.st_mode)) //只能拷贝普通文件,略过字符设备文件、块设备文件、FIFO文件、软连接等文件
		{		
			make_path(path_to_full, path_to, path_tree_new);
			ret_val = copy_file(path_from_full, path_to_full, cancel_cp);
			if(ret_val == CP_ERROR)
			{
				break;
			}
			
			copied_size += st.st_size; //  
			if(total_size > 0) // 
			{	
				percent = copied_size * 100 / total_size;
			}
			sprintf(note_text, "%s: %lld B, %s: %lld B.(%d %%)", "Total", 															   total_size, 															   "Copied", 															   copied_size, 														   (int)percent);
        
		}
	}
	closedir(dir);
	
	return ret_val;
}


signed long long  GetDirAllFileSize(const char *path)
{
    DIR *dir;
    struct dirent *entry;
    struct stat stat_buf;
    signed long long  totalSize = 0;
	char tmpPathName[256];
	
    if ((dir = opendir(path)) == NULL)
    {
        printf("cannot open dir:%s\n", path);
        return 0;
    }
	
    while ((entry = readdir(dir)) != NULL)
    {
        strcpy(tmpPathName, path);
        if(path[strlen(path) -1] != '/')
        {
            strcat(tmpPathName, "/");
        }
        strcat(tmpPathName, entry->d_name);
        lstat(tmpPathName, &stat_buf);

        if(S_ISDIR(stat_buf.st_mode) && 0 != strcmp(entry->d_name, ".")
                && 0 != strcmp(entry->d_name, ".."))
        {
            totalSize += GetDirAllFileSize(tmpPathName);
        }
        else if(0 != strcmp(entry->d_name, ".") && 0 != strcmp(entry->d_name, ".."))
        {
            totalSize += stat_buf.st_size;
        }

    }
	
    closedir(dir);
    return totalSize;
}


pthread_t tid1;
pthread_t tid2;
BOOL g_bcpcancel = FALSE;

BOOL cb_cancel_cp(void)
{
	return g_bcpcancel;
}

void *thd_func_counter(void *args)
{
	int loop_counter = 0;
	char ch;
	//sleep(3);
	while( 1 )
	{
		scanf("%c", &ch);
		if(ch == 'c')
		{
			g_bcpcancel = TRUE;
			break;
		}
		
		sleep(1);
	}
}

char from_path[128];
char to_path[128];

void *thd_func_copyfile(void *args)
{
	
	
	sprintf(note_text, "Calc all file size, please wait...");
	total_size = GetDirAllFileSize(from_path);
				
	sprintf(note_text, "%s: %lld KB, %s: %lld KB.(%d %%)", "Total", 															 total_size>>10, 															 "Copied", 															 copied_size>>10, 														 (int)percent);
														   
	cp_cmd(from_path, to_path, NULL, cb_cancel_cp);
	
	sync();
	pthread_cancel(tid1);
	sleep(1);
}



int main(int argc, char *argv[])
{
	//创建2个线程,一个线程随时可以结束拷贝 
	//             另一个线程拷贝数据

	install_timer(1, timer_handler);
	
	strcpy(from_path, argv[1]);
	strcpy(to_path,   argv[2]);
	
	pthread_create(&tid1, NULL, thd_func_counter,  NULL);
	pthread_create(&tid2, NULL, thd_func_copyfile, NULL);
	
	pthread_join(tid1, NULL);
	pthread_join(tid2, NULL);
	install_timer(0, timer_handler);
	
	printf("\n");
	return 0;
}


编译时记得加上-lpthread参数。


本类排行

今日推荐

热门手游