嵌入式Linux cp命令实现

2019-07-12 15:23发布

   最近项目在数据导出的时候出现问题,基本思路是:            首先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参数。