第四周学习笔记

第七章 文件操作

摘要

  • 多种文件系统:解释了操作系统中的各种操作级别,包括为文件存储准备存储设备、内核中的文件系统支持函数、系统调用、文件流上的I/O库函数、用户命令和各种操作的sh脚本;
    系统性概述了各种操作,包括从用户空间的文件流读/写到内核空间的系统调用,直到底层的设备I/O驱动程序级别:描述了低级别的文件操作,包括磁盘分区、显示分区表的示例程序、文件系统的格式化分区以及挂载磁盘分区;
  • 介绍了Linux系统的EXT2文件系统,包括EXT2文件系统的系统数据结构、显示超级块、组描述符块和索引节点位图以及目录内容的示例程序。

文件操作级别

硬件级别

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

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

  • 系统调用
  • I/O库函数
  • 用户命令
    mkdir ,rmdir,cd,pwd,ls
  • sh脚本

文件I/O操作

低级别文件操作

分区

  • 一块储存设备,如硬盘、U盘、SD卡等,可以分为几个逻辑单元
  • 各分区均可以格式化为特定的文件系统,也可以安装在不同的操作系统上。大多数引导程序。分区表位于第一个扇区的字节偏移446(0x1BE)处,该扇区称为设备的主引导记录(MBR)。每个扩展分区的第一个扇区是一个本地MBR

格式化分区

fdisk将一个储存设备划分为多个分区。每个分区都有特定的文件系统类型,但是分区还不能使用
按照教程在Linux创建一个新的分区

挂载分区

EXT2文件系统简介

EXT2文件系统数据结构


Block#0:引导快 BO是引导块,文件系统不会使用它。它用于容纳从磁盘引导操作系统的引导程序。

超级块

  • s_first_data_block:0表示4KB块大小,1表示1KB块大小。它用于确定块组描述符的起始块,s_first_data_block+ 1。
  • s_log_block_size确定文件块大小,为1KB*(2**s_log_block_size),例如О表示1KB块大小,1表示2KB块大小,2表示4KB块大小,等等。最常用的块大小是用于小文件系统的1KB和用于大文件系统的4KB。
  • s_mnt_count :已挂载文件系统的次数。当挂载计数达到max_mount_count时,fsck 会话将被迫检查文件系统的一致性。
  • s_magic是标识文件系统类型的幻数。EXT2/3/4文件系统的幻数是0xEF53

块组描述符

Block#2:快组描述符快(硬盘上的s_first_data_blocks-1)EXT2将磁盘块分成几个组。每个组都有8192个块(硬盘上的大小为32K)。每个组用一个块组描述符结构体描述。

位图

Block#8 块位图 0位表示对应项处于FREE状态,1位表示对应项处于IN_USE状态
Block#9 索引节点位图

索引节点

Block#10 :索引(开始)节点快

iblock[15]数组包含指向文件磁盘块的指针,这些磁盘块有:

  • 直接块:iblock[0]至i-block[11],指向直接磁盘块。
  • 间接块:i-block[12]指向一个包含256个块编号(对于1KBBLKSIZE)的磁盘块,每个块编号指向一个磁盘块。
  • 双重间接块:iblock[13]指向一个指向256个块的块,每个块指向256个磁盘块。
  • 三重间接块:iblock[14]是三重间接块。对于“小型”EXT2文件系统,我们可以忽略它。

问题:在使用fdisk -l 代码的时候出现fdisk: cannot open /dev/loop21: Permission denied的问题
解决:权限不足使用sudo -s进去root用户模式后解决问题

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

摘要

  • 如何使用系统调用进行文件操作系统调用的作用和Linux的在线手册页:如何使用系统调用进行文件操作;文件操作中最常用的系统调用;
  • 硬链接和符号链接文件;stat系统调用;基于stat信息,开发了一个类似于ls的程序来显示目录内容和文件信息;
  • open-close-lseek系统调用和文件描述符;如何使用读写系统调用来读写文件内容;
  • 如何使用系统调用来显示和复制文件;
  • 如何开发选择性文件复制程序,其行为类似于一个简化的Linuxdd实用程序。
  • 编程项目使用Linux系统调用来实现C程序,该程序将目录递归复制到目标中。

系统调用

操作系统中,有两种不同的模式运行,内核模式(Kmode)和用户模式(Umode)
Umode中,进程的权限有限,不能执行任何需要特殊权限的操作

系统调用手册页

Linux中,在线手册页保存在/usr/man/目录中,使用man命令可以显示相应命令方法

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

例如

#include <stdio.h>
#include <sys/types.h> 
#include <sys/stat.h> 
#include <unistd.h>
#include <err.h>
#include <errno.h>
#include <libgen.h> 
#include <stdlib.h>
#include <string.h>

