C - HsuJv/Note GitHub Wiki
- 第一个成员在与结构体变量偏移量为0的地址处。
- 其他成员变量要对齐放到对齐数的整数倍的地址处。对齐数 = min(编译器默认的一个对齐数, 该成员大小),这个默认值一般是机器字长。
- 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
-
标准IO库提供缓冲的目的是尽可能减少使用read和write调用的次数,降低执行IO的时间,它提供三种类型的缓冲:
- 全缓冲。在填满标准IO缓冲区4096Bytes后(缓冲区已满)才进行实际IO操作(通过write系统调用,将数据传递到内核高速缓冲区,最终内核将数据写入磁盘),对于磁盘文件通常就是全缓冲(fprintf)。
- 行缓冲。在输入和输出中遇到换行符时(缓冲区已满)进行实际的IO操作(通过write系统调用,将数据传递到内核高速缓冲区,最终内核将数据写入磁盘),当涉及到一个终端时,通常使用行缓冲。使用最频繁的printf函数就是采用行缓冲,所以感觉不出缓冲的存在。
- 不带缓冲。标准IO库不对字符进行缓冲存储。标准出错流stderr通常是不带缓冲的。
-
ISO C要求下列缓冲特征:
- 当且仅当标准输入和标准输出并不涉及交互式设备时,它们才是全缓冲的。
- 标准出错决不会是全缓冲。
-
很多系统默认使用下列类型的缓冲:
- 标准出错是不带缓冲的。
- 如若是涉及终端设备的其它流,则他们是行缓冲的;否则是全缓冲的。
-
当然,对于标准IO流,我们也可以更改缓冲类型,或者是直接刷新。ISO C中提供下面两个函数以更改缓冲类型:
-
void setbuf(FILE *fp, char *buf);
buf为NULL,表示关闭缓冲 -
int setvbuf(FILE *fp, char *buf, int mode, size_t size);
setvbuf函数中的mode参数可以为:_IOBUF 全缓冲, _IOLBF 行缓冲, _IONBF 不带缓冲,如果buf为NULL, 则标准IO库将自动地为该流分配适当长度(常量BUFSIZ)的缓冲区。一般而言,应由系统选择缓冲区的长度,并自动分配缓冲区,这样关闭流时,标准IO库将自动释放缓冲区。
-
-
强制冲洗一个流,使用函数:
int fflush(FILE *fp);
fflush(NULL);
-
补充:
- read()和write()系统调用在操作磁盘文件时不会直接发起磁盘请求,而是仅仅在用户空间缓冲区与内核缓冲区高速缓存之间复制数据。write()随机返回。在后续某个时刻,内核会将其缓冲区中的数据写入(刷新至)磁盘。(因此,可以说系统调用与磁盘操作并不同步)
- 与此同理,对输入而言,内核从磁盘中读取数据并存储到内核缓冲区中。read()调用将从该缓冲区中读取数据,直至把缓冲区中的数据读完,这时,内核会将文件的下一段内容读入缓冲区高速缓存。
- 这样设计,使得read()和write()很快,不需要等待(缓慢的)磁盘操作。同时,这一设计也极为高效,因为这减少了内核必须执行的磁盘传输次数。(预读和满写)
- 两句话:
- read()和write()负责在用户空间缓冲区和内核高速缓冲区高速缓存复制数据。
- 内核负责从磁盘读数据到内核高速缓冲区(预读),以及当内核高速缓冲区满了,写到磁盘中去(满写)。
-
总结:
- 自上而下,首先是通过stdio库将用户数据传递到stdio缓冲区(一般是4096Bytes,或者也可以有标准IO自动分配),该缓冲区位于用户态内存区。当缓冲区满时(行缓冲遇到‘\n',全缓冲满4096Bytes),stdio库会调用write()系统调用,将数据传递到内核高速缓冲区(位于内核态内存区)。最终,内核发起磁盘操作,将数据传递到磁盘。