学习笔记4

苏格拉底挑战


第七章 文件操作

一.知识点归纳

(一)文件操作级别

文件操作分为五个级别,按照从低到高的顺序排列如下:

1.硬件级别:

硬件级别的文件操作包括:

  • fdisk:将硬盘、U盘或SDC盘分区。
  • mkfs:格式化磁盘分区,为系统做好准备。
  • fsck:检查和维修系统。
  • 碎片整理:压缩文件系统中的文件。

2.操作系统内核中的文件系统函数

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

kmount(), kumount()mount/umount file systemskmkdir(), krmdir()make/remove directorykchdir(), kgetcwd()change directory, get CWD pathnameklink(),  kunlink()hard link/unlink fileskchmod(), kchown(), kutime()change r|w|x permissions,owner,timekcreat(), kopen()create/open file for R,W,RW,APPEND)
kread(),  kwrite()read/write opened filesklseek(), kclose()lseek/close file descriptorsksymlink(), kreadlink()create/read symbolic link fileskstat(),  kfstat(), klstat()get file status/informationkopendir(), kreaddir()open/read directories

3.系统调用

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

#include<fcntl.h>
int main(int argc, char *agrv[])
{
	int fd, n;
	char buf[1024];
	if((fd = open(argv[1], O_RDONLY))<0)
		exit(1);
	lseek(fd, 1024, SEEK_SET);
	n = read(fd, buf, 1024);
	close(fd);
}

4.I/O库函数

I/O库函数包括:

FILE mode I/0: fopen(),fread(); fwrite(),fseek(),fclose(),fflush()
char mode I/0: getc(), getchar(),ugetc(); putc(),putchar()
line mode I/0: gets(), fgets(); puts(), fputs()
formatted I/0: acanf(),fscanf(),sscanf(); printf(),fprintf(),sprintf()

5.用户命令

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

mkdir, rmdir, cd, pwd, ls, link, unlink, rm, cat, cp, mv, chmod, etc.

每个用户命令实际上是一个可执行程序(cd除外),通常会调用库I/O函数,而库I/O函数再发出系统调用来调用相应的内核函数。用户命令的处理顺序为:

Command => Library I/O function => System call => Kernel Function
或
Command =========================> System call => Kernel Function

(二)文件I/O操作

(三)低级别文件操作

1.分区

在 Linux 下,创建一个名为 mydisk 的虚拟磁盘映像文件。

dd if=/dev/zero of=mydisk bs=1024 count=1440

dd 是将一个1440(1KB)个0字节块写入目标文件 mydisk 的程序。我们选择 count=1440,因为它是旧软盘的 1KB 字节块的数量。必要时,可指定更大的库编号。

2.格式化分区

格式:

mkfs -t TYPE [-b bsize] device nblocks

假设是 EXT2/3 文件系统,它是 Linux 的默认文件系统。因此,

mkfs -t ext2 vdisk 1440  或  mke2fs vdisk 1440
sudo mount -o loop vdisk /mnt
sudo umount /mnt  或  sudo umount vdisk

3.挂载分区

#(1)用dd命令创建一个虚拟磁盘映像
dd if=/dev/zero of=mydisk bs=1024 count=32768
#(2)在vdisk上运行fdisk来创建一个分区P1
fdisk vdisk
#(3)使用以下扇区数在vdisk的分区1上创建一个循环设备
losetup -o $(expr 2048 \* 512) --sizelimit $(expr 65535 \* 512) /dev/loop1
vdisk
losetup -a
#(4)格式化/dev/loop1,它是一个EXT2文件系统
mke2fs -b 4096 /dev/loop1 7936
#(5)挂载循环设备
mount /dev/loop1 /mnt
#(6)访问作为文件系统一部分的挂载系统
(cd /mnt; mkdir bin boot dev etc user)
#(7)设备使用完毕后,将其卸载
umount /mnt
#(8)循环设备使用完毕后,通过以下命令将其断开
losetup -d /dev/loop1

(四)EXT2文件系统简介

在 Linux 下,我们可以创建一个包含简单 EXT2 文件系统的虚拟磁盘,如下文所示:

dd if=/dev/zero of=mydisk bs=1024 count=1440
mke2fs -b 1024 mydisk 1440

