您的位置:首页 > 运维架构 > Linux

嵌入式Linux cp命令实现

2014-09-12 18:38 477 查看
最近项目在数据导出的时候出现问题,基本思路是:

首先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参数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: