20211119陈俊积
一、知识点归纳和苏格拉底挑战
第七章
文件操作级别
文件操作分为五个级别,按照从低到高的顺序排列如下:
(1)硬件级别
硬件级别的文件操作包括:
fdisk:将硬盘、U盘或SDC盘分区。
mkfs:格式化磁盘分区,为系统做好准备。
fsck:检查和维修系统。
碎片整理:压缩文件系统中的文件。
(2)操作系统内核中的文件系统函数
每个操作系统内核均可为基本文件操作提供支持,例如
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() (lseak/close file descriptors) ksymlink(), kreadlink () (create/read symbolic link files) kstat(), kfstat(),klstat() (get file status/information) kopendir(), kreaddiz() (open/read airectories)
(3)系统调用
用户模式程序使用系统调用来访问内核函数,如open(),read(),lseek()和close()都是c语言库函数。每个库函数都会发出一个系统调用,使进程进入内核模式来执行相应的内核函数。当进程结束执行内核函数时,会返回到用户模式,并得到所需的结果。在用户模式和内核模式之间切换需要大量的操作(和时间)。因此,内核和用户空间之间的数据传输成本昂贵。对于读/写文件,最好的方法是匹配内核的功能。内核会按数据块大小(从1KB到8KB)来读取/写入文件。例如,在Linux 中,硬盘的默认数据块大小是4KB,软盘的是1KB。因此,每个读/写系统调用还要尝试一次传输一个数据块。
(4)I/O库函数
用户通常需要读/写单独的字符、行或数据结构记录等。如果只有系统调用,用户模式程序则必须自己从缓冲区执行这些操作。由于大多数用户会认为这非常不方便,因此C语言库提供了一系列标准的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脚本
sh语言包含所有的有效Unix/Linux命令,它还支持变量和控制语句,如if、do、for、while、case等。除sh之外,Perl和Tcl等其他许多脚本语言也使用广泛。
文件I/O操作
低级别文件操作
(1)分区
一个区块存储设备可以分为几个逻辑单元,成为分区。分区表位于第一个扇区的字节偏移446(ox1BE)处,改扇区称为设备的主引导记录。如果某分区是扩展类型(类型编号=5),那么它可以划分为更多分区。
(2)格式化分区
fdisk只是将一个存储设备划分为多个分区。每个分区都有特定的文件系统类型,但是分区还不能使用。为了存储文件,必须先为特定的文件系统准备好分区。该操作习惯上称为格式化磁盘或磁盘分区。在Linux中,命令
mkfs -t TYPE [-b bsize] device nblocks
在一个nblocks设备上创建一个TYPE文件系统,每个块都是bsize字节。如果bsize未指定,则默认大小为1KB.
在Linux中,还不能访问新的文件系统。它必须挂在到跟文件系统中的现有目录。由于细腻文件系统不是真正的设备i,他们必须作为循环设备挂载,如:
sudo mount -o loop vdisk /mnt
第八章
系统调用
操作系统中,进程以内核模式(Kmode)和用户模式(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 close(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,char *newpath)
硬链接文件会共享文件系统中相同的文件表示数据结构。文件链接会记录链接到同一索引结点的硬连接数量。相反,系统调用:
unlink(char *pathname)
会减少文件的链接数。如果链接变为0,文件就被完全删除。这就是rm命令的作用。为防止重要文件被意外删除,最好创建多个链接到这个文件的硬链接。
(2)软链接文件
软连接命令:
ln -s oldpath newpath #in command with the -s flag
创建从newpath到oldpath的软链接或符号链接。对应的系统调用是:
symlink(char *oldpath,chat *newpath)
newpath是LNK类型的普通文件,包含oldpath字符串。它可作为一个绕行标志,使访问指向链接好的目标文件。与硬链接不同,软链接适用于任何文件,包括目录。
Stat系统调用
stat/lstat/fstat系统调用可将一个文件的信息返回。
所有的stat系统调用都以stat结构体形式返回信息,其中包含以下片段:
struct stat{
dev_t st_dev;
ino_t st_ino;
mode_t st_mode;
st_nlink; nlink_t
uid_t st_uid;
gia_t st_gid;
dev_t st_rdev;
off_t st_size;
u32 st_blksize;
u32 st_block8;
time_t st_atime;time_E
st_mtime time_t st_ctime;
};
Open-close-lseek系统调用(有收获)
open:打开一个文件进行读、写、追加.它会返回一个进程可用的最小文件描述符,用于后续的read( ),write( ),lseek( )和close( )系统调用。
#include<sys/type.h>
#include<sys/stat.h>
#include<fcntl.h>
int open (char *pathname,int flags,mode_t mode);
close:关闭打开的文件描述符
#include <unistd.h>
int close(int fd);
lseek:将文件描述符的字节偏移量重新定位成偏移量
#include<sys/type.h>
#include <unistd.h>
off_t seek(int fd,off_t offset,int whence);
umask:设置文件创建掩码;文件权限(mask&~umask)
Read()系统调用
read:读取打开的文件描述符。read( )将n个字节从打开的文件描述符读入用户空间中的buf[ ].返回值是实际读取的字节数,如果失败,会返回-1。
#include <unistd.h>
int read(int fd,void *buf,int nbytes)
Write()系统调用
write:写入打开的文件描述符。read( )将n个字节从用户空间中的buf[ ]写入文件描述符,必须打开该文件描述符进行写、读写或追加。返回值是实际读取的字节数,通常等于n个字节。如果失败,会返回-1。
#include <unistd.h>
int write(int fd,void *buf,int nbytes)
苏格拉底挑战
二、问题与解决思路
用课本上给的复制程序生成的文件不能正常打开
解决方法:在运行程序时用sudo命令
或
三、实践内容与截图
第七章
分区:
虚拟磁盘映像创建:
在磁盘映像文件上运行fdisk:
帮助文档
格式化分区:
使用1440个块把vdisk格式化为ext2文件系统:
把vdisk挂载到/mnt目录里边。df -h查看挂载点,然后卸载设备:
第八章
mkdir、chdir、getcwd 系统调用:
1.c
#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);
}
新建一个文件夹newdir
ls程序(ls -l file命令):
2.c
#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;
}
相当于ls -l file 命令:
显示文件内容(cat 命令):
3.c
#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);
}
}
将1.c(即mkdir、chdir、getcwd 系统调用)打印出来
复制文件:
4.c
#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);
}
将1.c的内容复制到5.c中并用3.c打印出来: