前言
前一段学习Linux编程的时候准备了一些小程序来练手,今天写到了多线程文件拷问,遇到了一些问题,总结一下。
程序功能
启动程序输入源文件名和目的文件名,可以将源文件内容复制一份到目的文件。
程序分析
- 打开源文件,获取(fstat)源文件大小
- 打开(创建)目的文件,修改(ftruncate)目的文件的大小
- 创建线程,进行拷贝,对文件大小分解,每个线程只负责拷贝部分文件,拷贝大小通过len/MAX_PTHREAD计算,对最后一个进程多拷贝len%MAX_PTHREAD个字节。采用 fseek移动文件偏移量,nleft确定需要拷贝的字节的大小。
遇到的问题
- 写入的大小不能用strlen控制:因为读取的数据内有’\0’数据,strlen遇到’\0’停止计算长度,导致fwrite写入的数据小于fread读到的输入。
- fread读取的文件偏移量问题:一个文件指针有一个偏移量,在线程中共享,为了避免当前线程的文件偏移量不会被其他线程修改,需要对文件偏移量加锁,对文件加锁即对文件偏移量加锁。因为线程的主要任务就是根据文件的偏移量对文件进行读写,如果对其加锁就没有多线程的意义了,这里采用每个线程对文件打开一次,修改文件偏移量,保证当前线程的文件偏移量不会被其他线程修改。
程序测试
因为最初写这个程序的时候读写是使用的文件描述符,对文件读写时的文件偏移量无法有效控制,导致运行程序不稳定,有时正确,有时错误。后来改成文件指针的形式,随便测了几次,都正确了,想确定是否真的正确,我写了一个守护进程每隔一分钟测一次,经过一晚上的测试,结果完全正确,截图如下:
程序源码
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<cstdlib>
#define MAX_PTHREAD 100
using namespace std;
struct Thread_information
{
char* src;
char* dst;
int ith;
};
int len;//文件的总大小
void sys_err(const char* err)
{
perror(err);
exit(1);
}
void *copy(void *arg)
{
Thread_information p = *((Thread_information*)arg);
delete (Thread_information *)arg;
int nleft = len/MAX_PTHREAD;
if(p.ith+1==MAX_PTHREAD)
{
nleft += len%MAX_PTHREAD;
}
FILE * SRC = fopen(p.src,"r");
FILE *DST = fopen(p.dst,"w");
int ret = fseek(SRC,p.ith*(len/MAX_PTHREAD),SEEK_SET);
if(ret == -1)
sys_err("fseek error");
ret = fseek(DST,p.ith*(len/MAX_PTHREAD),SEEK_SET);
if(ret == -1)
sys_err("fseek error");
int nread;
char buf[1024];
// printf("%d %d %d\n",p.ith,(int)srcfp,(int)dstfp);
// fflush(stdout);
while(nleft>0)
{
if(nleft>=1024)
nread = fread(buf,1,sizeof(buf),SRC);
else
nread = fread(buf,1,nleft,SRC);
// if(nread==0)
// printf("%d : read %d nleft %d curseek %d\n",p.ith,nread, nleft,(int)lseek(p.srcfd,0,SEEK_CUR));
nread = fwrite(buf,1,nread,DST);
nleft -= nread;
}
fclose(SRC);
fclose(DST);
printf("-");
fflush(stdout);
}
int main(int argc, char *argv[])
{
Thread_information *information = new Thread_information;
if(argc!=3)
{
printf("usage: %s <srcfile> <dstfile>\n",argv[0]);
}
int srcfd = open(argv[1],O_RDONLY);
if(srcfd==-1)
sys_err("open srcfile error");
int dstfd = open(argv[2],O_WRONLY|O_CREAT,0664);
if(dstfd==-1)
sys_err("open dstfile error");
struct stat stcur;
int ret = fstat(srcfd,&stcur);
if(ret==-1)
sys_err("fstat error");
len = stcur.st_size;
ret = ftruncate(dstfd,len);
if(ret==-1)
sys_err("ftruncate error");
close(srcfd);
close(dstfd);
pthread_t pid[MAX_PTHREAD];
for(int i = 0; i< MAX_PTHREAD; i++)
{
information = new Thread_information;
information->ith = i;
information->src = argv[1];
information->dst = argv[2];
if(pthread_create(&pid[i],NULL,copy,(void *)information)!=0)
perror("create");
}
for(int i = 0; i < MAX_PTHREAD; i++)
pthread_join(pid[i],NULL);
printf("\n");
return 0;
}