linux线程和fopen()fclose()fgets()

linux线程和fopen()fclose()fgets(),第1张

概述我正在看一些使用pthreads的遗留 Linux代码. 在一个线程中,通过fgets()读取文件. FILE变量是在所有线程之间共享的全局变量. (嘿,我没写这个…) 在另一个线程中,FILE一次又一次地关闭并用另一个文件名重新打开. 发生这种情况几秒钟后,线程fgets()就像继续读取它从上一个文件中读取的最后一条记录一样:几乎就像有错误但fgets()没有返回NULL.然后它自行排序并开始从 我正在看一些使用pthreads的遗留 Linux代码.

在一个线程中,通过fgets()读取文件. file变量是在所有线程之间共享的全局变量. (嘿,我没写这个…)

在另一个线程中,file一次又一次地关闭并用另一个文件名重新打开.

发生这种情况几秒钟后,线程fgets()就像继续读取它从上一个文件中读取的最后一条记录一样:几乎就像有错误但fgets()没有返回NulL.然后它自行排序并开始从新文件中读取.

代码看起来有点像这样(为了简洁起见,我希望它仍然可以理解):

在一个线程中:

while(gRunState != S_EXIT){  nanosleep(&timer_delay,0);  flag = fgets(buff,sizeof(buff),gfile);  if (flag != NulL){    // do something with buff...  }}

在另一个线程中:

fclose(gfile);gfile = fopen(newfilename,"r");

没有锁定以确保fgets()不会与fclose()/ fopen()同时调用.

有关失败模式的任何想法可能导致fgets()失败但不返回NulL?

解决方法 所描述的代码如何出错

stdio库缓冲数据,分配内存以存储缓冲的数据. GNU C库动态分配文件结构(某些库,特别是在Solaris上,使用指向静态分配的文件结构的指针,但缓冲区仍然是动态分配的,除非你设置缓冲否则).

如果您的线程使用指向全局文件指针的指针的副本(因为您将文件指针作为参数传递给函数),那么可以想象代码将继续访问已经分配的数据结构(甚至虽然它被关闭释放了,但是会从已经存在的缓冲区中读取数据.只有当您退出函数或读取超出缓冲区内容时才会出现问题 – 或者先前分配给文件结构的空间将重新分配以供新用途使用.

file *global_fp;voID somefunc(file *fp,...){    ...    while (fgets(buffer,sizeof(buffer),fp) != 0)        ...}voID another_function(...){    ...    /* Pass global file pointer by value */    somefunc(global_fp,...);    ...}

概念证明代码

使用GCC 4.0.1在MacOS X 10.5.8(Leopard)上测试:

#include <stdio.h>#include <stdlib.h>file *global_fp;const char etc_passwd[] = "/etc/passwd";static voID error(const char *fmt,const char *str){    fprintf(stderr,fmt,str);    exit(1);}static voID abuse(file *fp,const char *filename){    char buffer1[1024];    char buffer2[1024];    if (fgets(buffer1,sizeof(buffer1),fp) == 0)        error("Failed to read buffer1 from %s\n",filename);    printf("buffer1: %s",buffer1);    /* Dangerous!!! */    fclose(global_fp);    if ((global_fp = fopen(etc_passwd,"r")) == 0)        error("Failed to open file %s\n",etc_passwd);    if (fgets(buffer2,sizeof(buffer2),fp) == 0)        error("Failed to read buffer2 from %s\n",filename);    printf("buffer2: %s",buffer2);}int main(int argc,char **argv){    if (argc != 2)        error("Usage: %s file\n",argv[0]);    if ((global_fp = fopen(argv[1],argv[1]);    abuse(global_fp,argv[1]);    return(0);}

在自己的源代码上运行时,输出为:

Osiris JL: ./xx xx.cbuffer1: #include <stdio.h>buffer2: ##Osiris JL:

因此,经验证明在某些系统上,我概述的情景可能会发生.

如何修复代码

在其他答案中很好地讨论了对代码的修复.如果你避免我说明的问题(例如,通过避免全局文件指针),这是最简单的.假设这是不可能的,使用适当的标志进行编译可能就足够了(在许多类Unix系统上,编译器标志’-D_REENTRANT’完成工作),最终你将使用基本标准的线程安全版本I / O功能.如果做不到这一点,您可能需要围绕对文件指针的访问放置明确的线程安全管理策略;一个互斥体或类似的东西(并修改代码以确保线程在使用相应的文件指针之前使用互斥锁).

总结

以上是内存溢出为你收集整理的linux线程和fopen()fclose()fgets()全部内容,希望文章能够帮你解决linux线程和fopen()fclose()fgets()所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/1025256.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-23
下一篇 2022-05-23

发表评论

登录后才能评论

评论列表(0条)

保存