最近项目在数据导出的时候出现问题,基本思路是:
首先vfork创建一个进程,子进程中创建2个线程,线程1负责界面显示,线程2负责数据拷贝;父进程保存配置文件并退出应用;
其中,线程2中又创建一个进程用来调用系统cp命令,在线程1中又vfork使用类system命令函数发送kill命令杀死cp拷贝(kill -9 $(pgrep cp));
通过以上思路,可以知道,设计混乱,线程中不断创建进程,进程又线程的,好歹哥也编程几年了,怎么能容忍这么垃圾的代码存在,闲暇之时,改之;
新的思路:
自己实现cp命令,显示界面交给定时器处理;
但是还有个问题就是我拷贝数据时怎么取消,如果一个文件过大怎么办,解决办法为:定义一个回调函数,在循环的条件中也使用该函数的返回值,该回调函数返回值为一个全局变量,即可解决。
以下是demo程序:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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("
%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)
");
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
", 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".
", 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".
", 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.
", 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.
", 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
", 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("
");
return 0;
}
编译时记得加上-lpthread参数。