Linux系统编程第七八章学习笔记

前言

此文章意在于介绍多种文件系统,操作系统中的不同操作级别,包括为文件存储准备存储设备、内核中的文件系统支持函数、系统调用、文件流上的I/O库函数、用户命令和各种操作的sh脚本。系统性概述了从用户空间的文件流读/写到内核空间的系统调用直到底层的设备I/O驱动程序级别,包括低级别的文件操作,如磁盘分区、显示分区表的示例程序、文件系统的格式化分区以及挂载磁盘分区。介绍了Linux系统的EXT2文件系统,包括系统数据结构、超级块、组描述符、块和索引节点位图以及目录内容的示例程序。此外,还讲解了如何使用系统调用进行文件操作,系统调用的作用和Linux的在线手册页,文件操作中最常用的系统调用,硬链接和符号链接文件,stat系统调用,文件描述符和读写系统调用的使用。
5

第七章

1.文件操作级别

文件操作分为五个级别,按照从低到高的顺序排列如下。
(1)硬件级别:硬件级别的文件操作包括:

  • fdisk:将硬盘、U盘或SDC盘分区。
  • mkfs:格式化磁盘分区,为系统做好准备。
  • fsck:检查和维修系统。
  • 碎片整理:压缩文件系统中的文件。
    其中大多数是针对系统的实用程序。普通用户可能永远都不需要它们,但是它们是创建和维护系统不可缺少的工具。

(2)操作系统内核中的文件系统函数:每个操作系统内核均可为基本文件操作提供支
持。下文列出了类Unix系统内核中的一些函数,其中前缀k表示内核函数。

kmount (),kumount ()//(mount /umount file systems )
kmkdir(),krmdir ()//(make/remove directory)
kchdir(), kgetcwd()//(change directory, get CWD pathname)
klink(), kunlink()//(hard link/unlink files)
kchmod(), kchown(), kutime() //(change r |w|x permissions; owner, time)
kcreat (),kopen()//(create/open file for R, W, RW, APPEND)
kread(), kwrite ()//(read/write opened files )
klseek(); kclose()//(1seek/c1ose file descriptors )
ksymlink(),kreadlink( )//(create/read symbolic link files)
kstat(),kfstat(), klstat() //(get file status /information)
kopendir(),kreaddir( )//(open/read airectories)

(3)系统调用:用户模式程序使用系统调用来访问内核函数。例如,下面的程序可读取文件的第二个1024字节。

