每日一练(三十二)


《每日一练合集》

2.1 lseek() 空洞

每个打开的文件都有一个当前文件偏移量,后续的读、写操作都是基于这个文件偏移量进行的,通常是一个非负整数。

默认打开一个文件的时候,文件偏移量为0,除非指定以O_APPEND追加方式打开。

SYNOPSIS
       #include <sys/types.h>
       #include <unistd.h>

       off_t lseek(int fd, off_t offset, int whence);

DESCRIPTION
       lseek() repositions the file offset of the open file description associated with the file descriptor fd
       to the argument offset according to the directive whence as follows:

       SEEK_SET
              The file offset is set to offset bytes.

       SEEK_CUR
              The file offset is set to its current location plus offset bytes.

       SEEK_END
              The file offset is set to the size of the file plus offset bytes.

       lseek() allows the file offset to be set beyond the end of the file (but this does not change the  size
       of  the  file).   If  data  is  later written at this point, subsequent reads of the data in the gap (a
       "hole") return null bytes ('\0') until data is actually written into the gap.

当文件偏移量大于文件的当前长度时,并且对文件下一次写的时候,将加长该文件,并在文件中构成一个空洞,且位于文件中但是没有被写过的字节在读的时候都将为0.

注意!!!由写入位置大于文件长度造成的空洞在磁盘上是不占用存储区的,这个具体的实现和文件系统的实现有关。

在这里插入图片描述

还有,在特殊情况下偏移量是可以为负数的,而lseek返回的是当前的偏移量,所以判断出错的时候不可以判断为负数,而是要判断 是否为 -1.

2.2 文件共享

Linux使用三种数据结构来表示一个被打开的文件:

  • 进程:每个进程的进程表中有一个记录项,记录项中包含了一张打开的文件描述符的表,每个文件描述符都关联了一个指针,指向文件表项
  • 内核:内核中为打开的文件维护一张文件表。文件表中包含了:文件状态标志、当前文件的偏移量、指向文件v节点的指针
  • 文件:每个打开的文件都有一个v节点表(v-node),节点中包含了文件类型和对此文件进行各种操作的函数的指针。对于大多数文件,v节点表含包含了文件的i节点(i-node,索引节点),i节点中包含了文件的所有者、文件长度、指向文件实际数据块在磁盘上的指针。这些信息都是在打开文件的时候从磁盘上读入内存的。

三者的关系如下,可以看作是从进程到内核,再从内核到内存
在这里插入图片描述

如果是两个独立的进程打开同一个文件,也就是文件的共享,情况如下:
在这里插入图片描述

共享文件的时候,内核中为每个进程都创建了一张文件表,这种安排是为了每个进程都有它自己对该文件的偏移量!!!

还有lseek函数只是修改了文件表项中当前文件的偏移量,并没有进行任何IO操作

这里要注意!!!使用O_APPEND标志打开一个文件,然后再写入。和直接使用lseek函数的SEEK_END定位到文件末尾写入。是不同的!!!

使用O_APPEND打开文件的时候,会将相应标志位的状态保存到文件表中,但是文件偏移还是默认0,只有在每次对文件写入的时候,当前文件的偏移量首先设置为i节点的表项中的文件长度,所以写入的数据就自动添加到文件的当前尾部。

而lseek不同,调用lseek写入尾端的时候,会直接改写文件表项中文件的偏移量。

还要注意文件描述符标志和文件状态标志的区别:

  • 文件描述符标志,用于一个进程的一个描述符
  • 文件状态标志,用于指向该文件表项的任何进程中的所有描述符

2.3 原子操作

由于每个进程对于文件都有自己的文件表项,文件表项中都有自己的偏移量,当多个进程同时写一个文件的时候,可能会导致意为的结果,为了避免这种情况,就要了解原子操作的概念了。

原子操作指的是由多步组成的操作,如果该操作是原子 地执行,则要么执行完所有步骤,要么一步也不执行,不可能只执行所有步骤的一个子集。

上面的文件共享中,如果两个进程要对同一文件的尾部追加内容,:

  • 使用O_APPEND的方式打开,然后写入
  • 默认方式打开,然后通过lseek定位写入尾部

其中使用lseek定位写入的时候可能会有意想不到的结果,如下:
在这里插入图片描述

所以,为了保证操作的原子性,尽量在打开文件的时候就设置O_APPEND标志,这样每次进行写的时候内核都会根据当前文件的大小设置偏移量。不必在写之前调用lseek,以防止进行多步骤操作时,其他进程打断操作。

类似的原子操作情况在创建文件的时候也是:

在这里插入图片描述

2.4 Uboot启动流程

Uboot是Linux上电之后执行的第一个程序,其主要对板子进行一些必要初始化操作,然后引导内核的加载,内核再挂载根文件系统,最后就可以执行应用程序了。

Uboot的启动流程分为两个阶段,汇编语言阶段和C语言阶段,下面简单分析一下Uboot的启动流程:
汇编阶段:

  • 切换到SVC模式以获得更高的权限
  • 关闭看门狗、中断、MMU(内存映射用不到)等,以提高Uboot的稳定性
  • 基本硬件的初始化:时钟、串口、FLASH、内存
  • 拷贝至内存执行,以提高运行速度
  • 设置好C语言运行所需要的栈
  • 跳转到C语言入口

C语言阶段:

  • 进行其余外设的初始化
  • 将内核拷贝到内存,从FLASH拷贝到内核
  • 去内存中运行内核

2.5 数组指针的运算

int a [5][4], (*p)[4]=a;,数组a的首地址为100,*(p+2)+3等于 ( C)

  • A 116
  • B 118
  • C 144
  • D 122

首先,p:int (*p)[4] = a,说明p是一个数组指针,指向元素个数为4的int型数组。所以p本身代表的就是数组的地址,所以*(p+2)就相当于移动两个数据类型,也就是移动两个int [4]的地址,若*p=100*(p+2)=100+2*4*4=32,接下来再*(p+2)+3相当于再移动3个int的地址,即*(P+2)+3=100+2*4*4+3*4=144

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值