20201306吴龙灿第七、八章学习笔记

Ⅰ知识点归纳

一、文件操作

1.文件操作级别

​文件操作分为五个级别,按照从低到高的顺序可以如下表示:

硬盘级别

​这些操作多是针对系统的实用程序,一般用户不会涉及到它们的运用,但是这些操作是创建和维护系统的关键。这些操作包括:

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

操作系统内核中的文件系统函数。
​每个操作系统内核都是可以为基本文件操作提供支持的。它们有一个共同点,就是有 k 前缀:

系统调用

​用户模式用来访问内核函数的工具,在C语言中是一系列文件操作相关的库函数,如:

open() => kopen()
read() => kread()
lseek()...
close()...

这些函数在第十章中有较为详实的使用解析。
内核函数传输数据是按数据块的格式进行的。
​这些库函数会发出一个系统调用,使进程进入内核模式来执行相关的内核函数,以达到文件操作的目的。进程结束执行内核函数后就会回到用户模式。在内核模式下,每次读取的内容一般为 n KB的数据块,根据系统不同, n 的值会在1~8间变化 ,比如,在Linux中,硬盘默认的数据块大小为4KB,软盘为1KB。

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库函数都是建立在系统调用之上的。

用户命令

​在Linux系统中,会经常用到一些用户命令,如 mkdir ,ls ,mv ,cp等,除此自外还有如下用户命令

rmdir ,cd ,pwd ,link ,unlink ,rm ,cat ,chmod...
其中除了cd命令以外,其余都为一个可执行程序,这些程序通常会通过调用I/O库函数实现功能。

sh脚本

使用sh编程语言编写的一种脚本程序,虽然比系统调用方便,但是要手动输入命令写脚本,还要指定设备来进行输入。

2.文件I/O操作

下图为文件I/O操作示意图。

3.低级别文件操作

1.分区

​像给一块大容量硬盘分区,可以通过分区操作来将一个大存储空间分为不同的逻辑单元,各个分区可以格式化成为特定的文件文件系统,可以有效的隔离不同数据。
​在Linux系统下,可以通过如下操作创建一个虚拟磁盘映象文件:

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

2.格式化分区​

fdisk 将一个存储设备划分为多个分区,但是刚刚划分出来的分区还需要经过格式化才可以存储文件,可以使用mkfs命令在特定的分区上建立 linux 文件系统,mkfs的一般格式如下:

mkfs [-V] [-t fstype] [fs-options] filesys [blocks]

3.挂载分区​

losetup 命令用于设置循环设备。循环设备可把文件虚拟成区块设备,籍以模拟整个文件系统,让用户得以将其视为硬盘驱动器,光驱或软驱等设备,并挂入当作目录来使用。​ losetup命令语法如下:

losetup [-d][-e <加密方式>][-o <平移数目>][循环设备代号][文件]

4.EXT2文件系统简介

ext2是Linux系统所默认的文件系统
它有如下的磁盘块:
BLOCK#0: 引导块 用于容纳从磁盘引导操作系统的引导程序,不会被文件操作系统使用
BLOCK#1: 超级块 在硬盘分区中字节偏移量为1024,超级块容纳了关于整个文件系统的信息
超级块有一些重要字段,这些字段表明了超级块的各段含义:
BLOCK#2: 块组表述符块 ext2将磁盘块分成若干组,每组8192块(约32K),每组都有一个块组描述符结构体
BLOCK#8: 块位图 表示某种项的位序列
BLOCK#9: 索引节点位图 代表一个文件的数据结构
BLOCK#10: 索引节点 大小为128字节的索引结构体,表示一个文件

二、使用系统调用进行文件操作

常用的系统调用
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)

进程控制

1)getpid,getppid——获取进程识别号

#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);

获取进程标识号。这可以作为一个独一无二的临时文件名。

pid_t getppid(void);

获取父进程标示号。

2)fork——创建一个子进程

#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);

通过完全复制当前进程创建一个新进程。
如果创建子进程成功,在父进程中返回子进程的进程标示符,在子进程中返回0。
如果失败,在父进程中返回-1,子进程不会被创建,errno被设置。
因为在fork()的调用处,整个父进程空间会原模原样地复制到子进程中,包括指令,
变量值,程序调用栈,环境变量,缓冲区,等等。所以,子进程和父进程是不能通过
程序内的变量(即使是全局变量)通信的,对于这两个进程来说,它们有各自的进程空间,
互不影响。但父进程和子进程可以通过管道,共享内存,等方式实现通信。

3)sleep——使进程睡眠指定的秒数

#include <unistd.h>
unsigned int sleep(unsigned int seconds);

到达指定时间后返回0,若有信号中断,则返回剩余的秒数。

4)wait,waitpid——等待子进程终止

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);

等待子进程的状态发生变化,并且获得状态变化的信息。状态变化是指:子进程终止;子进程被信号停止;
或者子进程被信号恢复。
等待子进程终止可以让系统释放子进程的资源,如果不等待,那么就会产生僵尸进程。
如果一个子进程状态发生变化,系统调用立即返回,否则就一直阻塞,直到子进程状态发生变化,或者一个
信号中断系统调用。
wait(&status);等价于waitpid(-1, &status, 0);
wait等待一个子进程终止,waitpid等待pid指定的子进程状态改变。默认waitpid仅等待子进程终止,可以
通过options来改变行为。
wait执行成功返回终止子进程的进程号,否则返回-1.
waitpid执行成功返回状态改变子进程的进程号;如果指定了WHOHANG并且pid指定的子进程存在,但是
状态没有改变,立即返回0。错误返回-1.
子进程的返回状态由status存储,可以通过宏来获得信息。如果不关心,可以将status设成NULL。

