第七章 文件操作
摘要
- 多种文件系统:解释了操作系统中的各种操作级别,包括为文件存储准备存储设备、内核中的文件系统支持函数、系统调用、文件流上的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(¤tstat.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,¤tstat) == -1){
printf("the dir:%s :",buf);
printf("get stat error\n");
continue;
}
lstat(buf,¤tstat);
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);
}