课程目标
- 编程目标:
(a)实现ls –l
全部功能。
(b)实现tree
工具全部功能。 - 理解文件系统对磁盘文件的管理策略。
- 掌握文件属性获取与修改的API应用。
- 特殊文件的基本信息。
主要知识点
- VFS文件系统以及ext2/3/4文件系统结构。
- 文件信息存储
inode
以及文件类型,文件属性的获取。 - 文件属性的获取与修改。
- 编程应用。
1—Linux一切都是文件及文件系统
- 一切皆文件的概念
Linux一切皆为文件的思想是基于虚拟文件系统(Virtual File System,VFS)实现。把底层的硬件实现全部隐藏,用户操作普通文件或者其它的硬件设备,都基于文件系统的方式,即基本上都可以使用open/read/write/close
这些文件操作的系统调用来访问。便于应用层的程序开发。 - Linux下根据设备的属性将文件分为7类:
文件类型 | 类别 | 标识符 |
---|---|---|
普通文件 | 磁盘文件 | - |
目录文件 | 磁盘文件 | d |
链接文件 | 磁盘文件 | l |
字符设备文件 | 设备文件 | c |
块设备文件 | 设备文件 | b |
管道文件 | 进程通信文件 | p |
socket文件 | 网络通信文件 | s |
- 磁盘文件系统怎么来管理文件?
struct inode
结构体存储了文件的**基本属性,这个属性在内核中定义。
在终端中输入man 2 stat
查看stat相关信息:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);
以上三个函数可以获取某个文件的详细的属性,
可以通过 strut stat结构体查看
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */ 文件在当前文件系统下的唯一编号
mode_t st_mode; /* protection */ 存放的是文件的类型以及权限
nlink_t st_nlink; /* number of hard links */ 硬链接个数
uid_t st_uid; /* user ID of owner */ 拥有者ID
gid_t st_gid; /* group ID of owner */ 拥有组ID
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* total size, in bytes */ 文件的大小
blksize_t st_blksize; /* blocksize for file system I/O */ 块的大小
blkcnt_t st_blocks; /* number of 512B blocks allocated */ 块的个数
time_t st_atime; /* time of last access */ 最近访问时间
time_t st_mtime; /* time of last modification */ 最近修改熟悉的时间
time_t st_ctime; /* time of last status change */ 最近修改内容的时间
};
查看文件编号ls -li
2—文件类型及文件模式
mode_t st_mode; /* protection */ 存放的是文件的类型以及权限
3个bit的权限修饰位
- setuid位
- setgid位
以上两个bit被设置后,相应的执行这个文件时,会提升它的执行权限
以/usr/bin/passwd
可执行文件为例。用户的密码存储在/etc/passwd/shadow
文件中:
delphi@delphi-vm:~/code/test$ ll /etc/shadow
-rw-r----- 1 root shadow 1010 2014-08-26 21:53 /etc/shadow
从以上可以看出,普通用户对这个文件没有写的权限,因此,如果用vi编辑器修改肯定是不可以的。但是,普通用户可以使用passwd
这个命令来修改自己的密码,也就是修改了/etc/shadow
这个文件。原因时/usr/bin/passwd
这个可执行文件被设置了setuid
位。
delphi@delphi-vm:~/code/test$ ll /usr/bin/passwd
-rwsr-xr-x 1 root root 37100 2011-02-15 06:12 /usr/bin/passwd*
当普通用户执行这个程序时,因为设置了setuid
位,相应的这个进程对文件的访问权限上升到该可执行文件拥有者的权限,在这个地方就是普通用户执行时,对文件的读写的权限被提升为这个文件的拥有者root的权限,而root用户对/etc/shadow文件有写的权限,因此,可以修改密码成功。
- 粘贴位:在早期计算机,如果一个可执行文件被设置此位,期望这个可执行文件常驻内存,提高运行的速率
3—磁盘文件属性读取与修改编程
1. 权限的修改
man 2 chmod
#include <sys/stat.h>
int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);
man 2 umask
mode_t umask(mode_t mask);//修改和获取当前的umask值。默认0022
源码中对于权限的宏定义
#define S_IRWXU 00700 //拥有者读写执行的权限
#define S_IRUSR 00400 //拥有者读的权限
#define S_IWUSR 00200 //拥有者写的权限
#define S_IXUSR 00100 //拥有者执行的权限
#define S_IRWXG 00070
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010
#define S_IRWXO 00007
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001
新创建的文件的权限
如果使用以下权限新创建文件
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH
(octal 0666) when creating a new file, the permissions on the resultingfile will be:
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
(because 0666 & ~022 = 0644; i.e., rw-r--r--).
最终这个文件的权限:0666& ~022 = 0644,而~022就是umask值
- 默认新创建文件的权限:
0666&~umask
- 默认新创建的目录的权限:
0777&~umask
2.用户的信息
uid_t st_uid; /* user ID of owner */ 拥有者ID
gid_t st_gid; /* group ID of owner */ 拥有组ID
/etc/passwd
存储的是用户的ID以及相关信息。
/etc/group
存储的是组的ID以及相关信息。
通过 stat返回的是用户的ID,要获取它的用户名,实际是读取对应的文件。
#include <sys/types.h>
#include <pwd.h>
struct passwd *getpwnam(const char *name);
struct passwd *getpwuid(uid_t uid);
返回的是一个struct passwd指针:
struct passwd {
char *pw_name; /* username */
char *pw_passwd; /* user password */
uid_t pw_uid; /* user ID */
gid_t pw_gid; /* group ID */
char *pw_gecos; /* real name */
char *pw_dir; /* home directory */
char *pw_shell; /* shell program */
};
#include <sys/types.h>
#include <grp.h>
struct group *getgrnam(const char *name);
struct group *getgrgid(gid_t gid);
返回一个 struct group指针:
struct group {
char *gr_name; /* group name */
char *gr_passwd; /* group password */
gid_t gr_gid; /* group ID */
char **gr_mem; /* group members */
};
3. 相关时间
在struct stat
中时间分为以下三种:
time_t st_atime; /* time of last access */ 最近访问时间
time_t st_mtime; /* time of last modification */ 最近修改熟悉的时间
time_t st_ctime; /* time of last status change */ 最近修改内容的时间
查找man 2 utime
#include <sys/types.h>
#include <utime.h>
int utime(const char *filename, const struct utimbuf *times);
struct utimbuf {
time_t actime; /* access time */
time_t modtime; /* modification time */
};
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
4. 文件大小
文件的大小:可以把一个文件截短或扩展
- 截断:
truncate
#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);
如果length小于当前文件大小,截短;如果大于,依赖于系统,有些系统会扩展。
5. 链接问题
- 硬链接:用
ln -d
来创建,实际上时增加某个为你教案的属性中硬链接数值。
编程中用link()
函数实现
创建硬链接
NAME
link - make a new name for a file (为文件创建一个新名字即为硬链接)
SYNOPSIS
#include <unistd.h>
int link(const char *oldpath, const char *newpath);
========================
删除硬链接
NAME
unlink - delete a name and possibly the file it refers to
SYNOPSIS
#include <unistd.h>
int unlink(const char *pathname);
仅仅是将某个文件的硬链接数自动减1,
有时候用来删除文件是因为文件没有
创建硬链接时,硬链接数为1,而减去1
变为0,从而删除文件。
- 符号链接:类似于windows的快解方式类似,创建了一个新文件,只是不分配新数据空间,符号链接文件的大小为源文件路径字符串长度。
在控制台中创建符号链接:
delphi@delphi-vm:~/code/test$ ln -s copy copy.lk
delphi@delphi-vm:~/code/test$ ll copy copy.lk
-rwxrw-r-- 1 delphi delphi 1000 2018-08-07 12:20 copy*
lrwxrwxrwx 1 delphi delphi 4 2018-08-07 15:36 copy.lk -> copy*
在编程中创建符号链接:
创建符号链接:
NAME
symlink - make a new name for a file
SYNOPSIS
#include <unistd.h>
int symlink(const char *oldpath, const char *newpath);
读取符号链接:
NAME
readlink - read value of a symbolic link
SYNOPSIS
#include <unistd.h>
ssize_t readlink(const char *path, char *buf, size_t bufsiz);
默认情况下,读取符号链接的内容和属性时得到源文件的信息,
因此,针对符号链接有专门的函数:
stat ==> lstat
chmod ==> lchmod
6. 编程实现ls -l
-
参数列表问题
ls -l
后面可以跟一个或者多个文件和目录。要解决这个问题可以使用命令行参数解析的工具getopt/getopt_long
。
另外,遍历非-l
其他参数,有可能是目录,有可能是文件,是目录则读取目录下所有的子文件及子目录信息;如果是普通文件,列出文件信息;如果没有指定参数,则列出当前目录下的文件信息。
怎么来判断文件是文件还是目录呢?**lstat()
函数能够获取给定的路径类型。 -
信息的问题
网友评论