日期:2014-05-16  浏览次数:20715 次

怎样提高写文件的速度
情况是这样的:我在写一个从网络接收报文,然后把报文存在一个文件里面的程序。报文发送的间隔达到了几十微秒,时间非常短。
这部分代码如下:
if(*counter==1)
  {
  if((fd=open("/home/goosefile.txt",O_RDWR|O_CREAT))<0)
  {
  DBGL("file open failed\n");
  exit(1);
  }
  }
  if((file_w=fork())<0)
  {
  DBGL("fork file operation failed\n");
  exit(1);
  }  
  if(file_w>0)
  {
  waitpid(file_w,NULL,0);
  ++file_count;
  printf("countnum is %lu\n",file_count);//这是计算接收到的报文数量
  }
  else if(file_w==0)
  {  
  lseek(fd,10,SEEK_END);  
  if((writefd=write(fd,packet,pkthdr->len))<0)
  {
  DBGL("file write failed\n");
  exit(1);
  }  
  exit(1);
  }

  文件只在第一个报文到来的时候打开一次,每次来一个报文都开辟一个进程,主进程等待子进程结束,子进程退出。 但是测试结果非常不理想:发10000个报文只能接收到7000个左右,而且如果提高发送速率的话,收到的报文就更少了。
  就是说,现在如何提高写文件的速度。

------解决方案--------------------
用标准I/O函数,,fprintf,fscanf,fwrite,fread,fopen,fclose,,,这个自带缓冲。
或者如果你非要用write,read读写文件的话,就自己加个缓存,,每4KB或者8KB时一次性写入。这样磁盘I/O效率最高。而用标准I/O函数就不用考虑这个问题了,因为他自带缓冲

还有你不应该使用接收一个报文就fork一下,,fork的开锁是比较大的。。
应该使用单消费者/单生产者的模型或者单生产者/多消费者模型。。一个线程负责接收报文放到公共缓冲区,另一个(或多个)线程负责从公共缓冲中取报文,然后fwrite到文件。 单生产者一般用环形缓冲配合锁实现,,自己搜索下,
------解决方案--------------------
网络I/O线程负责收,直接丢到链式队列里,让文件I/O线程去顺序写。

方案1:直接mutex+cond+queue,网络I/O线程只管往queue里丢了就signal唤醒cond,无须判断队列容量,直到撑爆内存core掉为止(可做内存池控制进程内存low-waterline防core,不过会导致丢弃部分报文,也可直接控制队列容量,丢弃报文即可,但不可挂起cond导致阻塞网络I/O),文件I/O就是取了去写,队列空就挂起cond。

方案2:epoll+pipe/socketpair+1字节做文件I/O实践触发, 写pipe失败则丢弃报文即可,或者和上面一样直接控制队列尺寸即可.
------解决方案--------------------
楼上都有大的建议,我说一句,就是要接受线程 与 写文件线程分开。 还有如果不知道你写的文件内容以后只是留作记录。为什么不考虑共享缓冲区代替反复写硬盘,把受到的内容放在内存中,退出时统一刷到硬盘。
------解决方案--------------------
探讨

不能放到内存中,内存才多大呀。我们的仪器是要一直运行的。
现在我是想着线程或者进程的话,肯定要涉及到同步机制,因为我们对报文的记录每一帧都非常严格,不能丢掉任何一个。

------解决方案--------------------
内存映射最快, 1G的文件用内存映射可以秒, 普通I/O就慢的要命.

楼主完全可以做内存映射, 
1,自己估摸一下最长的报文是max_size.
2,将max_size向上对齐到4倍数得到piece_size.
3,计算一下1G/piece_size等于n条。

以后每次将文件尺寸扩展1G,然后内存映射,以piece_size为偏移最小单位,每次偏移过去将报文复制上去。

64位服务器16G内存,可以每次映射8G,自己看着弄。

需要在文件头部维护一个结构体,存储这个文件当前多少个G了,以及在当前那1G里写了多少piece了。
------解决方案--------------------
只是用来持续的写文件,,用内存映射并不会快的,,你这样占的内存也太大了些吧,,,并且把问题复杂化了。
简单问题简单解决,,两线程的单生产者/单消费者+fwrite写文件就行了。
------解决方案--------------------
探讨

只是用来持续的写文件,,用内存映射并不会快的,,你这样占的内存也太大了些吧,,,并且把问题复杂化了。
简单问题简单解决,,两线程的单生产者/单消费者+fwrite写文件就行了。

------解决方案--------------------
mmap ?