Linux系统编程第七、八章学习笔记
——文件操作&使用系统调用进行文件操作
一、文件操作类别
1、硬件类别
- disk:将硬盘、U盘或SDC盘分区。
- mkfs:格式化磁盘分区,为系统做好准备。
- fsck:检查和维修系统。
2、 操作系统内核中的文件系统函数:每个操作系统内核均可为基本文件操作提供支持。
操作系统内核中的文件系统函数:
kmount(), kumount() (装载/卸载文件系统)
kmkdir(), krmdir() (生成/删除目录)
kchdir(), kgetcwd () (更改目录,获取CWD路径名)
klink(), kunlink() (链接/取消链接文件)
kchmod(), kchown(),kutime() (更改r | w | x权限、使用者、时间)
kcreat(), kopen() (为R、W、RW、APPEND创建/打开文件)
kread(), kwrite() (读写打开的文件)
klseek(), kclose() (lseak/close文件描述符)
ksymlink(), kreadlink() (创建/读取符号链接文件)
kstat(), kfstat(), klstat() (获取文件状态/信息)
kopendir(), kreaddiz() (打开/读取 airectories)
3、 系统调用:用户模式程序使用系统调用来访问内核函数。
4、I/O库函数:系统调用可让用户读、写多个数据块,这些数据块只是一系列字节。
文件模式I/O: fopen(),fread();fwrite(),fseek(),fclose(),fflush()
字符模式I/O: getc(), getchar(); ugetc(); putc(),putchar()
行模式I/O: gets() , fgets();puts( ) , fputs()
格式化I/O: scanf(),fscanf().sscanf(); printf(),fprintf() , sprintf()
5、用户命令:用户可以使用Unix/Linux命令来执行文件操作,而不是编写程序。
6、sh脚本:虽然比系统调用方便得多,但是必须要手动输入命令,如果使用的是GUI,必须要拖放文件图标和点击指向设备来输入,操作繁琐而且耗时。
二、文件I/O操作
双线上方的上半部分表示内核空间,下半部分表示进程的用户空间。该图显示了进程读/写文件流时的操作序列。控制流用标签(1)到(10)标识,说明如下。
下面的步骤(1)——(4)是用户模式下的操作,步骤(5)——(10)是内核模式下的操作。
(1) 用户模式下的程序执行操作可以打开一个读/写文件流。
(2) fopen()在用户(heap)空间中创建一个FILE结构体,包含一个文件描述符fd、一个fbuf[BLKSIZE]和一些控制变量。
(3) fread(ubuf,size,nitem,fp):将nitem个size字节读取到ubuf上,通过:
-
将数据从FILE结构体的fbuf上复制到ubuf上,通过:
-
如果fbuf没有更多数据,则执行(4a)。
(4) 文件中的文件系统函数:
假设非特殊文件的read(fd,fbuf[],BLKSIZE)系统调用。
(5) 在read()的系统调用中,fd是一个打开的文件描述符,它是运行进程的fd数组中的一个索引,指向一个表示打开文件的OpenTable.
(6) OpenTable包含文件的打开模式、一个指向内存中文件INODE的指针和读/写文件的当前字节偏移量。从OpenTable的偏移量,
-
计算逻辑块编号lbk。
-
通过INODE.i_block[]数组将逻辑块编号转换为物理块编号blk。
(7) Minode包含文件的内存INDOE
(8) 为提高磁盘I/O效率,操作系统内核通常会使用一组I/O缓冲区作为高速缓存,以减少物理I/O的数量。
三、低级别文件操作
1、分区
一个块存储设备,如硬盘、U盘、SD卡等,可以分为几个逻辑单元,称为分区。各分区均可以格式化为特定的文件系统,也可以安装在不同的操作系统上。大多数引导程序,如GRUB、LILO等,都可以配置为从不同的分区引导不同的操作系统。分区表位于第一个扇区的字节偏移446(0xlBE)处,该扇区称为设备的主引导记录。
每个拓展分区的第一个扇区是一个本地MBR。每个本地MBR在字节偏移量0X1BE处也有一个分区表,只包含两个条目。第一个条目定义了拓展分区的起始扇区和大小。第二个条目指向下一个本地MBR。所有本地MBR的扇区编号都与P4的起始扇区有关。
2、格式化分区
fdisk只是将一个存储设备划分为多个分区.每个分区都有特定的文件系统类型,但是分区还不能使用。为了存储文件,必须先为特定的文件系统准备好分区。该操作系统习惯上称为格式化磁盘或磁盘分区。
3、挂载分区
Man 8 losetup:显示用于系统管理的losetup实用工具命令:
(1) 用dd命令创建一个虚拟磁盘映像
(2) 在vdisk上运行fdisk来创建一个分区P1
(3) 使用以下扇区数在vdisk的分区1上创建一个循环设备
(4) 格式化/dev/loop1,它是一个EXT2文件系统
(5) 挂载循环设备
(6) 访问作为文件系统一部分的挂载设备
(7) 设备使用完毕后,将其卸载
(8) 循环设备使用完毕后,通过以下命令将其断开
四、系统调用
1、access:检查对某个文件的权限
-chdir:更改目录
-chmod:更改某个文件的权限
-chown:更改文件所有人
-chroot:将(逻辑)根目录更改为路径名
-getcwd:获取CWD的绝对路径名
-mkdir:创建目录
-rmdir:移除目录(必须为空)
-link:将新文件名硬链接到旧文件名
-unlink:减少文件的链接数;如果链接数达到0,则删除文件
-symlink:为文件创建一个符号链接
-rename:更改文件名称
-utime:更改文件的访问和修改时间
2、以下系统调用需要超级用户权限
mount:将文件系统添加到挂载点目录上
umount:分离挂载的文件系统
mknod:创建特殊文件
stat:获取文件状态信息
open:打开一个文件进行读、写、追加
close:关闭打开的文件描述符
read:读取打开的文件描述符
write:写入打开的文件描述符
link:将新文件硬链接到旧文件
unlink:取消某个文件的链接;如果链接文件链接数为0,则删除文件
readlink:读取符号链接文件的内容;
symlink:创建一个符号链接
五、链接文件
1、硬链接文件
硬链接文件会共享文件系统中相同的文件表示数据结构(索引节点)。文件链接数会记录链接到同一索引节点的硬链接数量。硬链接仅适用于非目录文件。否则,它可能会在文件系统名称空间中创建循环,这是不允许的。相反,系统调用: unlink(char *pathname)会减少文件的链接数。如果链接数变为0,文件会被完全删除。这就是rm(file)命令的作用。如果某个文件包含非常重要的信息,就最好创建多个链接到文件的硬链接,以防被意外删除。
2、软链接
创建从newpath到oldpath的软链接或符号链接。对应的系统调用是:
symlink(char *oldpath, char *newpath)
newpath是LNK类型的普通文件,包含oldpath字符串。它可作为一个绕行标志,使访问指向链接好的目标文件。与硬链接不同,软链接适用于任何文件,包括目录。
六、stat系统调用
#include <sys/ types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat (const char *file_name,struct stat *buf);
int fstat (int filedes,struct stat *buf) ;
int lstat(const char *file_name,struct stat *buf) ;
- stat文件状态
- stat结构体
- stat与文件索引节点
- 文件类型和权限
- opendir-readdir函数
- readlink函数
- ls程序
- open-close-lseek系统调用
- 打开文件和文件描述符
- 关闭文件描述符
- lseek文件描述符
- write()系统调用
实践
对磁盘分区的实验
image
点击查看代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
struct stat mystat, *sp;
char *t1 = "xwrxwrxwr------";
char *t2 = "---------------";
int ls_file(char *fname)
{
struct stat fstat, *sp;
int r, i;
char ftime[64];
sp = &fstat;
if ( (r = lstat(fname, &fstat)) < 0)
{
printf("can* t stat %s\n", fname);
exit(1);
}
if ((sp->st_mode & 0xF000)==0x8000) // if (S_ISREG())
printf("%c",'-');
if ((sp->st_mode & 0xF000)==0x4000) // if (S_ISDIR())
printf("%c",'d');
if ((sp->st_mode & 0xF000)==0xA000) // if (S_ISLNK())
printf ("%c",'l');
for (i=8; i >= 0; i--)
{
if (sp->st_mode & (1 << i)) // print r|w|x
printf("%c",t1[i]);
else
printf("%c",t2[i]); // or print -
}
printf("%4d ",sp->st_nlink); // link count
printf("%4d ",sp->st_gid); // gid
printf("%4d ",sp->st_uid); // uid
printf("%8d ",sp->st_size); // file size
// print time
strcpy(ftime, ctime(&sp->st_ctime)); // print time in calendar form
ftime[strlen(ftime) -1] = 0; // kill \n at end
printf("%s ",ftime);
// print name
printf("%s", basename(fname)); // print file basename
// print -> linkname if symbolic file
if ((sp->st_mode & 0xF000)== 0xA000)
{
// use readlink() to read linkname
printf(" -> %s", linkname); // print linked name
}
printf("\n");
}
int ls_dir(char *dname)
{
// use opendir(), readdir(); then call ls_file(name)
}
int main(int argc, char *argv[])
{
struct stat mystat, *sp = &mystat;
int r;
char *filename, path[1024], cwd[256];
filename = "./"; // default to CWD
if (argc > 1)
filename = argv[1]; // if specified a filename
if (r = Istat(filename, sp) < 0)
{
printf("no such file %s\n", filename);
exit(1);
}
strcpy(path, filename);
if (path[0] != '/')
{
getcwd(cwd, 256);
strcpy(path, cwd); strcat(path, " /") ; strcat(path,filename);
}
if (S_ISDIR(sp->st_mode))
ls_dir(path);
else
ls_file(path);
}