• 第四周学习笔记


    第七章 文件操作

    摘要

    • 多种文件系统:解释了操作系统中的各种操作级别,包括为文件存储准备存储设备、内核中的文件系统支持函数、系统调用、文件流上的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);
    }
    
  • 相关阅读:
    组合模式及C++实现
    YUV422(UYVY)转RGB565源代码及其讲解.md
    会用errno,事半功倍
    可变参数宏
    camera理论基础和工作原理
    !!!??? 2.3 核心模块与应用程序的对比
    KVM与VMware的性能比较
    单片机中定时器与计数器的区别
    编译器对变量的内存分配方式
    【转载】Modelsim 与Vivado联合仿真版本对应问题
  • 原文地址:https://www.cnblogs.com/yycyhyhf/p/16725027.html
Copyright © 2020-2023  润新知