5)execve——执行程序
#include <unistd.h>
int execve(const char *filename, char *const argv[],
             char *const envp[]);

Ⅱ最有收获的地方

  因为平时我所应用的操作系统就是Windows系列,所以我并没有体会到操作系统的原理。Windows几乎将所有的进程都通过窗口这个东西封装起来了,所以即使Windows很人性化,提高了使用性,但是却很难真正搞懂这其中的原理所在。
  这两章都是针对于系统调用层面的文件操作,因为之前使用Linux系统输入过很多命令行,但都不懂这个作用,但是再教材上学到了有关这些命令行的程序,收获巨大。我最感兴趣的是ls程序。
  如此重要的原因在于它允许查看目录中的文件。将经常使用它来列出目录内容。 ls 不是一个复杂的命令,实现起来也很容易,但它确实包含许多不同的选项,可用于列出包含附加信息的文件。
  在这过程中,你可能会发现其中一些选项非常有用,即使 ls 本身总是足以列出内容。掌握 ls 命令将使你更有效地列出目录内容和查找文件。它也可以在 Bash 脚本中使用,以帮助其他工具操作文件。

Ⅲ问题

怎么实现ls命令?

Ⅳ解决思路

我们已经通过在命令行上输入ls命令,列出目录中包含的文件和子目录。

-a 选项还将列出隐藏文件(名称以 . 开头的文件)。 除非您在根目录中,否则它还会列出 . (当前工作目录)和 … (向上一个目录)作为文件。
那么我们如何读取目录、文件信息呢?
最多常见的readdir使用方式:

#include<stdio.h>
#include<dirent.h>
#include<string.h>
#include <stdio.h>
#include <iostream>
#include <stdlib.h>

using namespace std;

#define FILE_NAME "/opt/code/linux_command_code"

int main(int argc, char **argv)
{
	DIR   *dir;
	struct  dirent *ptr;
	dir = opendir(FILE_NAME);
	if(NULL == dir)
	{
		cout << "opendir is NULL" << endl;
		return -1;
	}
	while( (ptr = readdir(dir))!=NULL)
	{
		printf("d_ino:%ld  d_off:%ld d_name: %s\n", ptr->d_ino,ptr->d_off,ptr->d_name);   
	}
	closedir(dir);
	
	return 0;
}

编译运行:

Ⅴ实践内容与截图,代码链接

实践内容我选择再分区范围内进行:
再Linux下,创建一个名为mydisk的虚拟磁盘映像文件。

dd if=/dev/zero of=mydisk bs=1024 count=1440
然后在磁盘上运行fdisk:
截图如下:

Ⅵ还没有解决的问题

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<ext2fx/ext2_fs.h>
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef struct ext2_super_block SUPER;
SUPER *sp;
char buf[1024];
int fd,blksize,inodesize;
int printf(char *s,u32 x)
{
	printf("%-30s=%8d\n",s,x);
}
int super(char *device)
{
	fd=open(device,O_RDONLY);
	if(fd<0){
		printf("open %sfailed\n",device);exit(1);
	}
	lseek(fd,(long)1024*1,0);
	read(fd,buf,1024);
	sp=(SUPER *)buf;
	printf("%-30s=%8x","s_magic",sp->s_magic);
	if(sp->s_magic!=0xEF53){
		printf("NOT an EXT2 FS\n");exit(2);
	}
	printf("EXT2 FS OK\n");
	printf("s_inodes_count",sp->s_inodes_count);
	printf("s_blocks_count",sp->s_blocks_count);
	printf("s_r_blocks_count",sp->s_r_blocks_count);
	printf("s_free_inodes_count",sp->s_free_inodes_count);
	printf("s_free_blocks_count",sp->s_free_blocks_count);
    printf("s_first_data_block",sp->s_first_data_block);
    printf("s_log_block_size",sp->s_log_block_size);
    printf("s_blocks_per_group",sp->s_blocks_per_group);
    printf("s_inodes_per_group",sp->s_inodes_per_group);
    printf("s_mnt_count",sp->s_mnt_count);
    printf("s_max_mnt_count",sp->s_max_mnt_count);
    printf("%-30s=%8x\n","s_magic",sp->s_magic);
    printf("s_mtime=%s",ctime(&sp->s_mtime));
    printf("s_wtime=%s",ctime(&sp->s_wtime));
    blksize=1204*(1<<sp->s_log_block_size);
    printf("block size = %d\n",blksize);
    printf("inode size = %d\n",sp->s_inode_size);
	
}
char *device="mydisk";
int main(int argc, char *argv[])
{
	if(argc>1)
	device=argv[1];
	super(device);
}

截图:

还是没能解决头文件的问题,希望各位大佬帮忙。

posted @ 2022-09-25 17:10  乌龍茶  阅读(43)  评论(0)    收藏  举报