• 文件和目录管理


    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与该值进行或运算就可以了,取消的话与该值的非进行与运算,下面列出这些值:

    1. S_IRWXU:拥有者可以读、写、运行。
    2. S_IRUSR:拥有者可以读。
    3. S_IWUSR:拥有者可以写。
    4. S_IXUSR:拥有者可以运行。
    5. S_IRWXG:组可以读、写、运行。
    6. S_IRGRP:组可以读。
    7. S_IWGRP:组可以写。
    8. S_IXGRP:组可以运行。
    9. S_IRWXO:其他人可以读、写、运行。
    10. S_IROTH:其他人可以读。
    11. S_IWOTH:其他人可以写。
    12. 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种扩展属性命名空间:

    1. system:用于实现利用扩展属性的内核功能,例如访问控制表。eg:system.posix_acl_access便是位于此用户空间的扩展属性,用户是否可以读取或写入这些属性取决于所使用的安全模块。
    2. security:用于实现安全模块。
    3. trusted:把受限制的信息存入用户空间。
    4. 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,内核会把此类请求传递给一个设备驱动程序,然后设备把处理结构返回给用户。下面是几个有意思的特殊文件:

    1. /dev/null,丢掉对该设备的所有写入,对于读请求总是返回eof。
    2. /dev/zero,丢掉所有写入请求,对读请求返回null字节的无穷流。
    3. /dev/full,对写请求触发ENOSPC,表示设备已满,对读请求返回无穷null。
    4. /dev/random,内核的随机数产生器。它收集杂乱的数据,把他们连接在一起并进行单向散列运算,所得到的结果放入一个熵池,内核不断估算熵的位数目。
  • 相关阅读:
    Linux命令全训练
    解决maven中静态资源只能放到properties中的问题
    Mybatis出现错误org.apache.ibatis.executor.ExecutorException: No constructor found in
    Fence Repair
    Saruman's Army
    Best Cow Line
    区间调度问题
    硬币问题
    迷宫最短路径
    Divide by Zero 2017 and Codeforces Round #399 (Div. 1 + Div. 2, combined) A. Oath of the Night's Watch
  • 原文地址:https://www.cnblogs.com/ggzwtj/p/2215379.html
Copyright © 2020-2023  润新知