《Unix/Linux系统编程》学习笔记4

第七章 文件操作

一.知识点归纳

(一)文件操作级别

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

1.硬件级别:

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

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

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

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

kmount(), kumount()		(mount/umount file systems)
kmkdir(), krmdir()		(make/remove directory)
kchdir(), kgetcwd()		(change directory, get CWD pathname)
klink(),  kunlink()		(hard link/unlink files)
kchmod(), kchown(), kutime()	(change r|w|x permissions,owner,time)
kcreat(), kopen()		(create/open file for R,W,RW,APPEND)
kread(),  kwrite()		(read/write opened files)
klseek(), kclose()		(lseek/close file descriptors)
ksymlink(), kreadlink()		(create/read symbolic link files)
kstat(),  kfstat(), klstat()	(get file status/information)
kopendir(), 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

6.sh脚本

(二)文件I/O操作

(三)低级别文件操作

1.分区

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

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

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

(2)在磁盘映像文件上运行 fidisk:

fdisk mydisk

image
image

image

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。

二.问题与解决思路

三.实践内容与截图

为了编译和运行程序,系统必须安装 ext2fs.h 头文件

#sudo apt-get install ext2fs-dev	#课本上命令有误
sudo apt-get install libext2fs-dev	#用这个下载

(一)显示超级块

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<ext2fs/ext2_fs.h>

//typedef u8, u16, u32, SUPER for convenience
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 print(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);
	)
	1seek(fd, (1ong)1024*1, 0); // block 1 or offset 1024
	read(fd, buf, 1024);
	sp =(SUPER *)buf;			// as a super block structure
	// check for EXT2 FS magic number:
	printf("8-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");
	print("s_inodes_count",sp->s_inodes_count);
	print("s_blocks_count",sp->s_blocks_count);
	print("s_r_blocks_count",sp->s_r_blocks_count);
	print("s_free_inodes_count", sp->s_free_inodes_count);
	print("s_free_blocks_count", sp->s_free_blocks_count);
	print("s_first_data_blcok", sp->s_first_data_block);
	print("s_log_block_size",sp->s_1og_block_size);
	print("s_blocks_per_group", sp->s_blocks_per_group);
	print("s_inodes_per_group", sp->s_inodes_per_group):
	print("s_mnt_count",sp->s_mnt_count);
	print("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 = 1024 * (1 << sp->s_log_block_size);
	printf("block size = %d\n", blksize);
	printf("inode size = %d\n", sp->s_inode_size);
}

char *device = "mydisk";	// default device name
int main(int argc, char *argv[]) 
{
	if(argc>1)
		device = argv[1];
	super(sevice);
}

(二)显示位图

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<ext2fs/ext2_fs.h>

typedef struct ext2_super_block SUPER;
typedef struct ext2_group_desc GD;

#define BLKSIZE 1024
SUPER *sp;
GD *gp;
char buf[BLKSIZE];
int fd;

int get_block(int fd, int blk, char *buf)
{
	lseek(fd, (1ong)blk*BLKSIZE, 0);
	read(fd, buf, BLKSIZE);
}

int imap(char *device)
{
	int i, ninodes, blksize, imapblk;
	fd = open(dev, O_RDONLY);
	if (fd < 0)
	{
		printf("open %s failed\n", device);
		exit(1);
	}
	get_block(fd,1, buf);			// get superblock
	sp=(SUPER *)buf;
	ninodes = sp->s_inodes_count;		// get inodes_count
	printf("ninodes = %d\n", ninodes):
	get_block(fd, 2, buf);			// get group descriptor
	gp=(GD*)buf;
	imapblk = gp->bg_inode_bitmap: 		// got imap block number
	printf("imapblk = &d\n", imapblk);
	get_block(fd, imapblk, buf):		// get imap block into buf[]
		for (i=0) i<=nidoes/8;i++) 
	{					// print each byte in HEX
		printf("%02x ", (unsigned char)buf[i]);
	}
	printf("\n");
}

char *dev = "mydisk";	// default device name
int main(int argc, char *argv[]) 
{
	if(argc>1)
		dev = argv[1];
	imap(dev);
}

(三)显示根索引节点

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<ext2fs/ext2_fs.h>

#define BLKSIZE 1024
typedef struct ext2_super_block SUPER;
typedef struct ext2_group_desc 	GD;
typedef struct ext2_inode 		INODE;
typedef struct ext2_dir_entry_2 DIR;
SUPER 	*sp;
GD 		*gp;
INODE 	*ip;
DIR 	*dp;
char buf[BLKSIZE];
int fd, firstdate, inodesize, blksize, iblock;
char *dev = "mydisk";	// default device name

int get_block(int fd, int blk, char *buf)
{
	lseek(fd, blk*BLKSIZE, SEEK_SET);
	return read(fd, buf, BLKSIZE);
{
	
int inode(char *dev)
{
	int i;
	fd = open(dev, O_RDONLY) < 0):
	if (fd<0){
		printf("open failed\n"); exit(1);
	)
	get_block(fd, 1, buf);				// get superblock
	sp=(SUPER *)buf;
	firstdata = sp->s_first_data_block;
	inodesize = sp->s_inode_size;
	blksize = 1024*(1<<sp->s_log_block_size);
	printf("first_data_block=%d block_size=%d inodesize=%d\n",firstdata, blksize, inodesize);
	get_block(fd, (firstdata+1), buf);		// get group descriptor
	gp=(GD *)buf;
	printf("bmap_block=%d imap_block=%d inodes_table=%d ",gp->bg_block_bitmap,gp->bg_inode_bitmap,gp->bg_inode_table,gp->bg_free_blocks_count);
	printf("free_blocks=%d free_inodes=&d\n",gp->bg_free_inodes_count,gp->bg_used_dirs_count);
	iblock = gp->bg_inode_table;
	printf("root inode information:\n", iblock);
	printf("----------------------\n");
	get_block(fd, iblock, buf);
	ip=(INODE *)buf +1;		// ip point at #2 INODE
	printf("mode=%4x ",ip->i_mode);
	printf("uid=%d gid=%d\n", ip->i_uid, ip->i_gid);
	printf("size=%d\n",ip->i_size);
	printf("ctime=%s", ctime(&ip->i_ctime));
	printf("links=%d\n", ip->i_links_count);
	for (i=0; i<15;i++)		// print disk block numbers
	{
		if (ip->i_block[i])	// print non-zero blocks only
			printf("i_block[%d]=&d\n",i,ip->i_block[i]);
	}
}

int main(int argc, char *argv[]) 
{
	if(argc>1)
		dev = argv[1];
	inode(dev);
}

(四)显示目录条目


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

一.知识点归纳

(一)系统调用

在操作系统中,进程以两种不同的模式运行,即内核模式和用户模式,简称 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 @ 2022-09-25 21:25  油菜园12号  阅读(86)  评论(0编辑  收藏  举报