1、文件与其元数据
Linux获取文件状态的时候会把其相关的元数据放在stat结构中,如下:
struct stat {
dev_t st_dev; //文件设备节点
ino_t st_ino; //ino
mode_t st_mode; //文件模式
nlink_t st_nlink; //硬链接数目
uid_t st_uid; //用户ID
gid_t st_gid; //组ID
dev_t st_rdev; //如果是设备,设备编号
off_t st_size; //大小,字节
blksize_t st_blksize; //有效率文件I/O的首选的块大小
blkcnt_t st_blocks; //文件系统块的数目
time_t st_atime; //最近访问时间
time_t st_mtime; //最近修改时间
time_t st_ctime; //最近变更时间
};
下面是通过stat调用获得文件信息,eg:
int main(){
int ret, i;
struct stat sb;
ret = stat("test", &sb);
if(ret == -1)
printf("stat error.\n");
printf("test is %ld bytes.\n", sb.st_size);
return 0;
}
fstat和stat的不同之处是调用的时候参数是文件描述符,下面程序判断文件是否位于一个物理设备,eg:
int main(){
int ret, fd;
struct stat sb;
fd = open("test", O_RDWR);
ret = fstat(fd, &sb);
if(ret == -1)
printf("stat error.\n");
if(gnu_dev_major(sb.st_dev))
printf("test is in physical device.\n");
return 0;
}
可以通过chmod(fchmod)设置文件所属的用户,eg:
int main(){
int ret, x;
struct stat sp;
ret = stat("test", &sp);
printf("%d\n", x=sp.st_mode);
while(x > 0){
printf("%d", x%2);
x/=2;
}
printf("\n");
x = sp.st_mode|3;
ret = chmod("test", x);
if(ret == -1)
printf("chmod error.\n");
ret = stat("test", &sp);
printf("%d\n", x=sp.st_mode);
while(x > 0){
printf("%d", x%2);
x/=2;
}
printf("\n");
return 0;
}
以前没有写过mode中的各个位代表的是什么意思,其实也不需要知道。定义了一些常量来表示,如果要设定该属性只需要将mode与该值进行或运算就可以了,取消的话与该值的非进行与运算,下面列出这些值:
- S_IRWXU:拥有者可以读、写、运行。
- S_IRUSR:拥有者可以读。
- S_IWUSR:拥有者可以写。
- S_IXUSR:拥有者可以运行。
- S_IRWXG:组可以读、写、运行。
- S_IRGRP:组可以读。
- S_IWGRP:组可以写。
- S_IXGRP:组可以运行。
- S_IRWXO:其他人可以读、写、运行。
- S_IROTH:其他人可以读。
- S_IWOTH:其他人可以写。
- S_IXOTH:其他人可以运行。
在调用chown可以修改文件的所有者,需要注意的是,如果哦path指向的是一个链接,那么chown会跟随链接改变目标的所有权,而lchown则只是变更符号链接本身的所有权,可能发生错误的原因也是非常地多,可以更具errno来判断,eg:
int main(){
int ret, x;
struct stat sp;
ret = chown("test", 1000, 1000);
if(ret == -1){
printf("chown error.\n");
}
return 0;
}
2、扩展属性
扩展属性(xattrs)提供了一个机制用来将《键/值》对永久地关联到文件,让现有的文件系统得以支持在原始设计中未提供的功能。扩展属性是文件系统不可知论者,应用程序可以通过一个标准的接口来操纵他们,此接口不因文件系统而异。每个扩展属性可以通过唯一的键来区分,键的内容必须是有效的UTF-8,格式为namespace.attribute,每个键采用完全限定的形式。
需要注意的是这里的值可以是任意字节的数组,未必是字符存,而且最后可能不是null,这样在访问的时候必须知道值的大小。在设置的时候当然也就需要设置值大小。
一个扩展属性的用处:GUI的文件管理程序的行为根据文件类型而异。要判断文件的格式,Winodws之类的操作系统仅需要查看文件的扩展名就可以了,而Unix系统往往需要查看文件的内容来判断类型。有些文件管理程序会直接产生此信息,有些则会将产生的信息缓存起来以备下次使用。一个更好的做法就是将此类元数据存入扩展属性。
Linux下定义的4种扩展属性命名空间:
- system:用于实现利用扩展属性的内核功能,例如访问控制表。eg:system.posix_acl_access便是位于此用户空间的扩展属性,用户是否可以读取或写入这些属性取决于所使用的安全模块。
- security:用于实现安全模块。
- trusted:把受限制的信息存入用户空间。
- user:一般进程所使用的标准命名空间,经过一般文件权限位来控制此命名空间的访问。
下面给出set、get、remove、list四个操作的一个简单的例子:
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/xattr.h>
#include <sys/types.h>
void testset(){
char key[7] = {'u','s','e','r','.','#','\0'};
char value[2] = {'#','\0'};
int i, ret;
for(int i = 0; i < 10; i++){
key[5] = value[0] = '0'+i;
ret = setxattr("test", key, value, 2, 0);
}
}
void testlist(){
char buf[1000];
int ret, i=0, j = 0;
printf("The key on test are:\n");
ret = listxattr("test", buf, 1000);
while(i < ret){
printf("%s\n", buf+i);
i += strlen(buf+i);
i++;
}
}
void testremove(){
char key[7] = "user.2";
int ret;
ret = removexattr("test", key);
printf("%d\n", ret);
}
void testget(){
char key[7] = "user.#";
char value[3];
int ret, i;
printf("The <key,value> on test are:\n");
for(i = 0; i < 10; i++){
key[5] = '0'+i;
ret = getxattr("test", key, value, 3);
if(ret != -1)
printf("<%s,%s>\n", key, value);
}
}
int main(){
testset();
testlist();
testremove();
testget();
return 0;
}
3、目录
每个进程都有一个当前目录,该目录最初继承自父进程,当前工作目录是内核用于解析相对路径名称的起点。下面的代码可用与获得当前的工作目录,eg:
int main(){
char buf[20];
if(getcwd(buf, 20)){
printf("%s\n", buf);
}
char *cwd = get_current_dir_name();
printf("%s\n", cwd);
return 0;
}
当一个用户首次登录她的系统,login进程会根据/etc/passwd文件中设定将她的当前工作目录设定为她的home目录,然而有些时候进程会想变更它的当前工作目录,为此系统提供两个系统调用:
int main(){
int fd, ret;
char *cwd;
ret = chdir("/");
if(ret != -1){
printf("%s\n", get_current_dir_name());
}
fd = open("/home/ggzwtj/", O_DIRECTORY);
ret = fchdir(fd);
if(ret != -1){
printf("%s\n", get_current_dir_name());
}
return 0;
}
创建删除目录的系统调用,eg:
int main(){
int fd, ret;
ret = mkdir("testdir", O_RDWR);
if(ret != -1)
printf("创建目录成功!\n");
ret = rmdir("testdir");
if(ret != -1)
printf("删除目录成功!\n");
return 0;
}
读目录:
int main(){
int ret;
DIR *dir;
struct dirent *entry;
dir = opendir("/home/ggzwtj/xx");
while((entry = readdir(dir)) != NULL){
printf("%s\n", entry->d_name);
}
closedir(dir);
return 0;
}
4、链接
硬链接和软链接的区别就不说了,下面进入正题,创建硬链接。eg:
int main(){
int ret;
ret = link("test", "testlink");
if(ret == -1)
printf("link error.\n");
return 0;
}
创建软链接。eg:
int main(){
int ret;
ret = symlink("test", "testlink");
if(ret == -1)
printf("link error.\n");
return 0;
}
解除链接。eg:
int main(){
int ret;
ret = link("test", "testlink");
if(ret == -1)
printf("link error.\n");
unlink("testlink");
return 0;
}
5、文件的复制、移动
在文件系统看来,拷贝文件就是复制一份文件内容,然后在新的目录下创建该拷贝的硬链接。而移动文件的代价就小的多,只是对文件的目录项更名。虽然没有提供系统调用来专门地实现复制、移动,但是知道文件系统的实质操作后也就简单了。eg:
int main(){
int ret;
ret = rename("test", "/home/ggzwtj/test");
if(ret == -1)
printf("rename error.\n");
return 0;
}
需要注意是两个位置必须是在相同的文件系统中,还必须有访问权限。
6、设备节点
设备节点是一种特殊的文件,应用程序可以通过它链接到设备驱动程序。当一个应用程序在设备节点上进行普通的Unix I/O,内核会把此类请求传递给一个设备驱动程序,然后设备把处理结构返回给用户。下面是几个有意思的特殊文件:
- /dev/null,丢掉对该设备的所有写入,对于读请求总是返回eof。
- /dev/zero,丢掉所有写入请求,对读请求返回null字节的无穷流。
- /dev/full,对写请求触发ENOSPC,表示设备已满,对读请求返回无穷null。
- /dev/random,内核的随机数产生器。它收集杂乱的数据,把他们连接在一起并进行单向散列运算,所得到的结果放入一个熵池,内核不断估算熵的位数目。