下面程序是一个精简版的多进程拷贝文件,要求源文件是一个文件,而不是一个目录。程序注意点是,必须在创建进程后在进程中打开源和目的文件,否则,
通过fork之后,多个进程会同时共享一个文件表项,导致读写文件混乱。由于每个进程分割地拷贝源文件的各个部分,所以不会出现写入文件混乱
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <assert.h>
#include <sys/wait.h>
#define MAX_PATH 255
#define ONE_M (1024*1024)
#define LINE_SIZE 1024
int copy(const char *src, const char *dst, int processNum);
int multi_process_copy(const char *src, const char *dst, int processNum);
int copy_process(const char *src, const char *dst, int paceSize, int processNum, int srcFileSize);
int core_copy(int fdSrc, int fdDst, int posBegin, int posEnd, int srcFileSize, int flag);
const char *getRetValue(int retCode);
enum error_no
{
ERROR_SUCCESS = 0,
ERROR_INVALID_PARAMS = 1,
ERROR_INVALID_PERMITS = 2,
ERROR_INVALID_FILE_STAT = 3,
ERROR_INVALID_DIR = 4,
ERROR_FILE_CREATE = 5,
ERROR_COPY_FAIL = 6,
ERROR_OPEN_FILE = 7,
ERROR_WAIT_CHILD_PROCESS = 8,
ERROR_SEEK_FILE = 9,
ERROR_WRITE_ERROR = 10
};
int main(int argc, char *argv[])
{
if (argc != 4)
{
printf("usage: %s src dst process_num\n", argv[0]);
return ERROR_INVALID_PARAMS;
}
char src[MAX_PATH] = {0};
char dst[MAX_PATH] = {0};
int processNum = 0;
strncpy(src, argv[1], strlen(argv[1]));
strncpy(dst, argv[2], strlen(argv[2]));
processNum = (int)atoi(argv[3]);
if (processNum < 0) //auto
{
processNum = 0;
}
int ret = copy(src, dst, processNum);
printf("result: %s\n", getRetValue(ret));
}
int copy(const char *src, const char *dst, int processNum)
{
assert(src != NULL);
assert(dst != NULL);
if (access(src, F_OK) < 0)
{
perror("access");
return ERROR_INVALID_PERMITS;
}
struct stat fileInfo;
if (stat(src, &fileInfo) < 0)
{
perror("stat");
return ERROR_INVALID_FILE_STAT;
}
if (fileInfo.st_mode & S_IFDIR)
{
return ERROR_INVALID_DIR;
}
if (access(dst, F_OK) < 0)
{
int fd = open(dst, O_CREAT, 0755);
if (!fd)
{
perror("create");
return ERROR_FILE_CREATE;
}
close(fd);
}
else
{
int fd = open(dst, O_TRUNC);
if (!fd)
{
perror("open");
return ERROR_FILE_CREATE;
}
close(fd);
}
return multi_process_copy(src, dst, processNum);
}
int multi_process_copy(const char *src, const char *dst, int processNum)
{
assert(src != NULL);
assert(dst != NULL);
struct stat fileInfo;
if (stat(src, &fileInfo) < 0)
{
perror("stat");
return ERROR_INVALID_FILE_STAT;
}
int srcFileSize = (int) fileInfo.st_size;
int paceSize = 0;
if (processNum == 0)
{
processNum = srcFileSize / ONE_M + 1;
paceSize = ONE_M;
}
else
{
if (processNum > 1)
{
paceSize = srcFileSize / processNum + srcFileSize % processNum;
}
else
{
paceSize = srcFileSize;
}
}
int retCode = 0;
if ( ( retCode = copy_process(src, dst, paceSize, processNum, srcFileSize)) == ERROR_SUCCESS )
{
return ERROR_SUCCESS;
}
else
{
return ERROR_COPY_FAIL;
}
}
int copy_process(const char *src, const char *dst, int paceSize, int processNum, int srcFileSize)
{
assert(src != NULL);
assert(dst != NULL);
if (paceSize == 0)
{
return ERROR_SUCCESS;
}
if (srcFileSize > 0)
{
int fdDst = open(dst, O_WRONLY);
if (!fdDst)
{
perror("open");
return ERROR_OPEN_FILE;
}
lseek(fdDst, srcFileSize, SEEK_SET);
write(fdDst, '\0', 1);
}
pid_t pid;
int i = 0;
for (; i < processNum; i++)
{
pid = fork();
if (pid == 0)
{
break;
}
}
if (pid == 0)
{
int fdSrc = open(src, O_RDONLY);
if (!fdSrc)
{
perror("open");
return ERROR_OPEN_FILE;
}
int fdDst = open(dst, O_WRONLY);
if (!fdDst)
{
perror("open");
return ERROR_OPEN_FILE;
}
int posBegin = i * paceSize;
int posEnd = posBegin + paceSize;
//printf("process[%d](%d--%d), total: %d\n", i, posBegin, posEnd, posEnd - posBegin);
if ( posBegin <= srcFileSize && posEnd > srcFileSize )
{
return core_copy(fdSrc, fdDst, posBegin, posEnd, srcFileSize, 1);
}
else if ( posBegin <= srcFileSize && posEnd <= srcFileSize )
{
return core_copy(fdSrc, fdDst, posBegin, posEnd, srcFileSize, 0);
}
else
{
return ERROR_SUCCESS;
}
}
else
{
int status;
while(waitpid(-1, &status, 0) > 0)
{
if ( WIFEXITED(status) == 0 )
{
unlink(dst);
return ERROR_COPY_FAIL;
}
}
}
return ERROR_SUCCESS;
}
int core_copy(int fdSrc, int fdDst, int posBegin, int posEnd, int srcFileSize, int flag)
{
off_t size = 0;
if (lseek(fdSrc, posBegin, SEEK_SET) < 0)
{
return ERROR_SEEK_FILE;
}
if (lseek(fdDst, posBegin, SEEK_SET) < 0)
{
return ERROR_SEEK_FILE;
}
int length = 0;
if (flag)
{
length = srcFileSize - posBegin;
}
else
{
length = posEnd - posBegin;
}
char line[LINE_SIZE] = {0};
do
{
if ( length >= LINE_SIZE )
{
//printf("1.length: %d\n", length);
memset(line, 0, sizeof(line));
if ((size = read(fdSrc, line, sizeof(line)))> 0)
{
if ( write(fdDst, line, size) != size )
{
return ERROR_WRITE_ERROR;
}
}
length -= LINE_SIZE;
}
else
{
//printf("2.length: %d\n", length);
memset(line, 0, sizeof(line));
if ((size = read(fdSrc, line, length))> 0)
{
if ( write(fdDst, line, size) != size )
{
return ERROR_WRITE_ERROR;
}
break;
}
break;
}
}while(1);
return ERROR_SUCCESS;
}
const char *getRetValue(int retCode)
{
switch(retCode)
{
case ERROR_SUCCESS:
return "copy complete!!!";
break;
case ERROR_INVALID_PARAMS:
return "invalid params";
break;
case ERROR_INVALID_FILE_STAT:
return "cannot stat the file";
break;
case ERROR_INVALID_DIR:
return "the src file cannot be directory";
break;
case ERROR_FILE_CREATE:
return "error in creating the dest file";
break;
case ERROR_COPY_FAIL:
return "copy file error";
break;
case ERROR_OPEN_FILE:
return "open file error";
break;
case ERROR_WAIT_CHILD_PROCESS:
return "cannot get the child status";
break;
case ERROR_SEEK_FILE:
return "cannot seek this file, maybe not regular file";
break;
case ERROR_WRITE_ERROR:
return "cannot write the dst file";
break;
}
}