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.系统调用
  1. 操作系统中进程以内核模式和用户模式运行,系统调用可以暂时让处于用户模式的进程拥有内核模式的高权限,以便拥有必要的操作权限。
2.系统调用手册
  1. 可以使用 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.系统调用指令
  1. 简单的系统调用
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);
  1. 常用的系统调用
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 :输出文件内容

posted @ 2021-10-10 22:28  20191314汇仁  阅读(168)  评论(0编辑  收藏  举报