static int	vflag;

static void usage(void)
{
	(void)fprintf(stderr,"usage: mkdir [-pv] [-m mode] directory_name ...\n");
}
static int create_directory(char *path, mode_t omode)
{
	struct stat sb;
	mode_t numask, oumask;
	int first, last, retval;
	char *p;
	p = path;
	oumask = 0;
	retval = 1;
	if (p[0] == '/')
		++p;
	for (first = 1, last = 0; !last ; ++p) {
		if (p[0] == '\0')
			last = 1;
		else if (p[0] != '/')
			continue;
		*p = '\0';
		if (!last && p[1] == '\0')
			last = 1;
		if (first) {
			oumask = umask(0);
			numask = oumask & ~(S_IWUSR | S_IXUSR);
			(void)umask(numask);
			first = 0;
		}
		if (last)
			(void)umask(oumask);
		if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
			if (errno == EEXIST || errno == EISDIR) {

				if (stat(path, &sb) < 0) {
					warn("%s", path);
					retval = 0;
					break;
				} else if (!S_ISDIR(sb.st_mode)) {
					if (last)
						errno = EEXIST;
					else
						errno = ENOTDIR;
					warn("%s", path);
					retval = 0;
					break;
				}
				if (last)
					retval = 2;
			} else {
				warn("%s", path);
				retval = 0;
				break;
			}
		} else if (vflag)
			printf("%s\n", path);
		if (!last)
		    *p = '/';
	}
	if (!first && !last)
		(void)umask(oumask);
	return (retval);
}
int main(int argc, char *argv[])
{
	int ch, exitval, success, pflag;
	mode_t omode;
	void *set = NULL;
	char *mode;
	omode = pflag = 0;
	mode = NULL;
	while ((ch = getopt(argc, argv, "m:pv")) != -1)
		switch(ch) {
		case 'm':
			mode = optarg;
			break;
		case 'p':
			pflag = 1;
			break;
		case 'v':
			vflag = 1;
			break;
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;
	if (argv[0] == NULL)
		usage();

	if (mode == NULL) {
		omode = S_IRWXU | S_IRWXG | S_IRWXO;
	}
	for (exitval = 0; *argv != NULL; ++argv) {
		if (pflag) {
			success = create_directory(*argv, omode);
		} else if (mkdir(*argv, omode) < 0) {
			if (errno == ENOTDIR || errno == ENOENT)
				warn("%s", dirname(*argv));
			else
				warn("%s", *argv);
			success = 0;
		} else {
			success = 1;
			if (vflag)
				(void)printf("%s\n", *argv);
		}
		if (!success)
			exitval = 1;

		if (success == 1 && mode != NULL && chmod(*argv, omode) == -1) {
			warn("%s", *argv);
			exitval = 1;
		}
	}
	return (exitval);
}

常用的系统调用

  • open:打开一个文件进行读、写、追加
  • close:关闭打开的文件描述符
  • read:读取打开的文件描述符
  • write:写入打开的文件描述符
  • dup:将文件描述符复制到可用的最小描述符编号中
  • dup2:将oldfd复制到newfd中,如果文件链接数为0,则删除文件
  • link:将新文件硬链接到旧文件
  • unlink:取消某个文件的链接;如果文件链接数为0,则删除文件
  • symlink:创建一个符号链接
  • readlink:读取符号链接文件的内容
  • umask:设置文件创建掩码;文件权限为(mask & ~umask)
  • mknod:创建特殊文件

链接文件

硬链接文件

硬链接:命令

ln oldpath newpath

符号链接文件

软连接:命令

ln -s oldpath newpath

[软链接和硬链接的区别](https://xzchsia.github.io/2020/03/05/linux-hard-soft-link/#:~:text=实际上,硬链接和源文件是同一份文件,而软连接是独立的文件,类似于快捷方式,存储着源文件的位置信息便于指向。.,使用限制上,不能对目录创建硬链接,不能对不同文件系统创建硬链接,不能对不存在的文件创建硬链接;可以对目录创建软连接,可以跨文件系统创建软连接,可以对不存在的文件创建软连接。.)

stat系统调用

stat与文件索引节点

每个索引节点在存储设备上都有唯一的索引节点编号(ino)。每个设备都由一对(主、次)设备号标识,例如0x0302表示/dev/hda2等

文件类型和权限

st_mode的类型是一个u16(16位)
前4位是文件类型,接下来3位表示文件的特殊用法,后9位基于权限位

opendir-readdir函数

目录也是一个文件

#include <dirent.h>

Linux中的dirent结构体是:

struct dirent{
  u32 d_ino;
  u16 d_reclen;
  char d_name[ ]

readlink函数

读取符号链接文件的内容

int readlink(char *pathname, char buf[ ],int bufsize);

ls程序

eg:C语言实现ls

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <utime.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>


int print_type(mode_t st_mode){
  char mode[10] = "----------";

  //file attributes
  //
  if(S_ISREG(st_mode)) mode[0] = '-';
  if(S_ISDIR(st_mode)) mode[0] = 'd';
  if(S_ISFIFO(st_mode)) mode[0] = 'p';
  if(S_ISSOCK(st_mode))mode[0] = 's';
  if(S_ISBLK(st_mode)) mode[0] = 'b';
  if(S_ISCHR(st_mode)) mode[0] = 'c';
  if(S_ISLNK(st_mode)) mode[0] = 'l';
  //file permission

  if(st_mode & S_IRUSR) mode[1] = 'r';
  if(st_mode & S_IWUSR) mode[2] = 'w';
  if(st_mode & S_IXUSR) mode[3] = 'x';
  if(st_mode & S_IRGRP) mode[4] = 'r';
  if(st_mode & S_IWGRP) mode[5] = 'w';
  if(st_mode & S_IXGRP) mode[6] = 'x';
  if(st_mode & S_IROTH) mode[7] = 'r';
  if(st_mode & S_IWOTH) mode[8] = 'w';
  if(st_mode & S_IXOTH) mode[9] = 'x'; 

  printf("%s ", mode);
  return 0;
}

int print_info(struct stat currentstat){
  struct passwd *p_passwd;
  struct group *p_group;
  char *p_time;
  int i;
  p_time = ctime(&currentstat.st_mtime);
  p_passwd = getpwuid(currentstat.st_uid);
  p_group = getgrgid(currentstat.st_gid);       
  printf("%d ",(int)currentstat.st_nlink);
  if(p_passwd != NULL)
	printf("%s ",p_passwd->pw_name);
  else
	printf("%d ",(int)currentstat.st_uid);
  if(p_group != NULL)
	printf("%s ",p_group->gr_name);
  else
	printf("%d ",currentstat.st_gid);
  printf("%7d ",(int)currentstat.st_size);
  for(i=0; p_time[i] !=0 && p_time[i]!='\n'; i++){
	  putchar(p_time[i]);
  }

  return 0;
}

int main(int argc, char* argv[]){
  char buf[500];
  DIR *currentdir = NULL;
  struct dirent *currentdp = NULL;
  struct stat currentstat;



  if(argc != 2)
  {
     printf("ERROR: Invalid Argument!\n");
     exit(1);
  }
  memset(buf,0,500); 
  sprintf(buf,"%s",argv[1]);
  currentdir = opendir(buf);
  if(currentdir == NULL){
     printf("open directory fail\n");
     return 0;
  }


  while((currentdp = readdir(currentdir)) != NULL)
 {
  if(currentdp->d_name[0] != '.')//跳过副目录
 {
    sprintf(buf,"%s/%s",buf,currentdp->d_name);
  if(lstat(buf,&currentstat) == -1){
    printf("the dir:%s :",buf);
    printf("get stat error\n");
    continue;
  }

  lstat(buf,&currentstat);


  
  print_type(currentstat.st_mode);        
  print_info(currentstat);
  printf("  ");
  printf("%s\n", currentdp->d_name);
 
  memset(buf,0,500);
  sprintf(buf,"%s",argv[1]);
  
 }//if end
 }//while end
  closedir(currentdir);
  return 0;
}

open-close-lseek系统调用

  • open:打开一个文件进行读、写、追加
  • close:关闭打开的文件描述符
  • read:读取打开的文件描述符
  • write:写入打开的文件描述符
  • lseek:将文件描述符的字节偏重量重新定位为偏移量
  • umask:设置文件创建掩码;文件权限为(mask & ~umask)

read()系统调用

将n个字节从打开的文件描述符读入用户空间中的buf[] 返回值是实际读取的字节数,read失败返回-1

write()系统调用

将n个字节从用户空间中的buf[]写入文件描述符,必须打开该文件描述符进行写,读写或追加

问题:运行以下代码时出现错误

#include <stdio.h>
#include <errno.h> 
int main()
{
char buf[256] ,*s;
int r;
r=mkdir("newdir",0766);//mkdir syscal1 
if(r<0)
    printf("errno=%d :%s\n",errno, strerror(errno));
r=chdir("newdir");	// cdinto newdir	
s=getcwd(buf,256);	// get CWD string into buf[	
printf("CWD = %s\n",s);
}
posted @ 2022-09-25 21:09  20201327刘谨铭  阅读(29)  评论(0编辑  收藏  举报