《Unix/Linux系统编程》第四周学习笔记
文件操作(第7章)
文件操作级别
硬件级别:
硬件级别的文件操作包括:
`fdisk`:将硬件、U盘或SDC盘分区。
`mkfs`:格式化磁盘分区,为系统做好准备。
`fsck`:检查和维修系统。
`碎片整理`:压缩文件系统中的文件。
操作系统内核中的文件系统函数:
kumount(),kumount()
(mount/umount file systems)
kmkdir(),krmdir()
(make/remove directory)
kchair(),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)
keymlink(),kreadlink ()
(create/read symbolic 1ink files)
kstat(),kfstat(),klatat() (get file status/information)
kopendir(),kreaddir()
(open/read directories)
系统调用
用户模式程序使用系统调用来访问内核函数。open() read() lseek() 和 close() 函数都是C语言库函数.每个库函数都会发出一个系统调用,
使进程进人内核模式来执行相应的内核函数,例如 open 可进入 kopen() , read 可进入 kread()函数,等等.当进程结束执行内核函数时,会返回到用
户模式,并得到所需的结果.在用户模式和内核模式之间切换需要大量的操作和时间.因此,内核和用户空间之间的数据传输成本昂贵.虽然可
以发出read(fd,buf,1)系统调用来只读取一个字节的数据,但是这种做法是不明智的,因为一个字节也会带来可怕的高成本.我们在每次必须进
入内核时,都要尽可能不虚此行.对于读/写文件,最好的方法是匹配内核的功能.内核会按数据块大小(从1KB到8KB)来读取/写入文件.例如,
在Linux中,硬盘的默认数据块大小是4KB,软盘的是1KB.因此,每个读/写系统调用还要尝试一次传输一个数据块.
I/O库函数
系统调用可让用户读/写多个数据块,这些数据块只是一系列字节.它们不知道,也不关心数据的意义.用户通常需要读/写单独的字符、行或数据结构
记录等。如果只有系统调用,用户模式程序则必须自己从缓冲区执行这些操作。大多数用户会认为这非常不方便.为此,C语言库提供了一系列标准的1/O
函数,同时也提高了运行效率。I/O库函数包括:
FILE mode I/O
:fopen(), fread(),Ewrite(),fseek(),fclose(),fflush()
char mode I/O
:getc(),getchar(), ugetc(), putc(),putchar()
line mode I/0
:gets(),fgets(), puts(),fputs()
用户命令
用户可以使用Unix/Linux命令来执行文件操作,而不是编写程序.用户命令的示例如下:
mkdir,rmdir,cd,pwd,1s,1link,unlink,rm,cat,ep,mv,chmod,ete
每个用户命令实际上是一个可执行程序(cd 除外),通常会调用库I/O函数,而库I/O函数再发出系统调用来调用相应的内核函数。用户命令的处理顺序为:
Command =>Library I/0 function =>System cal1 =>Kernel Function
或:Command======================== >System cal1 =>Kernel Function
sh脚本
sh脚本::虽然比系统调用方便得多,但是必须要手动输入命令,如果使用的是GUI必须要拖放文件图标和点击指向设备来输入,操作烦琐而且耗时.sh脚本是用
sh编程语言编写的程序,可通过命令解释程序sh来执行.sh语言包含所有的有效Unix/Linux命令.它还支持变量和控制语句,如if、do、for、while、case等
实际上,sh脚本广泛用于Unix/Linux系统编程.除sh之外,Perl和Tcl等其他许多脚本语言也使用广泛.
文件I/O操作
(1)用户模式下的程序执行操作
`FILE *fp= fopen("file","r"); or FILE *fp = Efopen("file","w");`
(2)fopen()在用户(heap)空间中创建一个FILE结构体,包含一个文件描述符fd、一个fbuf[BLKSIZE]和一些控制变量.它会向内核中的kopen()发出一个
fd=open("file",fags=READ or WRITE)系统调用,构建一个OpenTable来表示打开文件示例.OpenTable的mptr指向内存中的文件INODE.对于非特殊文件,
NODE的i_block数组指向存储设备上的数据块.成功后,fp会指向FILE结构体,其中fd是open()系统调用返回的文件描述符。
(3)fread(ubuf,size,nitem,fp):将nitem个size字节读取到ubuf上,通过:将数据从FILE结构体的fbuf上复制到ubuf上,若数据足够,则返回.
如果fbuf没有更多数据,则执行(4a).
(4a)发出read(fd, fbuf,BLKSIZE)系统调用,将文件数据块从内核读取到fbuf上,然后将数据复制到ubuf上,直到数据足够或者文件无更多数据可复制.
(4b)fwrite(ubuf,size,nitem,fp):将数据从ubuf复制到fbuf.
·若(fbuf有空间):将数据复制到fbuf上,并返回.
·若(fbuf已满):发出 writefd,fbuf,BLKSIZE)系统调用,将数据块写入内核,然后再次写人fbuf。这样,freadO/fwrite()会向内核发出 readO/writeO系统
调用,但仅在必要时发出,而且它们会以块集大小来传输数据,提高效率.同样,其他库I/O 函数,如 fgete/fputc、fgets lputs、fscanf/fprintf等也可以在
用户空间内的FILE 结构体中对 fbuf进行操作。
(5)内核中的文件系统函数:假设非特殊文件的read(fd,fbuf),BLKSIZE)系统调用.
(6)在read系统调用中,fd是一个打开的文件描述符,它是运行进程的fd数组中的一个索引,指向一个表示打开文件的OpenTable.
(7)OpenTable包含文件的打开模式、一个指向内存中文件INODE的指针和读/写文件的当前字节偏移量.从OpenTable的偏移量,·计算逻辑块编号Ibk。通过
INODE.iblock[]数组将逻辑块编号转换为物理块编号blk.
(8)Minode包含文件的内存INODE.EMODE.iblock[]数组包含指向物理磁盘块的指针.文件系统可使用物理块编号从磁盘块直接读取数据或将数据直接写入磁盘块,但将会导致过多的物理磁盘I/O。
(9)为提高磁盘I/O效率,操作系统内核通常会使用一组IO缓冲区作为高速缓存,以减少物理IO的数量。.
7.3 低级别文件操作
分区
一个块存储设备,如硬盘、u盘、SD卡等,可以分为几个逻辑单元,称为分区,各分区均可以格式化为特定的文件系统,也可以安装在不同的操作系统上。
大多数引导程序,如 GRUB、LILO等,都可以配置为从不同的分区引导不同的操作系统。分区表位于第一个扇 区的字节偏移446(OxlBE)处,该扇区称
为设备的主引导记录(MBR)。表有4个条目,每 个条目由一个16字节的分区结构体定义,即:
stuct partition {
u8 drive; // 0x80 - active
u8 head; // starting head
u8 sector; // starting sector
u8 cylinder; // starting cylinder
u8 sys_type; // partition type
u8 end_head; // end head
u8 end_sector; // end sector
u8 end_cylinder; // end cylinder
u32 start_sector; // starting sector counting from 0
u32 nr_sectors; // number of sectors in partition
};
个扩展分区的第一个扇区是一个本地MBR。每个本地MBR在字节偏移量OxlBE处也有一个分区表,只包含两个条目。第一个条目定义了扩展分区的起始房区和大小。
第二个条目指向下一个本地MBR。所有本地MBR的扇区编号都与P4的起始扇区有关。照例,链表以最后一个本地MBR中的0结尾。在分区表中,CHS值仅对小于8GB
的磁盘有效:对大于8GB但小于4G扇区的磁盘,只有最后两个条目start_sector和nr_sector有意义。接下来,我们将通过示例来演示fdisk和分区。由于使用
计算机的真实磁盘进行操作会非常危险,所以我们要使用一个虚拟磁盘映像.它只是一个普通的文件,但是看起来像一个真实磁盘。
- 在Linux下,例如Ubuntu,创建一个名为mydisk的虚拟磁盘映像文件。
- 在磁盘映像文件上运行fdisk:fdisk mydisk
格式化分区
挂载分区
EXT2文件系统简介
多年来,Linux一直使用EXT2 ( Card等1995; EXT2 2001 )作为默认文件系统。EXT3 (EXT3 2015 )是EXT2的扩展,
EXT3中增加的主要内容是一个日志文件,它将文件系统的更改记录在日志中。日志可在文件系统崩溃时更快从错误中恢复。
没有错误的EXT3文件系统与EXT2文件系统相同。EXT3的最新扩展是EXT4 ( Cao等2007)。EXT4的主要变化是磁盘块的分
配。在EXT4中,块编号是48位。EXT4不是分配不连续的磁盘块,而是分配连续的磁盘块区,称为区段。
编辑实例
显示位图
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <ext2fs/ext2_fs.h>
#include <sys/types.h>
#include <unistd.h>
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);
}
lseek(fd ,(long)1024*1,0);
read(fd, buf, 1024);
sp = (SUPER *)buf;
printf("%-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_block",sp->s_first_data_block);
print("s_log_block_size",sp->s_log_block_size);
print("s_blocks_per_group",sp->s_blocks_per_group);
print("s_innodes_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_mtime = %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";
int main(int argc, char *argv[])
{
if(argc < 1)
{
device = argv[1];
}
super(device);
}
显示跟索引结点
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/io.h>
#include <ext2fs/ext2_fs.h>
#define BLKSIZE 1024
typedef struct ext2_group_desc GD;
typedef struct ext2_super_block SUPER;
typedef struct ext2_dir_entry_2 DIR;
typedef struct ext2_inode INODE;
SUPER *sp;
GD *gp;
INODE *ip;
DIR *dp;
char buf[BLKSIZE];
int fd,firstdata, inodesize ,blksize, iblock;
char *dev = "mydisk";
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);
if(fd < 0)
{
printf("open faildn");
exit(1);
}
get_block(fd, 2, buf);
gp = (GD *)buf;
printf("bmap_block=%d imap_block=%d inodes_table=%d n",
gp->bg_block_bitmap,
gp->bg_inode_bitmap,
gp->bg_inode_table);
iblock = gp->bg_inode_table;
printf("----root inode information----n");
get_block(fd, iblock, buf);
ip = (INODE *)buf;
ip++;
printf("mode = %4x ",ip->i_mode);
printf("uid = %d gid = %dn", ip->i_uid, ip->i_gid);
printf("size = %dn", ip->i_size);
//unsigned int tmp = ip->i_ctime;
printf("ctime = %s",ctime((const time_t *)&ip->i_ctime));
printf("links = %dn", ip->i_links_count);
for ( i = 0; i < 15; i++)
{
if(ip->i_block[i])
{
printf("i_block[%d] = %dn", i, ip->i_block[i]);
}
}
}
int main(int argc, char *argv[])
{
if(argc>1) dev = argv[1];
inode(dev);
}
第7章问题
以下代码运行结果无法识别ext2文件系统
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <ext2fs/ext2_fs.h>
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);
}
lseek(fd ,(long)1024*1,0);
read(fd, buf, 1024);
sp = (SUPER *)buf;
printf("%-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_block",sp->s_first_data_block);
print("s_log_block_size",sp->s_log_block_size);
print("s_blocks_per_group",sp->s_blocks_per_group);
print("s_innodes_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_mtime = %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";
int main(int argc, char *argv[])
{
if(argc < 1)
{
device = argv[1];
}
super(device);
}
第八章(系统调用文件)
创建多个文件夹(c语言实现)
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
int main(int argc,char *argv[])
{
char buf[256];
int r;
int i = 0;
printf("%d",argc);
for(int i = 1;i<argc;i++)
{
r = mkdir(argv[i],0766);
if(r<0)
{
printf("errno=%d : %s\n", errno, strerror(errno));
}
r = chdir(argv[i]);
s = getcwd(buf, 256);
printf("cwd = %s\n", s);
}
}
软链接文件操作
ls程序(参考https://www.cnblogs.com/cfqlovem-521/p/15362070.html
)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <libgen.h>
#include <pwd.h>
#include <grp.h>
int ls_file(char *fname)
{
//get file status
struct stat fstat;
int r, i;
char ftime[64];
if((r = lstat(fname,&fstat)) < 0){
perror("lstat file failed ");
exit(1);
}
//get file type
if(S_ISREG(fstat.st_mode)){
printf( "%c" ,'-' ) ;
}else if (S_ISDIR(fstat.st_mode)){
printf ( "%c" ,'d' ) ;
}else if (S_ISLNK(fstat.st_mode)){
printf ( "%c",'l') ;
}else if (S_ISCHR(fstat.st_mode)){
printf ( "%c",'c') ;
}else if (S_ISFIFO(fstat.st_mode)){
printf ( "%c",'p') ;
}else if (S_ISSOCK(fstat.st_mode)){
printf ( "%c",'s') ;
}else if (S_ISBLK(fstat.st_mode)){
printf ( "%c",'b') ;
}else{
//unknown type
printf ( "%c",'?') ;
}
//get file rwx
char *t1 = "rwxrwxrwx";
for (i=8; i>= 0; i--){
if (fstat.st_mode & (1 << i)) //print rwx
printf("%c", t1[8-i]);
else
printf("%c", '-');
}
printf("%4d " , fstat.st_nlink); // link count
//uid
struct passwd *user;
user = getpwuid(fstat.st_uid);
printf("%s\t",user->pw_name);
// gid
struct group *gdata;
gdata = getgrgid(fstat.st_gid);
printf("%s ", gdata->gr_name);
printf("%8d " , fstat.st_size) ; // file size
// print time
//strcpy(ftime,ctime(fstat.st_ctime)) ; //print time in calendar form
//ftime[strlen(ftime)-1] = 0; // kill in at end
//printf ("%s ",ftime); //print name
//printf ("%s",basename(fname)) ; // print file basename
struct tm *block;
block = localtime(&fstat.st_mtime);
printf("%4d-%02d-%02d %02d:%02d ",
block->tm_year+1900, block->tm_mon+1, block->tm_mday,
block->tm_hour, block->tm_min);
//print name
printf("%s",basename(fname));
// print -> linkname if symbolic file
if (S_ISLNK(fstat.st_mode)){
char buf[100];
int result = readlink(fname,buf,100-1);
if (result < 0){
perror("readlink ");
exit(1);
}
printf(" -> %s",buf); //print link file content
}
printf("\n");
}
int ls_dir(char *dname){
//use opendir( ), readdir( ) ; then ca11 1s_file(name)
DIR *dp;
struct dirent *sdp;
dp = opendir(dname);
if(dp==NULL){
perror("opendir failed ");
exit(1);
}
int len = strlen(dname);
while( (sdp = readdir(dp)) != NULL){
if(sdp->d_name[0]=='.'){
continue;
}
char filename[1024];
int i;
for(i=0;i<len;i++){
filename[i] = dname[i];
}
filename[len] = '/';
len++;
for(i=0;sdp->d_name[i]!='\0';i++){
filename[i+len]=sdp->d_name[i];
}
filename[i+len]='\0';
len--;
ls_file(filename);
}
closedir(dp);
}
int main (int argc, char *argv[]){
//get file status
struct stat sbuf;
int r;
char cwd[1024];
getcwd(cwd,1023);
char *route = strcat(cwd,"/");
if(argc == 2){
route = strcat(cwd,argv[1]);
}
r = lstat(route,&sbuf);
if(r<0){
perror("lstat ");
exit(1);
}
//
if(S_ISDIR(sbuf.st_mode)){
ls_dir(route);
}else{
ls_file(route);
}
return 0;
}