前言
I/O泛指程序和外界的各种交互作用,包括file,pipe,network,console,semaphore等等。或者泛指能被OS理解为file的任何事物——file是一种广义概念。
FILE *fp=fopen("xxx.dat",“wb”);
很少人去关注这个FILE是个什么东西,这关系到我们接下来要谈的启动代码startcode对于I/O的初始化。
struct _iobuf{...};
typedef struct _iobuf FILE;
C语言层面通过一个pointer to FILE 来进行file操作。
在OS操作系统层面,Linux对应于FILE的是File Desriptor(fd),Windows对应的则是file handle。两者都是来映射kernel file object,但经过包装最后都成为了C语言所认识的pointer to FILE。
我们在编程时,只需要关注的时C语言层面的pointer to FILE,但是其与操作系统OS的fd或file handle有着一一对应的关系。我们现在就把这层关系挖出来。我们就能了解_IO_INIT在做什么。
fd是什么
fd 具体是个index of opened file table ——process个别拥有的这个table是个array of pointers,每个point指向一个kernel opened object。当client开启一个file,OS会建立一个(kernel)opened file object 并找到上述table中的一个idle entry指向之,然后以该entry 的 index 当作 fd。 此table位于kernel mode,因此client客户即使拥有fd亦无法获取table address。Linux的fd 0,1,2分别代表stdin,stdout,stderr。
C的FILE 与 Linux的 fd 必有一对一关系。只要有table address p,p+fd 就指向opened file table 的某个entry,从而可得kernel file object。(kernel mode所在得地址,使用者user mode是永远拿不到的)
Windows的file handle和Linux的fd大同小异,但handle不是idex,而是index经某种变种后的结果。
I/O initialization就是要在client space 中建立起stdin、stdout、stderr及其对应的FILEs,使程序进入main()之后立即可以使用printf()、scanf()等函数。
_io_init() 与 fopen() 总览
1._io_init行为
下图右侧的备注建议好好阅读下。
注意图中序号对应部分。
①:ioinfo是什么?其对应到C/C++程序对应的fopen得到的变量(FILE *fp=fopen("xxx.dat",“wb”)中的FILE* 变量)。Linux对应于FILE的是File Desriptor(fd),Windows对应的则是file handle。
每个进程至少有三个file handle(stdin,stdout,stderr),接下来的动作就是把继承下来的这三个或更多的file handle抄录到struct ioinfo。所以,一个进程最多可以开出64*32=2048个FILE,其中包括从父进程继承下来的部分。
③:操作系统怎么把继承而来的这三个或更多的file handle拷贝到struct ioinfo?
⑥:注意相同颜色部分。C语言开启一个FILE以后得到指针指向_iobuf这么一个结构。其中的_file(2^11=2048)怎么解释呢?其中的六个bit(#5~#10)表示第一维索引,共2^6=64。其他五个bit(#0~#4)表示第二维索引,共2^5=32个ioinfo。.
2.kernel mode 与 user mode 之间的桥梁
lpReserved2 将指向 inherited handles info 。字节0~3表示一个N值,代表了继承了几个file handle(原则上一般至少有stdin、stdout、stderr 三个file handle)。 N value for OSfile 则是 ioinfo 结构体中的 char os file,N OS handle values 则是ioinfo结构体中的 long osfhnd。
借由调用GetstartupInfo函数,取得lpReserved2指针,指向inherited handles info,取出N,OSfile,osfhnd,copy填入到ioinfo的结构体中。借由这种方式去登记/记载 父进程继承而来的file handle。
由上可知一个进程最多可以容纳/拥有/开启 32*64=2048 个ioinfo 。而fopen()会去寻找一个还没有使用的.
实验
参考:博览网——侯捷 - C++ Startup 揭密:C++ 程序的生前和死后