image
image

  • Block#0:引导块
    • B0是引导块(Boot Block),文件系统不会使用它。它用于容纳从磁盘引导操作系统的引导程序。
  • Block#1:超级块
    • 超级块(Super Block)描述整个分区的文件系统信息,如inode/block的大小、总量、使用量、剩余量,以及文件系统的格式与相关信息。超级块在每个块组的开头都有一份拷贝(第一个块组必须有,后面的块组可以没有)。
    • 超级块记录的信息有:
      1. block 与 inode 的总量(分区内所有Block Group的block和inode总量);
      2. 未使用与已使用的 inode / block 数量;
      3. block 与 inode 的大小 (block 为 1, 2, 4K,inode 为 128 bytes);
      4. filesystem 的挂载时间、最近一次写入数据的时间、最近一次检验磁盘 (fsck) 的时间等文件系统的相关信息;
      5. 一个 valid bit 数值,若此文件系统已被挂载,则 valid bit 为 0 ,若未被挂载,则 valid bit 为 1 。
  • Block#2:块组描述符块
    • 块组描述符块(GD)由很多块组描述符组成,整个分区分成多个块组就对应有多少个块组描述符。
    • 每个块组描述符存储一个块组的描述信息,如在这个块组中从哪里开始是inode Table,从哪里开始是Data Blocks,空闲的inode和数据块还有多少个等等。块组描述符在每个块组的开头都有一份拷贝。
  • Block#8:块位图
    • 块位图(Block Bitmap)用来描述整个块组中哪些块已用哪些块空闲。块位图本身占一个块,其中的每个bit代表本块组的一个block,这个bit为1代表该块已用,为0表示空闲可用。假设格式化时block大小为1KB,这样大小的一个块位图就可以表示1024*8个块的占用情况,因此一个块组最多可以有10248个块。
  • Block#9:索引节点位图
    • inode位图(inode Bitmap)和块位图类似,本身占一个块,其中每个bit表示一个inode是否空闲可用。 Inode bitmap的作用是记录block group中Inode区域的使用情况,Ext文件系统中一个block group中可以有16384个Inode,代表着这个Ext文件系统中一个block group最多可以描述16384个文件。
  • Block#10:索引(开始)节点块
    • inode表(inode Table)由一个块组中的所有inode组成。一个文件除了数据需要存储之外,一些描述信息也需要存储,如文件类型,权限,文件大小,创建、修改、访问时间等,这些信息存在inode中而不是数据块中。inode表占多少个块在格式化时就要写入块组描述符中。 在Ext2/Ext3文件系统中,每个文件在磁盘上的位置都由文件系统block group中的一个Inode指针进行索引,Inode将会把具体的位置指向一些真正记录文件数据的block块,需要注意的是这些block可能和Inode同属于一个block group也可能分属于不同的block group。我们把文件系统上这些真实记录文件数据的block称为Data blocks。

第八章 使用系统调用进行文件操作

一.知识点归纳

(一)系统调用

在操作系统中,进程以两种不同的模式运行,即内核模式和用户模式,简称 Kmode 和 Umode。在 Umode 中,进程的权限非常有限。它不能执行任何需要特殊权限的操作。特殊权限的操作必须在 Kmode 下执行。系统调用(简称 syscall)是一种允许进程进入 Kmode 以执行 Umode 不允许操作的机制。

(二)简单的系统调用

  • 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);
  • getcwd:获取 CWD 的绝对路径名
char *getcwd(char *buf, int size);
  • mkdir:创建目录
int mkdir(char *pathname, mode_t mode);
  • rmdir:移除目录(必须为空)
int rmdir(char *pathname);
  • link:将新文件硬链接到旧文件
int link(char *oldPath, int *newPath);
  • unlink:取消某个文件的链接;如果文件的链接数为0,则删除文件
int unlink(char *pathname);
  • symlink:创建一个符号链接
int symlink(char *target, char *newpath);
  • rename:更改文件名称
int rename(char *oldPath, int *newPath);
  • utime:更改文件的访问和修改时间
int utime(char *pathname, struct utimebuf *time);

以下系统调用需要超级用户权限。

  • mount:将文件系统添加到挂载点目录上
int mount(char *specialfile, char *mountDir);
  • umount:分离挂载的文件系统
int umount(char *dir);
  • 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 read(int fd);
  • read:读取打开的文件描述符
int read(int fd, char buf[], int count);
  • write:写入打开的文件描述符
