且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2022-09-16 17:11:53

笔者的某个目录下面有两千个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<2000;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
















本文转自存储之厨51CTO博客,原文链接:http://blog.51cto.com/xiamachao/1867239 ,如需转载请自行联系原作者