本周我学习了课本第七章和第八章的内容。
下面是我对这两章内容的总结。
首先是第七章的文件操作
7.1文件操作级别
文件操作分为五个级别,从低到高分别为:
(1)硬件级别
fdisk:将硬盘、U盘或SDC盘分区;
mkfs:格式化磁盘分区;
fsck:检查和维修系统
碎片整理:压缩文件系统中的文件
(2)操作系统内核中的文件系统函数
(3)系统调用:用户模式程序使用系统调用来访问内核函数
open()、read()、lseek()函数都是C语言库函数。每个库函数都会发出一个系统调用,使进程进入内核模式来执行相应的内核函数,例如open可进入
kopen()等。系统调用可让用户读/写多个数据块,这些数据块只是一系列
(4)I/O库函数
C语言提供了一个标准的I/O库函数,提高了运行效率
(5)用户命令
用户可以使用Unix/Linux命令来执行文件操作,而不是编写程序
(6)sh脚本
必须要手动输入命令。sh包含所有的有效Unix/Linux命令,还支持变量和控制语句,比系统调用方便得多。
7.2I/O文件操作
7.3低级别文件操作
各分区可以分为几个逻辑单元,可以格式化为特定的文件系统,称为分区。如果某分区是个扩展类型,则可以再次划分为几个扩展分区。
如下,我们在Ubuntu下创建了一个虚拟磁盘映射文件
7.3.3挂载分区
在终端输入:dd if=/dev/zero of=virtual.img bs=1M count=256
dd是将一个一个256个0字节块写入目标文件virtual.img的程序。
运行结果如下:
在磁盘映像上运行fdisk:
输入fdisk virtual.img
,结果如下图所示:
输入
sudo mkfs.ext4 virtual.img
使用mkfs命令格式化磁盘(我们这里是自己创建的虚拟磁盘镜像)
结果如下图所示:
使用mount命令挂载磁盘到目录树上
使用fdisk为磁盘分区
查看所有的loop回环设备
也可以使用losetup -a
来查看它们
当我输入losetup /dev/loop1时发现提示我设备繁忙,去云班课发现也有同学出现了类似的问题,上网搜查资料后发现,原来是命名的问题,我们只需
要一步步往后递推,寻找空闲的回环设备
解除设备关联:
取消映射
格式化/dev/loop1
作业195页练习:
sudo mkfs.ext2 /dev/sda1
(插叙:这里最好还是用sdb盘去分区,不然会出现很多意外情况)
格式化分区sda1为ext2文件系统类型
https://img2022.cnblogs.com/blog/2167156/202209/2167156-20220921224020654-2097077203.png)
////////插曲/////
由于我的不当操作(没有留意我的虚拟机里没有sdb盘,导致我把sda盘格式化成ext2系统文件格式了!!!!结果我的ubuntu打开时
就黑屏了555////////同学们引以为戒,一定要先分区,再挂载,不然会出现非常严重的后果————比如C整个盘格式化。。。
终于一番折腾我重装了ubuntu操作系统。。。继续这周的学习
终于新建了一个虚拟机~
在第七章学习超级块时,我对这块的知识非常迷,于是上网找了些比较浅显易懂和的解释:
Linux硬盘组织方式为:引导区、超级块(superblock),索引结点(inode),数据块(datablock),目录块(diredtory block).
其中超级块中包含了关于该硬盘或分区上的文件系统的整体信息,如文件系统的大小等;
超级块后面的数据结构是索引结点(inode,我们之后学到的),它包含了针对某一个具体文件的几乎全部信息,如文件的存取权限、所有者、大小、建立时间以及对应的目录块和数据块等;
数据块是真正存储文件内容的位置.
但是索引结点中不包括文件的名字,文件名是放在目录块里的.目录块里包含有文件的名字以及此文件的索引结点编号.
查看linux上的超级块信息:
先输入df查看我们的磁盘使用及挂载情况,可见我的磁盘sda盘已经分区
了,还有一个添加的虚拟磁盘sdb盘。
源自:https://blog.csdn.net/weixin_39675513/article/details/116702394
输入dumpe2fs /dev/sda5查看
发现数据量很大
第八章 使用系统调用进行文件操作
8.1系统调用
以两种不同的模式运行,即内核模式和用户模式,用户模式的权限非常有限,不能执行任何需要特殊权限的操作,有特殊权限的操作必须在Kmode下执行。
系统调用必须由程序自身发出,用法和普通函数一样
int syscall(int number,....)
syscall() 执行一个系统调用,第一个参数是系统调用编号,后面的参数是对应内核函数的参数。
系统调用可让进程从用户模式切换到内核模式,内核的系统调用处理程序根据系统调用编号number将调用路由到一个相应的内核函数。当进程结束执
行内核函数后会返回到用户模式并得到所需结果;如果失败,错误编号会记录在errno中,可以通过strerror获取错误对应的描述字符串。成功返回
=0,失败返回-1。
8.2系统调用手册页
大多数unix/linux中,在线手册页保存在/usr/man/目录中。在ubuntu linux中,则保存在/usr/share/man中。
下面列举了在终端输入man 相关命令的示意图:
使用man 2 stat命令:
使用man 2 read命令
在运行书上给定的代码时,我的输出出现了提示“段错误”的情况,经上网搜索后发现原来是函数访问的内存超过了应有的范围
运行代码,成功创建一个文件夹newdir(实现了命令行语句 mkdir newdir)
关于mkdir语句中的0766我不是很懂,上网查找资料后有了一定的了解
接下来我对代码进行了改动,使刚刚创建的newdir删除。(期间我又另创了一个newdir[i])
结果如下:已删除
8.3链接文件
Unix/Linux允许使用不同的路径名来表示同一个文件。这些文件都叫做LINK(链接)文件。有两种类型的链接,即硬链接和软连接或符号链接
①硬链接: ln oldpath newpath
对应的系统调用是 link(char *oldpath,char *newpath
)
对任何一个链接或者源文件进行修改,都会影响到其他的文件,但是删除链接文件,不会影响其他链接文件的访问。
inode信息中有一项叫做"链接数",记录指向该inode的文件名总数,如果给该inode增加一条硬链接,“链接数”会增加1。反过来,删除一个文件名,就
会使得inode节点中的"链接数"减1。当这个值减到0,表明没有文件名指向这个inode,系统就会回收这个inode号码,以及其所对应block区域。
创建硬链接:
运行硬链接:
硬链接文件会共享文件系统中相同的文件表示数据结构(索引节点)。文件链接数会记录链接到同一索引节点的硬链接数量。硬链接仅适用于非目录文件。
系统调用:
unlink(char *pathnmae)
会减少文件的链接数
如果链接数变为0,文件会被完全删除。————最好多创建几个到文件的硬链接
②符号链接文件 ln -s oldpath newpath
对应的系统调用是:symlink(char *oldpath char *newpath)
看了这个解说,我觉得软链接有点像存放指针(地址)的数组,hh。
hh3.c文件内容:
创建一个hh5.c软链接到hh3.c: 并用ls查看桌面文件内容,可以看到hh5链接到了hh3文件
软链接适用于任何文件,包括目录。
软链接可通过较短的名称访问一个较长的路径名称;还可以将标准动态库名称链接到实际版本的动态库。
(1)软链接一个缺点是目标文件可能不复存在了,用ls命令查看深色RED,看是否链接已经断开。
(2)如果hh->/a/b/c是软链接,用open("hh",0)西永调用将打开被链接的文件/a/b/c,而不是链接文件(hh)本身。所以open()/read()系统调用不能读
取软链接文件,必须要用readlink系统调用。
8.4stat系统调用
(1)inode
关于inode的相关学习内容转载自原文链接:https://blog.csdn.net/Nick_Di/article/details/118068557
在 Linux 中 inode 号(即索引节点号)才是文件的唯一标识而非文件名。文件名仅是为了方便人们的记忆和使用,系统或程序通过 inode 号寻
找正确的文件数据块。
文件存储在硬盘上,硬盘的最小存储单位叫做“扇区”(Sector)。每个扇区储存512字节(相当于0.5KB)
操作系统读取硬盘的时候,不会一个个扇区的读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个“块”(block)。**这种由多个扇
区组成的“块”,是文件存取的最小单位。“块”的大小,最常见的是4KB,即连续八个sector组成一个block**。
文件数据都储存在“块”中,那么很显然,我们还必须找到一个地方**储存文件的“元信息”,比如文件的创建者、文件的创建日期、文件的大小等等。这种
储存文件元信息的区域就叫做inode,中文译名为"索引节点"**。
每一个文件都有对应的inode,里面包含了与该文件有关的一些信息,如文件数据block的位置、blocks块数、文件内容上一次变更时间等等。可以
说,除了文件名以外的所有文件信息,都存在inode之中。
操作系统自动将硬盘分成两个区域。一个是数据区,存放文件数据;另一个是inode区(inode table),存放inode所包含的信息。
每个inode节点的大小,一般是128字节或256字节。Unix/Linux系统内部不使用文件名,而使用inode号码来识别文件。对于系统来说,文件名只是
inode号码便于识别的别称或者绰号。
文件名、inode、block三者关系如下图所示:
用stat xx.txt查看文档的状态,如下图所示:
stat的结构体:
原文链接为:https://blog.csdn.net/Nick_Di/article/details/118068557,大家有兴趣的可以直接跳转查看,我觉得讲的很清楚。
8.5opendir-readdir函数
Linux下opendir()、readdir()和closedir()这三个函数主要用来遍历目录。在使用这三个函数前必须先包括以下两个头文件:
#include <sys/types.h>
#include <dirent.h>
opendir函数的原型为:
DIR *opendir(const char name);
它返回一个DIR类型(类似于fp指针),是一个句柄,句柄要传给readdir()函数的参数即可。(传入name路径,成功则返回非空DIR
指针,否则返回NULL。)
readdir函数的原型为:
struct dirent readdir(DIR dir);
看它的参数就知道该参数是opendir函数返回的句柄,而该函数的返回值是struct dirent 类型:
struct
dirent {
ino_t d_ino; / inode number /
off_t d_off; / offset to the next dirent /
unsigned short d_reclen; / length of this record /
unsigned char d_type; / type of file /
char d_name[256]; / filename */
};
closedir函数的原型为:
int closedir(DIR *dir);
8.6 ls程序
课本上216页的代码可以让我们更加清楚地明白ls系统调用的过程
8.7 open-close-lseek系统调用
- open:打开一个文件进行读、写、追加:
int open(char *file,int flags,int mode);
会返回一个进程最小的文件描述符,用于后续的open();
write(),lseek(),close()等的系统调用。
file:文件的路径;
flags:打开的方式;有下列几种打开方式:
此外还有搭配使用的几种方式:
mode:创建权限, eg:0664;
- close:关闭打开的文件描述符:
int close(int fd);
- read:读取打开的文件描述符:
int read(int fd,char buf[],int count);
fd:文件描述符,标识要读取的文件。如果为0,则从标准输入读数据。类
似于scanf()的功能;
文件描述符:系统调用IO接口的操作句柄,为一个整数,在系统中唯一标
识文件。程序启动默认会打开的三个文件描述符:
0:标准输入
1:标准输出
2:标准错误
*buf:缓冲区,用来存储读来的数据
count:要读取文件的长度
返回值int:成功则返回实际读到的数据长度,失败-1
- write:写入打开的文件描述符:
int write(int fd,char buf[],int count);
- lseek:将文件描述符的字节偏移量重新定义为偏移量:
int lseek(int fd,int offset,int whence);
fd:打开的打开文件描述符
offset:偏移量
whence:相对起始位置
返回值:成功返回跳转后相对于文件起始处的偏移量。若跳转到文件末尾可获得文件长度。
- umask:设置文件创建掩码:文件权限为(mask&~umask)
编写简单的程序实现文件简单的打开、写入、关闭
可见显示出了i like linux!的字样
附:
关于在ubuntu里增加一个sdb盘的操作,可以在sdb盘进行一系列linux操作实验:
添加sbd盘后如下图所示:
详细操作方法见:https://blog.csdn.net/yueni_zhao/article/details/126240181
挂载分区