由打开文件失败引发的思考

笔者的某个目录下面有两千个c文件需要处理,为了快速实现,写了下面的代码去打开:

1
#include <stdio.h>

2
#include <string.h>

3

4
int main(void)

5
{

6
   int i = 0;

7
   FILE * fp = NULL;

8
   char filename[10] = {0,};

9

10
   printf("Hello!\n");

11

12
   for(i=0;i<8192;i++) {

13
       memset(filename, 10, 0);

14
       sprintf(filename, "t%d.c", i);

15
       fp = fopen(filename, "a+");

16
       printf("Open %s successfully: Turn %d done!\n",
filename, i);

17
   }

18

19
   return 0;

20
}

执行得很好,可是在上面15/16行之间加上一句写的话,却执行失败了:

…...........

15
       fp = fopen(filename, "a+");

16
       fwrite(filename, 1, strlen(filename), fp);

17
       printf("Open %s successfully: Turn %d done!\n",
filename, i);

…..........

执行结果如下:

Open
t1018.c successfully: Turn 1018 done!

Open
t1019.c successfully: Turn 1019 done!

Open
t1020.c successfully: Turn 1020 done!

Segmentation
fault (core dumped)。

正在百思不得其解的时候,突然想起了大三操作系统课程上讲进程块的时候提到了打开文件记录表这个信息,于是打开0.96内核的源代码,进去瞅了下,果然记录每个进程打开文件的数目是一个数组,而不是可以无限扩展的链表,0.96/linux/include/linux/sched.h中的代码如下:

112
struct task_struct {

113
/* these are hardcoded - don‘t touch */

114
   long state; /* -1 unrunnable, 0 runnable, >0 stopped */

115
   long counter;

116
   long priority;

117
   long signal;

118
   struct sigaction sigaction[32];

119
   long blocked;   /* bitmap of masked signals */

120
/* various fields */

121
   int exit_code;

122
   int dumpable;

123
   unsigned long start_code,end_code,end_data,brk,start_stack;

124
   long pid,pgrp,session,leader;

125
   int groups[NGROUPS];

126
   /*

127
    * pointers to (original) parent process, youngest child, younger
sibling,

128
    * older sibling, respectively.  (p->father can be replaced
with

129
    * p->p_pptr->pid)

130
    */

131
   struct task_struct *p_opptr,*p_pptr, *p_cptr, *p_ysptr, *p_osptr;

132
   /*

133
    * sleep makes a singly linked list with this.

134
    */

135
   struct task_struct *next_wait;

136
   unsigned short uid,euid,suid;

137
   unsigned short gid,egid,sgid;

138
   unsigned long timeout;

…...............

156
   struct {

157
       struct inode * library;

158
       unsigned long start;

159
       unsigned long length;

160
   } libraries[MAX_SHARED_LIBS];

161
   int numlibraries;

162    struct file * filp[NR_OPEN];

163
   unsigned long close_on_exec;

164
/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */

165
   struct desc_struct ldt[3];

166
/* tss for this task */

167
   struct tss_struct tss;

168
};

在0.96内核里面0.96/linux/include/linux/fs.h中,NR_OPEN被定义成一个比较小的数目:

#define
NR_OPEN 32

虽然我用的是4.2的内核,但这个限制仍然存在。具体可参考文件uapi/linux/limits.h,里面定义了打开文件数目、文件名长度等限制。在proc/fs等模块中,会包含这个头文件。

#define
NR_OPEN         1024

#define
NGROUPS_MAX    65536    /* supplemental group IDs are available */

#define
ARG_MAX       131072    /* # bytes of args + environ for exec() */

#define
LINK_MAX         127    /* # links a file may have */

#define
MAX_CANON        255    /* size of the canonical input queue */

#define
MAX_INPUT        255    /* size of the type-ahead buffer */

#define
NAME_MAX         255    /* # chars in a file name */

#define
PATH_MAX        4096    /* # chars in a path name including nul */

#define
PIPE_BUF        4096    /* # bytes in atomic write to a pipe */

#define
XATTR_NAME_MAX   255    /* # chars in an extended attribute name */

#define
XATTR_SIZE_MAX 65536    /* size of an extended attribute value (64k)
*/

#define
XATTR_LIST_MAX 65536    /* size of extended attribute namelist (64k)
*/

#define
RTSIG_MAX     32

但接着问题来了,为啥只是调用fopen()没有问题,在它后面调用fwrite()之后才会出现问题呢?而且,为何t1020.c以及之前的写操作都没有落盘呢?后来又仔细想了下vfs/文件系统/bio/块设备这些模块的具体流程,直觉是vfs和文件系统之间有延迟分配:

1.文件描述符只有真正写的时候才会分配,这样才会占用进程描述块中打开文件表中的一个槽位;

2.现代的大部分文件系统数据都是先写到为文件系统分配的page
cache里面,只有被要求刷新之后,

才会从磁盘上去寻找一块接纳page
buffer中数据的空间,然后把脏page
cache的内容写回。而在我上面的程序执行失败之前,一直没有调用fflush()去刷新,数据自然不能落盘。后来在fwrite()之后,加上了一句fflush(fp),果然之前写出的数据都能落盘。