#include <fcnt1 .h>
int main(int argc,char *argv[ ]}// run as a.out fil ename
(
int fd, n;
char buf [1024] ;
if ((fd = open (argv[1],O_RDONLY)) < 0) // if open() fails
    exit(1) ;
1seek(fd,1024, SEEK_ SET) ;// Iseek to byte 1024
n = read(fd,buf, 1024);// try .to read 1024 bytes
close(fd) ;

open()、read()、 lseek() 和close()函数都是C语言库函数。每个库函数都会发出一个系统调用,使进程进入人内核模式来执行相应的内核函数,例如open可进入kopen(),read可进人kread()函数,等等。当进程结束执行内核函数时,会返回到用户模式,并得到所需的结果。在用户模式和内核模式之间切换需要大量的操作(和时间)。因此,内核和用户空间之间的数据传输成本昂贵。虽然可以发出read(fd, buf, 1)系统调用来只读取一个字节的数据,但是这种做法是不明智的,因为一个字节也会带来可怕的高成本。我们在每次必须进入内核时,都要尽可能不虚此行。对于读1写文件,最好的方法是匹配内核的功能。内核会按数据块大小(从1 KB到8KB)来读取/写入文件。例如,在Linux中,硬盘的默认数据块大小是4KB,软盘的是1KB。因此,每个读/写系统调用还要尝试一次传输一个数据块。
(4) I/0 库函数:系统调用可让用户读/写多个数据块,这些数据块只是一系列字节。它们不知道,也不关心数据的意义。用户通常需要读/写单独的字符、行或数据结构记录等。如果只有系统调用,用户模式程序则必须自己从缓冲区执行这些操作。大多数用户会认为这非常不方便。为此,C语言库提供了一系列标准的I/O函数,同时也提高了运行效率。I/O库函数包括:

FILE mode I/O: fopen(), fread(); Ewrite() ,fseek(), fclose(), fflush()
char mode I/O: getc(), getchar() ugetc(); putc() ,putchar()
1ine mode I/O: gets(), fgets(); puts(), fputs()
formatted I/0: scanf(), fscanf(),sscanf(); printf(), fprintf (), sprintf()

(5)用户命令:用户可以使用Unix/Linux命令来执行文件操作,而不是编写程序。用户命令的示例如下:

mlcdir, rmdir, cd,pwd, 18,1ink, un1ink, 工m,cat, Cp, mv, chmod, etc 

(6)sh脚本:虽然比系统调用方便得多,但是必须要手动输人命令,如果使用的是GUI,必须要拖放文件图标和点击指向设备来输人,操作烦琐而且耗时。sh脚本是用sh编程语言编写的程序,可通过命令解释程序sh来执行。sh语言包含所有的有效Unix/Linux命令。它还支持变量和控制语句,如if、do、 for、while、 case等。实际上,sh 脚本广泛用于Unix/Linux系统编程。除sh之外,Perl 和Tel等其他许多脚本语言也使用广泛。

2.文件I/O操作

1
建议看该文章以学习详情Linux系统编程——系统调用之 I/O 操作(文件操作)

3.低级别文件操作

1.分区

一个块存储设备, 如硬盘、U盘、SD卡等,可以分为几个逻辑单元, 称为分区。各分区均可以格式化为特定的文件系统,也可以安装在不同的操作系统上。大多数引导程序,如GRUB、LILO等,都可以配置为从不同的分区引导不同的操作系统。分区表位于第一个扇区的字节偏移446 ( OxIBE)处,该扇区称为设备的主引导记录(MBR)。表有4个条目,每个条目由一个16字节的分区结构体定义,即:

stuct partition {
u8 drive;// 0x80 - active
u8 head;// starting head
u8 sector;// starting sector
u8 cylinder:// starting. cylinder.
u8 sys_ type;// partition type
u8 end_ head;// end head
u8 end_ sector ;// end. sector
u8 end_ _cylinder; // end cylinder
u32 start_ sector; // startingsector counting from 0
u32 nr_ sectors ; // number of sectors in partition
};

实践指令如下:

dd if=/dev/zero of=disk20191314 bs=1024 count=1440//创建一个名为mydisk的虛拟磁盘映像文件。
fdisk mydisk//运行fdisk

2
详细指令使用help得到
3

2.格式化分区&&3.挂载分区

详情请看Linux下分区的创建、格式化以及挂载

第八章

1.系统调用

在操作系统中,进程以两种不同的模式运行,即内核模式和用户模式,简称Kmode和Umode。在Umode中,进程的权限非常有限。它不能执行任何需要特殊权限的操作。特殊权限的操作必须在Kmode下执行。系统调用(简称sysall)是一-种允许进程进人Kmode以执行Umode不允许操作的机制。复刻子进程.修改执行映像,甚至是终止等操作都必须在内核中执行。

2.系统调用手册页

在Unix以及大多数版本的Linux中,在线手册页保存在/usr/man/目录中(Goldt等1995 ; Kerrisk 2010, 2017 )。而在Ubuntu Linux中,则保存在/usr/share/man目录中。man2子目录中列出了所有系统调用手册页。sh 命令man 2 NAME显示了系统调用名称的手册页。
例如:

man 2 stat: display man pages of stat(), fstat() and 1stat() syscalls
man 2 open: display man pages of open() syscal1
man 2 read: display man pages of read() sysca11, etc.

许多系统调用需要特别包含头文件,手册页的SYNOPSIS (概要)部分列出了这些文件。如果没有合适的头文件,C编译器可能会因为syscall函数名称类型不匹配而发出许多警告。一些系统调用可能还需要特定的数据结构作为参数,必须在手册页中描述这些参数。

3.使用系统调用进行文件操作

系统调用必须由程序发出。它们的用法就像普通函数调用- -样。每个系统调用都是一个库函数,它汇集系统调用参数,并最终向操作系统内核发出一个系统调用。
简单指令如下:

access:检查对某个文件的权限
int access (char *pathname, int mode) ;
chdir:更改目录
int chdir(const char *path);
chmod:更改某个文件的权限
int chmod(char *path, mode_ .t mode);
chown:更改文件所有人
int chown(char *nane, int uid, int g14); ,
chroot:将(逻辑)根目录更改为路径名
int chroot (char pathnome);
getewd:获取CWD的绝对路径名
char *getewd(char *buf, int size);
mkdir:创建目录
int mkdir (char tpathname, mode. t mode) ; 
rmdir:移除目录(必须为空)
int mdir (ohar *pathname); ,
link:将新文件名硬链接到旧文件名
int link(char *o14path, char *newpath);
unlink:减少文件的链接数;如果链接数达到0,则删除文件
int unlink (char *pathnane) ;
symlink:为文件创建一个符号链接
int syplink(char *o1dpath, char *nerpath);
rename:更改文件名称
int rename(char *o1dpath, char *newpath);
utime:更改文件的访问和修改时间
int utime(char *pathname, struct utimebuf time)
以下系统调用需要超级用户权限。
mount:将文件系统添加到挂载点目录上
int mount (char specialfile, char *mountDir);
umount:分离挂载的文件系统
int- umount(char *dir)p
mknod:创建特殊文件
int mkned(char tpath, int mode, int device);

4.常用的系统调用

如下:

stat:获取文件状态信息
int stat (char * filename, struct stat *buf )
int fstat (int filedes, struct stat *buf)
int lstat (char *filename, struct stat *buf)

open:打开一个文件进行读、写、追加
int open(char *file, int flags, int mode )

close:关闭打开的文件描述符
int close(int fd)

read:读取打开的文件描述符
int read(int fd, char buf[ ],int count )

write:写入打开的文件描述符
int write(int fa, char buf[ ],int count)

Iseek:重新定位文件描述符的读/写偏移量
int lseek(int fd, int offset, int whence)

dup:将文件描述符复制到可用的最小描述符编号中
int dup(int oldfd) ;

dup2:将oldfd复制到newfd中,如果newfd已打开,先将其关闭:
int dup2 (int olafd, int newfa)

link:将新文件硬链接到旧文件
int link (char *o1dPath, char *newPath)

unlink:取消某个文件的链接;如果文件链接数为0,则删除文件
int unlink (char *pathname) ;
symlink:创建一个符号链接
int symlink (char *target, char *newpath)

readlink:读取符号链接文件的内容
int readlink(char *path, char *buf, int bufsize)

umask:设置文件创建掩码;文件权限为(mask & ~umask)
int umask(int umask) ;


5.链接文件

在Unix/Linux中,每个文件都有-一个路径名。但是,Unix/Linux 允许使用不同的路径名来表示同一个文件。这些文件叫作LINK (链接)文件。有两种类型的链接,即硬链接和.软链接或符号链接。

1.硬链接文件&&2.符号链接文件

详情看大佬的Linux 文件系统 & 软/硬链接文件

6.stat系统调用

输入man 2 stat可获得关于stat的帮助,详情请观看man page
4

  • opendir&&readdir
    使用man 3 opendirman 3 readdir得到

7.open-close-lseek系统调用

open:打开一个文件进行读、写、追加
int open(char *file, int flags, int mode) ;

close:关闭打开的文件描述符
int close (int fd) ;

read:读取打开的文件描述符
int read(int fd, char buf[ ],int count) ;

write:写人打开的文件描述符
int write(int fd, char buf[ ],int count);

Iseek:将文件描述符的字节偏移量重新定位为偏移量
int 1seek(int fa, int offset,int whence) ;
umask:设置文件创建掩码;文件权限为(mask & ~umask)

8.read()系统调用

read)将n个字节从打开的文件描述符读入用户空间中的buf[]。返回值是实际读取的字节数,如果read()失败,会返回-1,例如当fd无效时。注意,buf] 区必须有足够的空间来接收n个字节,并且返回值可能小于n个字节,例如文件小于n个字节,或者文件无更多需要读取的数据。还要注意,返回值是一个整数,而不是文件结束(EOF)符,因为文件中没有文件结束符。文件结束符是I/O库函数在文件流无更多数据时返回的一个特殊整数值(-1)。

9.write()系统调用

write()将n个字节从用户空间中的buf[ ]写人文件描述符,必须打开该文件描述符进行写、读写或追加。返回值是实际写入的字节数,通常等于n个字节,如果write()失败,则为-1,例如由于出现无效的fd或打开fd用于只读等。

10.文件操作示例程序

系统调用适用于大数据块上的文件I/O操作,即不需要行、字符或结构化记录等的操作。
事例如下:在linux环境下实现文件的读写操作

苏格拉底挑战

文件操作知识问题

posted @ 2023-09-30 16:36  20211120  阅读(10)  评论(0编辑  收藏  举报