int write(int fd, char buf[], int count);
  • lseek:重新定位文件描述符的读/写偏移量
int lseek(int fd, char offset, int whence);
  • dup:将文件描述符复制到可用的最小描述符编号中
int dup(int oldfd);
  • dup2:将 oldfd 复制到 newfd 中,如果 newfd 已打开,先将其关闭
int dup2(int oldfd, int newfd);
  • link:将新文件硬链接到旧文件
int link(char *oldPath, int *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);

(四)链接文件

1.硬链接文件

硬链接:命令

ln oldpath newpath

创建从 newpath 到 oldpath 的硬链接,对应的系统调用为:

link(char *oldPath, int *newPath)

2.符号链接文件

软连接:命令

ln -s oldpath newpath	# ln command with the -s flag

创建从 newpath 到 oldpath 的软链接或符号链接,对应的系统调用为:

symlink(char *target, char *newpath)

二.问题与解决思路

三.实践内容与截图

(一)mkdir、chdir、getcwd 系统调用

#include<stdio.h>
#include<errno.h>
int main()
{
  char buf[256],*s;
  int r;
  r=mkdir("newdir",0766);//mkdir syscall
  if(r<0)
    printf("errno=%d : %s\n",errno,strerror(errno));
  r=chdir("newdir");//cd into newdir
  s=getcwd(buf,256);//get CWD string into buf[]
  printf("CWD = %s\n",s);
}

image

(二)ls程序

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <string.h>

int main(int argc,char * argv[]){
    if(argc<2){
        printf("%s need filename\n",argv[0]);
        return -1;
    }
    struct stat st;
    int ret=stat(argv[1],&st);
    if(ret==-1){
        perror("stat");
        return -1;
    }
    char perms[11]={0}; 

    switch (st.st_mode & S_IFMT){
        case S_IFLNK:
            perms[0]='l';
            break;
        case S_IFDIR:
            perms[0]='d';
            break;
        case S_IFREG:
            perms[0]='-';
            break;
        case S_IFBLK:
            perms[0]='b';
            break;
        case S_IFCHR:
            perms[0]='c';
            break;
        case S_IFSOCK:
            perms[0]='s';
            break;
        case S_IFIFO:
            perms[0]='p';
            break;
        default:
            perms[0]='?';
            break;
    }
    
    perms[1]=(st.st_mode & S_IRUSR) ? 'r':'-';
    perms[2]=(st.st_mode & S_IWUSR) ? 'w':'-';
    perms[3]=(st.st_mode & S_IXUSR) ? 'x':'-';
    perms[4]=(st.st_mode & S_IRGRP) ? 'r':'-';
    perms[5]=(st.st_mode & S_IWGRP) ? 'w':'-';
    perms[6]=(st.st_mode & S_IXGRP) ? 'x':'-';
    perms[7]=(st.st_mode & S_IROTH) ? 'r':'-';
    perms[8]=(st.st_mode & S_IWOTH) ? 'w':'-';
    perms[9]=(st.st_mode & S_IXOTH) ? 'x':'-';

    int linkNum=st.st_nlink;

    char * fileUser = getpwuid(st.st_uid)->pw_name;
    char * fileGrp = getgrgid(st.st_gid)->gr_name;
    long int fileSize = st.st_size;
    char * time = ctime(&st.st_mtime);

    char mtime[512]={0};
    strncpy(mtime,time,strlen(time)-1);

    char buf[1024];
    sprintf(buf,"%s %d %s %s %ld %s %s",perms,linkNum,fileUser,fileGrp,fileSize,mtime,argv[1]);
    printf("%s\n",buf);
    return 0;
}

(三)显示文件内容(cat 命令)

#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;
	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);
	}
}

(四)复制文件(cp sec dest 命令)

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>

#define BLKSIZE 4096
int main(int argc, char *argv[])
{
	int fd, gd, n, total=0;
	char buf[BLKSIZE];
	if(argc<3)
		exit(1);
	if((fd = (open(argv[1], O_RDONLY)) < 0))
		exit(2);
	if((gd = open(argv[2], O_WRONLY|O_CREAT)) < 0)
		exit(3); 
	while(n = read(fd, buf, BLKSIZE))
	{
		write(gd, buf, n);
		total += n;
	}
	printf("total bytes copied=%d\n", total);
	close(fd);
	close(gd);
}
posted @   HOSF  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示