实验2 linux文件系统
一、实验目的
1.理解文件系统的基本概念;
2.掌握文件系统的基本操作的实现;
3.能够利用利用文件系统的操作对实际问题进行分析建模,利用计算机求解。
二、实验预备知识
1.复习C/C++语言相关知识(如:数组的和结构体定义和使用),格式化输出等;
三、实验内容
实验一
1.创建文件file1,写入字符串“abcdefghijklmn”;
2.创建文件file2,写入字符串“ABCDEFGHIJKLMN”;
3.读取file1中的内容,写入file2,使file2中的字符串内容为“ ABCDEFGHIJKLMNabcdefghijklmn”
利用Linux进行C程序开发,首先需要了解程序要求,理清思路。
按照要求,我们需要用open创建两个文件file1和file2.利用write将字符串“abcdefghijklmn”写入file1中,同理创建file2,将“ABCDEFGHIJKLMN”写入file2;最后结合lseek函数,将file中的字符串读出,写入file2;
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int main()
{
int fd1,fd2;
char str[14];
fd1 = open("file1",O_RDWR|O_CREAT,S_IRWXU);
if(fd1 < 0)
perror("open");
write(fd1,"abcdefghijklmn",14);
lseek(fd1,0,SEEK_SET);
fd2 = open("file2",O_RDWR|O_CREAT,S_IRWXU);
if(fd2 < 0)
perror("open");
lseek(fd2,14,SEEK_END);
write(fd2,"ABCDEFGHIJKLMN",14);
read(fd1,str,14);
lseek(fd2,0,SEEK_SET);
write(fd2,str,14);
close(fd1);
close(fd2);
system("cat file2");
printf("\n");
system("rm -f file1 file2");
return 0;
}
通过阅读代码我们可以发现,在创建file2之后,我用了lseek函数,并且将偏移量设为14,这样做的目的是增大file2的文件大小,否则会产生文件覆盖,写入的小写字符串将大写字符串覆盖,导致实验失败。
利用lseek函数”扩充“文件时,应格外注意一点:lseek函数”扩充“文件后,并不能直接使得文件大小改变,需要在下一个写操作之后才能使文件变大。即第23句和第24句的位置不能互换,否则文件内容会发生覆盖。
system函数内的语句会被终端执行。
实验二
编写代码,完成以下功能:
1.创建新文件,该文件具有用户读写权限。
2.采用dup/dup2/fcntl复制一个新的文件描述符,通过新文件描述符向文件写入“class_name”字符串;
3.通过原有的文件描述符读取文件中的内容,并且打印显示;
由题目可知,我们需要创建一个文件file,然后用dup/dup2/fcntl中的任意一种方式,复制file的文件描述符,通过新的文件描述符将自己的班级名写入file中,最后通过原来的文件描述符读取文件信息。
通过对文件描述符的相关知识的了解,我们知道一个文件描述符只能对应一个文件,而多个文件描述符可以指向同一个文件。
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
int fd,fd1;
char *str = "yidongyiban";
fd = open("file",O_CREAT|O_RDWR);
if(fd < 0)
perror("open");
fd1 = dup(fd);
if(fd1 < 0)
perror("dup");
write(fd1,str,strlen(str));
lseek(fd,0,SEEK_SET);
char buf[12];
read(fd,buf,12);
printf("The buf is:%s\n",buf);
close(fd);
close(fd1);
system("rm -f file");
return 0;
}
实验三
编写程序实现以下功能:
1.输入文件名称,能够判断文件类型,判断实际用户对该文件具有哪些存取权限;
2.要求打印出文件类型信息,inode节点编号,链接数目,用户id,组id,文件大小信息;
3.修改文件的权限为当前用户读写,组内用户读写,组外用户无权限。
首先,在编写程序之前,我们需要了解如何给程序输入文件名称。利用scanf函数当然能够实现,但是在C语言中,还有一个更加方便的方法能够为程序传递所需参数。
在以前的学习中,我们可能认为main函数是没有参数的,因为main的写法是int main(),但是main函数是有且只有两个参数的。
C语言规定main函数的参数为argc和argv。其中,argc代表参数的个数,必须为int型;argv[]储存了函数的参数,必须为字符串类型的指针数组。因此,main函数的函数头可以写为:main(int argc,char *argv[])或main(int argc,char **argv)。
其中,argv[0]中储存的是程序的全名,argv[1]为第一个参数,argv[2]为第二个参数…
接下来,一个简单的程序能帮助理解:
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
for(int i = 0; i < argc; i++)
{
printf("The argv[%d] is:%s\n ",i,argv[i]);
}
return 0;
}
程序运行结果:
通过这个简单的程序,能够很清楚的看出argc和argv[]的含义
我们可以通过argv[]文件名传递到程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(int argc,char *argv[])
{
if(argc < 2)
{
printf("Please input the filename!\n");
return 0;
}
struct stat statbuf;
int i;
for(i = 1;i < argc;i++)
{
if(lstat(argv[i],&statbuf) < 0)
perror("lstat");
char *buf;
if(S_ISREG(statbuf.st_mode))
buf = "Regular file!";
else if(S_ISDIR(statbuf.st_mode))
buf = "Directory file!";
else if(S_ISCHR(statbuf.st_mode))
buf = "Char file!";
else
buf = "Other file!";
printf("The %s is:%s\n",argv[i],buf);
printf("The %s mode is:%d\n",argv[i],statbuf.st_mode);
printf("The %s inode is:%d\n",argv[i],statbuf.st_ino);
printf("The %s uid is:%d\n",argv[i],statbuf.st_uid);
printf("The %s gid is:%d\n",argv[i],statbuf.st_gid);
printf("The %s size is:%d\n",argv[i],statbuf.st_size);
printf("The %s link num is:%d\n",argv[i],statbuf.st_nlink);
}
for(i = 1;i < argc;i++)
{
if(access(argv[i],R_OK))
printf("The user can read the %s\n",argv[1]);
else if(access(argv[i],W_OK))
printf("The user can write the %s\n",argv[1]);
else if(access(argv[i],X_OK))
printf("The user can read and write the %s\n",argv[1]);
}
for(i = 1;i < argc;i++)
{
if(chmod(argv[i],0660) < 0)
perror("chmod");
else
printf("The file.mode chmod successful!\n");
}
return 0;
}
可能有人注意到了我在修改文件权限时用到了0660这样的一串数字,那么它的含义是什么呢?
0xxxx是文件权限的另一种表示方法,一般Linux文件或目录权限分为三个,当前用户,组内用户和组外用户。每个都有三个权限rwx,即读,写,执行权限。
权限的表示方法有两种,一是直观法,即直接用rwx表示,另外一种是二进制数值法,如:644,755等。读是4,写是2,执行是1,三个相加得7,以此类推,如果是6,则表示读,写,没有执行权限。
当运行程序但并没有传入参数时,程序退出并提示输入文件名
实验四
编写程序实现以下功能:
1.新建文件,设置文件权限屏蔽字为0;
2.建立该文件的硬链接文件,打印硬链接文件的inode节点号和文件大小;
3.建立该文件的软链接文件,打印软链接文件的inode节点号和文件大小;打印软链接文件中的内容;
4.打印源文件的inode节点号,文件大小和链接数目;
5.调用unlink对源文件进行操作,打印源文件链接数目;
在进行程序编写之前,我们应当了解文件的软链接和硬链接的含义及两者的区别。
1.软链接,以路径的形式存在。类似于Windows操作系统中的快捷方式
2.软链接可以 跨文件系统 ,硬链接不可以
3.软链接可以对一个不存在的文件名进行链接
4.软链接可以对目录进行链接
软链接文件存放的内容是原文件的路径名。
硬链接:硬链接文件是对原文件的文件名
Linux下的每个文件都具有各自的权限位,用户要想对文件进行某种操作,就必须具有相应权限。Linux下的文件权限位如下表所示:
st_mode 含义
S_IRUSR 当前用户读(0400)
S_IWUSR 当前用户写(0200)
S_IXUSR 当前用户执行(0100)
S_IRWXU 当前用户读、写、执行(0700)
S_IRGRP 组内用户读(0040)
S_IWGRP 组内用户写(0020)
S_IXGRP 组内用户执行(0010)
S_IRWXG 组内用户读、写、执行(0070)
S_IROTH 组外用户读(0004)
S_IWOTH 组外用户写(0002)
S_IXOTH 组外用户执行(0001)
S_IRWXO 组外用户读、写、执行(0007)
当用户创建一个文件时,需要为新的文件指定一个权限字,在实际应用中,系统有时不希望用户设置所有的权限。因此可以通过umask函数设置权限屏蔽字,直接将不需要用户干预的文件权限进行屏蔽,这样即使用户设置了相关文件权限位,系统也会将其忽略,仍然将其设为0;
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
umask(0);
struct stat statbuf;
int fd = open("file",O_CREAT|O_RDWR);
if(fd < 0)
perror("open");
char *str = "hello world";
if(write(fd,str,strlen(str)) < 0)
perror("write");
link("./file","./hard_link");
if(lstat("hard_link",&statbuf) < 0)
perror("lstat");
printf("The hard_link's inode is: %d\n",statbuf.st_ino);
printf("The hard_link's size is: %d\n",statbuf.st_size);
symlink("file","sort_link");
if(lstat("sort_link",&statbuf) < 0)
perror("lstat");
printf("The sort_link's inode is: %d\n",statbuf.st_ino);
printf("The sort_link's size is: %d\n",statbuf.st_size);
char buf[4];
readlink("sort_link",buf,4);
printf("The sort_link is: %s\n",buf);
if(lstat("file",&statbuf) < 0)
perror("lstat");
printf("The file's inode is: %d\n",statbuf.st_ino);
printf("The file's size is: %d\n",statbuf.st_size);
printf("The frist linknum is: %d\n",statbuf.st_nlink);
unlink("file");
if(lstat("file",&statbuf) < 0)
perror("lstat");
printf("The second linknum is: %d\n",statbuf.st_nlink);
close(fd);
return 0;
}
程序运行结果:
我们可以看到,在第二次利用lstat获取file的信息时,提示没有file,这正是进行ulink的结果。
ulink函数的作用:删除目录相并减少一个连接数,如果链接数为0并且没有任何进程打开该文件,该文件内容才能被真正删除,但是若又进程打开了该文件,则文件暂时不删除直到所有打开该文件的进程都结束时文件才能被删除。
因此,运行结果中显示的第二个linknum仍然是第一个的值。
实验五
1.新建/home/user目录;
2.把当前工作路径移至/home/user目录;
3.打印当前工作路径;
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
char str[128];
if(getcwd(str,128) < 0)
perror("getwcd");
else
printf("The workdir is:%s\n",str);
if(mkdir("/home/user",0666) < 0)
perror("mkdir");
else
printf("The dir create successfully!\n");
if(chdir("/home/user") < 0)
perror("chdir");
else
{
getcwd(str,128);
printf("The workdir is:%s\n",str);
}
rmdir("/home/user");
return 0;
}
虽然程序已经编写完毕,但是我们在运行程序时却会遇到问题:
权限不够
我们可以先切换到root用执行该程序,就能成功创建。
实验六
编写程序完成以下功能:
1.递归遍历/home目录,打印出所有文件和子目录名称及节点号。
2.判断文件类型,如果是子目录,继续进行递归遍历,直到遍历完所有子目录为止。
所谓递归,就函数自己调用自己,直到完成函数终止条件,跳出递归函数。
要想递归遍历/home目录,就必须确定合适的迭代条件。既然要遍历/home目录,那就要将所有的文件夹都访问到。因此,只要是/home目录下的文件夹,都要被访问,因此,我们以打开文件的结果作为迭代条件,只要打开的是目录文件,就能够进入递归函数。不仅如此,要想打开正确的目录,就必须获得目录的路径,因此,我们会用到spritnf函数来储存文件路径。
spritf函数的作用和printf的作用相似,只不过printf是将字符串输出到屏幕,而sprintf函数是将字符串输出到字符串中。
函数示例如下
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *str = "hello";
char *buf = "world";
char s[128];
sprintf(s,"%s%s",str,buf);
printf("The s is: %s\n",s);
return 0;
}
程序运行结果为:
利用sprintf函数,我们就能将文件的绝对路径写入字符串中,通过字符串打开文件
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
void show(char *path)
{
DIR *dir;
char str[128];
struct dirent *dirp;
struct stat statbuf;
dir = opendir(path);
if(dir)
{
while((dirp = readdir(dir)) != NULL)
{
sprintf(str,"%s/%s",path,dirp->d_name);
if(lstat(str,&statbuf) < 0)
perror("lstat");
if(dirp->d_name[0] == '.')
continue;
if(S_ISDIR(statbuf.st_mode))
{
show(str);
printf("The dirent's name is: %s\n",dirp->d_name);
printf("The dirent's inode is: %d\n",dirp->d_ino);
}
else
{
printf("The file's name is: %s\n",dirp->d_name);
printf("The file's inode is: %d\n",dirp->d_ino);
}
}
}
else
perror("opendir");
closedir(dir);
}
int main()
{
show("/home");
return 0;
}
实验完成