通过上面的简单程序,可以看到如果要对C语言的IO操作有深入认识,有赖于对内核中文件系统、系统IO路径的深入理解,只有这样我们才能透过段错误、数据无法写入等现象,看到背后文件系统在执行的本质。

相关连接:

http://blog.csdn.net/kai_ding/article/details/9914629

http://blog.csdn.net/dongpy/article/details/4552062

时间: 2024-10-12 04:03:00

由打开文件失败引发的思考的相关文章

fopen打开文件失败原因分析

场景说明: 今天同事说,在已有的工具代码中,无法使用fopen打开文件,然后走过去进行协助.但是在解决问题的时候,已经先入为主的认为是:文件路径出现了字符转义的问题,根本没有想到要打印出当前无法获取到文件句柄的错误,是否是文件路径不存在.所以一开始就是将\替换为/,结果可想而知,没有任何的作用.然后替换为\\,还是不行.没有理由的,一直认为. 回去单独写最简单的测试程序,没有问题.怀疑是Unicode编码的问题,也排除了.这时候就开始百度fopen失败的原因获取.刚开始的时候准备使用GetLas

fopen打开文件失败的问题

fopen打开带中文路径或含中文名称的文件失败. 解决这个问题有两个方法:一是改用_wfopen,这个函数接受两个宽字符类型,函数原型如下: FILE* _wfopen(const wchar_t* filename, const wchar_t* mode); 参数一表示文件名,参数二表示打开模式,返回文件描述符 第二种方法还是用fopen,这种方式文件名称编码需要与系统保持一致. fopen是C标准库的一个函数,函数内部是系统调用.Windows中调用CreateFile:Linux中调用o

一次安装rpcbind失败引发的思考

问题: yum install rpcbind -y 出现如下错误: Error in PREIN scriptlet in rpm package rpcbind-0.2.0-11.el6.x86_64 error: %pre(rpcbind-0.2.0-11.el6.x86_64) scriptlet failed, exit status 1 error: install: %pre scriptlet failed (2), skipping rpcbind-0.2.0-11.el6 V

剖析ifstream打开含中文路径名文件失败的原因

http://blog.csdn.net/yukin_xue/article/details/7543423 最近写程序的时候遇到了使用ifstream打开含中文路径文件时失败的问题,在网上翻了一下,发现这是一个普遍遇到的问题,在很多人的博文中也都给出了一些解决技巧,但大多是转载的东西,很少对这个问题引发的原因有一个清晰.全面的解释.因此,我觉得有必要对该问题引发的原因作一个详细的剖析,希望对遇到同样问题的朋友们能有所帮助. 首先,用一个简单的例子来重现一下我所遇到的问题: (1)在VS2008

打开文件open()函数的使用方法详解--C语言函数

头文件:#include <sys/types.h>    #include <sys/stat.h>    #include <fcntl.h> 定义函数: int open(const char * pathname, int flags); int open(const char * pathname, int flags, mode_t mode); 函数说明: 参数 pathname 指向欲打开的文件路径字符串. 下列是参数flags 所能使用的旗标: O_R

【mfc】使用系统文件对话框打开文件与保存文件、利用StdAfx.h设置全局变量

在<[mfc]利用文件的读写,theApp全局变量来现实登录帐号管理系统>(点击打开链接)中,虽然也使用到文件的打开与保存,但这是在用户打开与关闭对话框的时候就自动进行的.现在设置一个"打开"按钮与一个"保存"按钮,可以让用户自由选择保存的文件的位置,输入文件名,但是,保存的文件格式只能是我们指定的.ifo,因为以后打开文件,我们只接受.ifo的打开.本文亦是<[mfc]不同对话框之间互相操控.全局变量与日期控件>(点击打开链接)的进一步工作

一次部署HTTPS的相关事件引发的思考

前言: 上周五快要下班的时候,突然收到通知客户希望了解一下部署HTTPS的流程,这种事情谁听了都会有几分诧异的.因为这件事虽然和工作有一定的相关度,但平时不会走这个方向,实际上也较少接触.此外,客户手下应该不缺人,做运维和开发的肯定比我更懂这个,但情况却和我想的不一样. 正文: 客户有需求,就应该尽量满足!因此,尽管之前对Apache.Tomcat的一些配置不熟,也未有过自己部署HTTPS的经验[当然失败的尝试还是有的],便趁着周末了解了一下相关的东西,在本地搭建了环境.实践表明,当你对一个东西

C++文件操作:打开文件和写入文件 zz

http://www.weixueyuan.net/view/5825.html 如果程序的运行结果仅仅显示在屏幕上,当要再次查看结果时,必须将程序重新运行一遍:而且,这个结果也不能被保留. 如果希望程序的运行结果能够永久保留下来,供随时查阅或取用,则需要将其保存在文件中. 文件分类 外部文件和内部文件 外部文件:指磁盘文件,也是通常所谓的文件. 内部文件:指在程序中运行的文件,更正式的称谓是“文件流对象”. 程序中操作的是内部文件,最后保存的是外部文件. 文本文件和二进制文件 文本文件:由字符

UPDATE 时主键冲突引发的思考【转】

假设有一个表,结构如下: root@localhost : yayun 22:59:43> create table t1 ( -> id int unsigned not null auto_increment, -> id2 int unsigned not null default '0', -> primary key (id) -> )engine=myisam; Query OK, 0 rows affected (0.00 sec) root@localhost