Unix/Linux系统编程自学笔记-第七章:文件操作-第八章:使用系统调用进行文件操作
第七章-文件操作
目录
0.概述
1.文件级别操作
2.文件I/O操作
3.低级别文件操作
0.概述
第七章主要讨论了各种文件系统;解释了操作系统中的各种操作级别,包括为文件存储准备存储设备、内核中的文件系统支持函数、系统调用、文件流上的 I/O 库函数、用户命令和各种操作的sh脚本;各种文件操作;Linux的EXT2文件系统等。
这些都是本章所讨论的内容。
1.文件级别操作
文件操作分为五个级别,按照从低到高的顺序可以如下表示:
-
1.硬盘级别
这些操作多是针对系统的实用程序,一般用户不会涉及到它们的运用,但是这些操作是创建和维护系统的关键。这些操作包括:
1.fdisk: 将硬盘、U盘或者SDC盘分区 2.mkfs: 格式化磁盘分区,为系统做好准备 3.fsck: 检查和维修系统 4.碎片整理: 压缩系统中的文件
-
2.操作系统内核中的文件系统函数
每个操作系统内核都是可以为基本文件操作提供支持的。它们有一个共同点,就是有 k 前缀:
-
3.系统调用
用户模式用来访问内核函数的工具,在C语言中是一系列文件操作相关的库函数,如
open() => kopen() read() => kread() lseek()... close()...
这些函数在第十章中有较为详实的使用解析。
内核函数传输数据是按数据块的格式进行的。
这些库函数会发出一个系统调用,使进程进入内核模式来执行相关的内核函数,以达到文件操作的目的。进程结束执行内核函数后就会回到用户模式。在内核模式下,每次读取的内容一般为 n KB的数据块,根据系统不同, n 的值会在1~8间变化 ,比如,在Linux中,硬盘默认的数据块大小为4KB,软盘为1KB。
-
4.I/O库函数
I/O库函数可以提供数据缓冲区,方便对数据按字符、行或者数据结构的形式进行读写,C语言中有许多常用的I/O库函数,比如 scanf() 等
一些基本格式:
文件模式: fopen() , fread() ; fwrite() ,fseek() ,fclose() ,fflush() 字符模式: getc() ,getchar() , ugetc() ,putc() ,putchar() 行模式: gets() ,fgets() ,puts() ,fputs() 数据结构模式: scanf() ,fscanf() ,sscanf() ,printf() ,fprintf() ,sprintf()
除了读/写内存位置的函数 sscanf()和sprintf()以外,其他的所有I/O库函数都是建立在系统调用之上的.
-
5.用户命令
在Linux系统中,会经常用到一些用户命令,如 mkdir ,ls ,mv ,cp等,除此自外还有如下用户命令
rmdir ,cd ,pwd ,link ,unlink ,rm ,cat ,chmod...
其中除了cd命令以外,其余都为一个可执行程序,这些程序通常会通过调用I/O库函数实现功能
-
6.sh脚本
使用sh编程语言编写的一种脚本程序,虽然比系统调用方便,但是要手动输入命令写脚本,还要指定设备来进行输入
2.文件I/O操作
-
文件I/O操作一般有如下过程:
3.低级别文件操作
-
1.分区
像给一块大容量硬盘分区,可以通过分区操作来将一个大存储空间分为不同的逻辑单元,各个分区可以格式化成为特定的文件文件系统,可以有效的隔离不同数据。
在Linux系统下,可以通过如下操作创建一个虚拟磁盘映象文件:
dd if=/dev/zero of=disk20191314 bs=1024 count=1440
它将一个1440各0字节块写入到了disk20191314中
![](https://img2020.cnblogs.com/blog/2525905/202110/2525905-20211010224621188-1575924021.png)
使用 fdisk 命令可以创建和维护分区表,fdisk命令的一般格式如下:
fdisk [参数1(必要)][参数2(可选)]
参数含义:
1.必要参数 -l 列出素所有分区表 -u 与 -l 搭配使用,显示分区数目 2.选择参数 -s<分区编号> 指定分区 -v 版本信息
使用如下面命令可以显示所有分区的信息:
sudo fdisk -l
-
2.格式化分区
fdisk 将一个存储设备划分为多个分区,但是刚刚划分出来的分区还需要经过格式化才可以存储文件,可以使用mkfs命令在特定的分区上建立 linux 文件系统,mkfs的一般格式如下:
mkfs [-V] [-t fstype] [fs-options] filesys [blocks]
各个参数的含义:
device : 预备检查的硬盘分区,例如:/dev/sda1 -V : 详细显示模式 -t : 给定档案系统的型式,Linux 的预设值为 ext2 -c : 在制做档案系统前,检查该partition 是否有坏轨 -l bad_blocks_file : 将有坏轨的block资料加到 bad_blocks_file 里面 block : 给定 block 的大小,默认为1kb
使用如下命令创建一个 msdos 的档案系统,同时检查是否有坏轨存在,并且将过程详细列出来 :
sudo mkfs -V -t msdos -c /mydiscs
-
3.挂载分区
losetup 命令用于设置循环设备。循环设备可把文件虚拟成区块设备,籍以模拟整个文件系统,让用户得以将其视为硬盘驱动器,光驱或软驱等设备,并挂入当作目录来使用。
losetup命令语法如下:
losetup [-d][-e <加密方式>][-o <平移数目>][循环设备代号][文件]
各个参数含义:
-d 卸除设备。 -e<加密方式> 启动加密编码。 -o<平移数目> 设置数据平移的数目。
使用
man 8 losetup
指令可以查看用于系统管理的 losetup 实用工具命令EXT2文件系统简介
-
ext2是Linux系统所默认的文件系统
-
它有如下的磁盘块:
BLOCK#0: 引导块 用于容纳从磁盘引导操作系统的引导程序,不会被文件操作系统使用
BLOCK#1: 超级块 在硬盘分区中字节偏移量为1024,超级块容纳了关于整个文件系统的信息
超级块有一些重要字段,这些字段表明了超级块的各段含义:
BLOCK#2: 块组表述符块 ext2将磁盘块分成若干组,每组8192块(约32K),每组都有一个块组描述符结构体
BLOCK#8: 块位图 表示某种项的位序列
BLOCK#9: 索引节点位图 代表一个文件的数据结构
BLOCK#10: 索引节点 大小为128字节的索引结构体,表示一个文件
-
目录条目 包含了dir_entry 结构:
-
-
第八章-使用系统调用进行文件操作
目录
0.概述
本章概述了如何使用系统调用进行文件操作,而这些操作基本在第十章中都有体现
1.系统调用
- 操作系统中进程以内核模式和用户模式运行,系统调用可以暂时让处于用户模式的进程拥有内核模式的高权限,以便拥有必要的操作权限。
2.系统调用手册
-
可以使用
man 2 stat
等命令来查看操作说明:man 2 stat: 查看stat() ,fstat() ,lstat()函数的说明 man 2 open: 查看open()函数的说明 man 2 read: 查看read()函数的说明
3.使用系统调用进行文件操作
系统调用必须由程序发出,其最终用法像普通函数一样。每个系统调用都是一个库函数,它汇集系统调用参数,并最终向操作系统内核发出一个系统调用,可实现用户模式进程向内核模式的转变
下面是实现系统调用的一般函数格式:
int syscall(int a, int b, int c, int d);
各个参数的含义:
a: 系统调用编号
b: 对应内核函数的参数
c: 对应内核函数的参数
d: 对应内核函数的参数
当进程结束执行内核函数时,会返回到用户模式,并得到所需的结果。返回值≥0表示成功,-1表示失败。如果失败,errno变量(在errno.h中)会记录错误编号,它们会被映射到描述错误原因的字符串。
4.系统调用指令
- 简单的系统调用
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 *name,int uid,int gid),
chroot:将(逻辑)根目录更改为路径名
int chroot(char *pathname):
getewd:获取CWD的绝对路径名
char *getcwd(char *buf, int aize):
mkdir:创建目录
int mkdir(char *pathname,mode_t mode);
rmdir:移除目录(必须为空)
int rmdir(char *pathname);
link:将新文件名硬链接到旧文件名
int 1ink(char *o1dpath,char *newpath);
umlink:减少文件的链接数;如果链接数达到0,则删除文件
int unlink(char *pathname);
symlink:为文件创建一个符号链接
int symlink(char o1dpath, charnewpath);
rename:更改文件名称
int rename (char *oldpath, char *newpath);
utime:更改文件的访问和修改时间
int utime(char *pathname, struct utimebuf *time) 以下系统调用需要超级用户权限。
mount:将文件系统添加到挂载点目录上
int mount(char *specialfile, char *mountDir);
umount:分离挂载的文件系统
int umount(char *dix);
mknod:创建特殊文件
int mknod(char *path,int mode, int device);
- 常用的系统调用
stat 获取文件状态信息
int stat(char *filename ,struct stat *buf)
int fstat(char 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[ 1, int count)
write:写入打开的文件描述符
int write(int fd, char buf[ ], int count)
lseek:重新定位文件描述符的读/写偏移量
int 1seek(int fd, int offset, int whence)
dup:将文件描述符复制到可用的最小描述符编号中
int dup(int oldfd);
dup2:将oldfd复制到newfd中,如果newfd已打开,先将其关闭
int dup2(int oldfd, int newfd)
link:将新文件硬链接到旧文件
int link(char *oldPath, 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.打印文件实践
对于一个文件,有时需要打印它出来,而这个程序就可以实现这一功能:
/*---20191314---*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#define BLKSIZE 4096
int main(int argc , char *argv[])
{
int fd , i , m , n;
char buf[BLKSIZE] , dummy;
fd = 0;
if (argc > 1){
fd = open(argv[1] , O_RDONLY);
if (fd < 0) exit(1);
}
while (n = read(fd , buf , BLKSIZE)){
m = write(1 , buf , n);
}
}
它可以将文件内容输出,当没有指定文件时,它就会输出输入缓冲区里内容:
./a.out :输出输入缓冲区内容
./a.out finame :输出文件内容