Linux文件的基本操作相关函数

1、Linux虚拟地址空间

  • Linux可执行文件格式为ELF,通过file xxxx可以查看文件类型
  • 虚拟地址空间在32位系统中为4G,每个应用程序都有独立的4G
  • 地址位从高到低分别为:
    • 3G ~ 4G:Linux Kernel,内存管理、进程管理、设备驱动、VFS虚拟文件系统
    • 0G ~ 3G
    • 环境变量
    • 命令行参数
    • 栈空间
    • 共享库
    • 堆空间
    • ELF
    • 未初始化的全局变量
      • 已初始化的全局变量
      • 代码段、二进制机器指令
      • 受保护的地址(0 ~ 4K)

2、Linux文件基本组成

  • denty(目录项),一个目录项相当于一个硬连接
    • 文件名
    • inode编号
  • iNode
    • struct stat
    • 大小、权限、类型、所有者、组、
    • 存储指针地址—->数据块——>磁盘

1、open函数

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
const char* file_path = "file.txt";
int main(){
    // 1、打开文件,以读写的方式
    int fd = open(file_path, O_RDWR);
    // 1.1、如果打开失败,则创建文件,权限644
    if (fd == -1){
    // 创建出来的文件实际权限是当前指定权限与本地掩码取反后的与运算
    // umask可以查看或修改本地掩码,一般是0002
    fd = open(file_path, O_RDWR | O_CREAT, 0644);
    }
    // 1.2、如果创建失败,则退出程序
    if (fd == -1){
    perror("Create file");
           exit(0);
    }else{
    // 2、打开文件后的操作
        printf("OK!\n");
    }
    // 3、操作结束后关闭文件
    int ret = close(fd);
    // 3.1、关闭失败后给出错误提示
    if (ret == -1){
        perror("Close file");
    }else{
        printf("Closed file success!!\n");
    }
    return 0;
}

2、read&write基本操作

// close需要引入的头文件
#include <unistd.h>

// open函数需要导入的三个头文件
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

// 标准的C语言头文件
#include <stdlib.h>
#include <stdio.h>

const char* file_r = "file.txt";
const char* file_w = "write.txt";
int main(){
    // 打开一个已存在的文件
    int fd_r = open(file_r, O_RDONLY);
    if (fd_r == -1){
        perror("Open file");
        exit(1);
    }
    // 创建一个新文件,准备写入
    int fd_w = open(file_w, O_CREAT | O_WRONLY, 0664);
    if (fd_w == -1){
        // 如果open失败,函数会修改errno全局变量值,而perror会读取该全局变量的值
        perror("Create file");
        exit(1);
    }
    // 读取与写入,每次读取10个字节,sizeof(buf)是10
    // 如果是char* buf[10]的话,sizeof(buf)是80
    char buf[100] = {0};
    int count = read(fd_r, buf, sizeof(buf));
    if (count == -1){
        perror("Read file");
        exit(1);
    }
    while(count){
        int ret = write(fd_w, buf, count);
        printf("Write bytes %d\n", ret);
        count = read(fd_r, buf, sizeof(buf));
    }
    close(fd_r);
    close(fd_w);

    return 0;
}

3、lseek函数的使用

#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(){
    const char* file = "lseek.txt";
    int fd = open(file, O_RDWR);
    if (fd == -1){
        perror("Open file");
        exit(1);
    }
    // off_t lseek(int fd, off_t offset, int whence);
    // whence的值有SEEK_SET、SEEK_CUR、SEEK_END
    // 从尾部移动0个位置,返回的偏移量即为文件当前总长度
    int ret = lseek(fd, 0, SEEK_END);
    printf("file length = %d\n", ret);

    // 文件拓展,从尾部移动2000个位置
    ret = lseek(fd, 2000, SEEK_END);
    printf("return value : %d\n", ret);

    // 在当前位置写入内容,文件被拓展,如果不写入,不会改变当前文件大小
    // 写入内容后,中间的偏移会被填充
    write(fd, "", 1);
    close(fd);
    return 0;
}

4、stat函数的使用

  • stat是Linux下的命令,后面可以直接跟文件名,如:stat a.out
    parallels@ubuntu:~/Linux/fopen$ stat a.out
    File: a.out
    Size: 8560          Blocks: 24         IO Block: 4096   regular file
    Device: 801h/2049d    Inode: 2498221     Links: 1
    Access: (0775/-rwxrwxr-x)  Uid: ( 1000/parallels)   Gid: ( 1000/parallels)
    Access: 2019-01-04 16:08:45.366586892 +0800
    Modify: 2019-01-04 16:08:44.438516020 +0800
    Change: 2019-01-04 16:08:44.438516020 +0800
    Birth: -
    
  • stat是一个结构体
    struct stat {
      dev_t     st_dev;         /* ID of device containing file */
      ino_t     st_ino;         /* Inode number */
      mode_t    st_mode;        /* File type and mode */
      nlink_t   st_nlink;       /* Number of hard links */
      uid_t     st_uid;         /* User ID of owner */
      gid_t     st_gid;         /* Group ID of owner */
      dev_t     st_rdev;        /* Device ID (if special file) */
      off_t     st_size;        /* Total size, in bytes */
      blksize_t st_blksize;     /* Block size for filesystem I/O */
      blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */
    
      /* Since Linux 2.6, the kernel supports nanosecond
      precision for the following timestamp fields.
      For the details before Linux 2.6, see NOTES. */
    
      struct timespec st_atim;  /* Time of last access */
      struct timespec st_mtim;  /* Time of last modification */
      struct timespec st_ctim;  /* Time of last status change */
    
      #define st_atime st_atim.tv_sec      /* Backward compatibility */
      #define st_mtime st_mtim.tv_sec
      #define st_ctime st_ctim.tv_sec
    };
    
  • stat是一个函数

  • lstat与stat参数相同,不同的是lstat不穿透,对当前文件操作

    “`c
    #include <sys/types.h>

    <h1>include <sys/stat.h></h1>

    #include <unistd.h></p></li>
    </ul>

    <p>int stat(const char *pathname, struct stat *statbuf);
    int fstat(int fd, struct stat *statbuf);
    // lstat与stat的区别在于,lstat不追踪软链接,直接作用于当前文件
    int lstat(const char *pathname, struct stat *statbuf);

    <pre><code class="line-numbers">* stat函数的简单应用

    “`c
    #include <stdlib.h>
    #include <stdio.h>

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>

    int main(int argc, char* argv[]){
    if (argc < 2){
    printf(“./a.out filename\n”);
    exit(1);
    }
    struct stat statbuf;
    int ret = stat(argv[1], &statbuf);
    if (ret == -1){
    perror(“stat”);
    exit(1);
    }
    mode_t mode = statbuf.st_mode;
    printf(“mode: %d\n”, mode);
    printf(“文件类型:%o\n”, mode & 0170000);
    printf(“文件所有者权限:%o\n”, mode & 0700);
    printf(“设备ID:%lo\n”, statbuf.st_dev);
    printf(“INode:%ld\n”, statbuf.st_ino);
    printf(“Links:%ld\n”, statbuf.st_nlink);
    printf(“Uid:%d\n”, statbuf.st_uid);
    printf(“Gid:%d\n”, statbuf.st_gid);
    printf(“Size:%ld\n”, statbuf.st_size);
    return 0;
    }
    “`

    #### 5、access函数

    * 判断文件是否拥有某个权限

    * man 2 access查看函数说明

    “`c
    #include <unistd.h>

    #include <stdio.h>
    #include <stdlib.h>

    int main(int argc, char* argv[]){
    if (argc < 2){
    printf(“./a.out filename\n”);
    exit(1);
    }
    int ret = access(argv[1], W_OK);
    if (ret == -1){
    perror(“Access”);
    exit(1);
    }
    printf(“you can write this file \n”);
    return 0;

    }
    “`

    #### 6、chmod函数

    “`c
    #include <sys/stat.h>
    #include <stdio.h>
    #include <stdlib.h>

    int main(int argc, char* argv[]){
    if (argc < 2){
    printf(“./a.out filename\n”);
    exit(1);
    }
    int ret = chmod(argv[1], 0664);
    if (ret == -1){
    perror(“chmod”);
    exit(1);
    }
    printf(“Success!!\n”);
    return 0;
    }

    • 权限也可以通过参数传进来,使用strtol( )函数将字符串转成权限参数的8进制值
      #include <sys/stat.h>
      #include <stdio.h>
      #include <stdlib.h>
      
      int main(int argc, char* argv[]){
        if (argc < 3){
            printf("e.g: ./a.out filename 777\n");
            exit(1);
        }
        long mode = strtol(argv[2], NULL, 8);
        int ret = chmod(argv[1], mode);
        if (ret == -1){
            perror("chmod");
            exit(1);
        }
        printf("Success!!\n");
        return 0;
      }
      

    7、chown函数

    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char* argv[]){
        if (argc < 4){
            printf("e.g: ./a.out filepath user group\n");
            exit(1);
        }
        int ret = chown(argv[1], atoi(argv[2]), atoi(argv[3]));
        if (ret == -1){
            perror("chown");
            exit(1);
        }
        printf("Success!!\n");
        return 0;
    }
    

    8、truncate函数使用

    • (truncate英文翻译是“截断”的意思,超出部分空洞填充)

    • Linux本身有truncate命令

      • truncate xxx —size 1000
      #include <sys/types.h>
      #include <unistd.h>
      #include <stdio.h>
      #include <stdlib.h>
      
      int main(int argc, char* argv[]){
        if (argc < 3){
            printf("./a.out filename n\n");
            exit(1);
        }
        int ret = truncate(argv[1], atoi(argv[2]));
        if (ret == -1){
            perror("truncate");
            exit(1);
        }
        printf("Success!!\n");
        return 0;
      }
      

    9、link函数使用

    • Linux本身有link命令
      • link filename linkname // 硬链接
      • ln filename linkname // 硬链接
      • ln -s filename linkname // 软链接,快捷方式
      #include <unistd.h>
      #include <stdio.h>
      #include <stdlib.h>
      
      int main(int argc, char* argv[]){
        if (argc < 3){
            printf("e.g: ./a.out filename linkname\n");
            exit(1);
        }
        int ret = link(argv[1], argv[2]);
        if (ret == -1){
            perror("link");
            exit(1);
        }
        printf("Success!!\n");
        return 0;
      }
      

    10、symlink函数的使用

    • 软链接,即ln -s
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char* argv[]){
        if (argc < 3){
            printf("./a.out filename symlink\n");
            exit(1);
        }
        int ret = symlink(argv[1], argv[2]);
        if (ret == -1){
            perror("symlink");
            exit(1);
        }
        printf("Success!!!\n");
        return 0;
    }
    

    11、readlink函数

    • 对应的Linux命令为readlink
      • readlink linkname
      #include <unistd.h>
      #include <stdio.h>
      #include <stdlib.h>
      
      int main(int argc, char* argv[]){
        if (argc < 2){
            printf("e.g: ./a.out linkpath\n");
            exit(1);
        }
        char buf[512];
        int ret = readlink(argv[1], buf, sizeof(buf));
        if (ret == -1){
            perror("radlink");
            exit(1);
        }
        // 返回ret即为link路径的长度,在最后面加结束字符
        buf[ret] = '\0';
        printf("%s\n", buf);
        return 0;
      }
      

    12、unlink函数

    • 对应的Linlux命令为unlink
      • unlink filename
    • unlink命令会删除当前文件及链接数,但如果当前文件处于打开状态,则该文件的实际删除会在关闭后执行,因此可以在程序中做为临时文件操作,open后执行一次unlink操作,close后清除
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(){
        // 打开临时文件(实现上会在磁盘中创建一个新文件)
        int fd = open("tempfile", O_CREAT | O_RDWR, 0664);
        if (fd == -1){
            perror("open");
            exit(1);
        }
        // 删除临时文件,在文件被关闭后彻底删除,不影响当前文件操作
        unlink("tempfile");
    
        // 写入文件数据
        const char* txt = "Hello Linux!!!\n";
        int len = strlen(txt);
        write(fd, txt, len);
    
        // 重置文件操作指针
        lseek(fd, 0, SEEK_SET);
    
        // 读取文件内容
        char buf[512] = {0};
        len = read(fd, buf, len);
    
        // 将读取出来的内容写到屏幕上
        // 文件描述符表中的文件描述符0:stdin输入流、1:stdout输出流、2:stderr
        write(1, buf, len);
    
        // 关闭文件,关闭后文件将被彻底删除
        close(fd);
        return 0;
    }
    

    12、rename函数

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char* argv[]){
        if (argc < 3){
            printf("e.g: ./a.out filenam e newname\n");
            exit(1);
        }
        int ret = rename(argv[1], argv[2]);
        if (ret == -1){
            perror("rename");
            exit(1);
        }
        printf("%s is renamed to %s\n", argv[1], argv[2]);
        return 0;
    }
    

    13、chdir、getcwd函数

    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char* argv[]){
        if (argc < 2){
            printf("e.g: ./a.out path\n");
            exit(1);
        }
        int ret = chdir(argv[1]);
        if (ret == -1){
            perror("chdir");
            exit(1);
        }
        char buf[512] = {0};
        char* path = getcwd(buf, sizeof(buf));
        printf("Current path:%s\n", path);
        return 0;
    }
    

    14、mkdir、rmdir、opendir、readdir、closedir

    #include <stdio.h>
    #include <stdlib.h>
    #include <dirent.h>
    #include <string.h>
    
    void print_dir(const char* path){
        // 1、打开目录
        DIR* dir = opendir(path);
        if (dir == NULL){
            perror("opendir");
            exit(1);
        }
        // 2、读取目录
        struct dirent* dirent = readdir(dir);
        // 循环读取,函数会自动寻找下一个,如果没有则返回空
        // 配合循环中最后一句使用,否则是死循环
        while(dirent){
            // 获取当前元素的名称(可能是目录、文件、链接、管道等)
            char* subpath = dirent->d_name;
            // 获取当前元素类型
            int d_type = dirent->d_type;
            // 过滤名称为.或..的目录
            if (strcmp(subpath, ".") && strcmp(subpath, "..")){
                // 拼接字符串,将当前路径与当前元素拼接成全路径
                char *p = malloc(strlen(path)+strlen(subpath)+1);
                sprintf(p, "%s/%s", path, subpath);
                // 输出当前元素的全路径
                printf("%s\n", p);
                // 如果是目录,则递归
                if (d_type == DT_DIR){
                    print_dir(p);
                }
                free(p);
            }
            dirent = readdir(dir);
        }
        // 3、关闭目录
        closedir(dir);
    }
    int main(int argc, char* argv[]){
        if (argc < 2){
            printf("e.g: ./a.out dirpath\n");
            exit(1);
        }
        // 从传进来的目录开始递归打印
        print_dir(argv[1]);
        return 0;
    }
    

    15、dup、dup2函数(文件描述符拷贝)

    • dup
      #include <stdio.h>
      #include <stdlib.h>
      #include <unistd.h>
      #include <sys/types.h>
      #include <sys/stat.h>
      #include <fcntl.h>
      
      int main(){
      
        int fd = open("a.txt", O_CREAT | O_RDWR, 0664);
        if (fd == -1){
            perror("open");
            exit(1);
        }
        printf("fd:%d\n", fd);
        char txt[] = "Hello Linux!!\n";
        write(fd, txt, sizeof(txt));
        int dup_fd = dup(fd);
        printf("dup_fd:%d\n", dup_fd);
        char dup_txt[] = "I am a function named dup!!\n”;
        // 结果是dup_txt数据会写入到a.txt中
        write(dup_fd, dup_txt, sizeof(dup_txt));
        close(fd);
        close(dup_fd);
        return 0;
      }
      
    • dup2
      #include <stdio.h>
      #include <stdlib.h>
      #include <unistd.h>
      #include <fcntl.h>
      
      int main(){
        int df1 = open("a.txt", O_RDWR);
        printf("df1:%d\n", df1);
        int df2 = open("dup2.txt", O_CREAT | O_RDWR, 0664);
        printf("df2:%d\n", df2);
        // 这句话的意思是:用df1来指向df2,那么原df1会被关闭
        int df_dup2 = dup2(df2, df1);
        printf("df_dup2:%d\n", df_dup2);
        char txt1[] = "txt1\n";
        char txt2[] = "txt2\n";
        char txt_dup2[] = "txt_dup2\n”;
        // 最终结果是所有的数据都写在dup2.txt文件中,原a.txt没有影响
        write(df1, txt1, sizeof(txt1));
        write(df2, txt2, sizeof(txt2));
        write(df_dup2, txt_dup2, sizeof(txt_dup2));
        close(df1);
        close(df2);
        close(df_dup2);
        return 0;
      }
      

    16、fcntl函数(修改文件描述符操作权限)

    #include <unistd.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(){
        char *p = "I am a student!!!\n";
        char *q = "Just do it and you can get it!\n";
        // 1、打开文件,以只写的方式打开
        int fd = open("a.txt", O_WRONLY);
        if (fd == -1){
            perror("open");
            exit(1);
        }
        // 2、写入内容,此时会从头开始写,覆盖原有的内容
        int ret = write(fd, p, strlen(p));
        if (ret == -1){
            perror("write");
            exit(1);
        }
        // 3.1、获取文件描述符当前的操作权限
        int flag = fcntl(fd, F_GETFL, 0);
        if (flag == -1){
            perror("fcntl getflag");
            exit(1);
        }
        // 3.2、修改文件描述符操作权限
        flag |= O_APPEND;
        ret = fcntl(fd, F_SETFL, flag);
        if (ret == -1){
            perror("fcntl setflag");
            exit(1);
        }
        // 4、再次写入,会匹配新的操作权限,因为添加了“追加”权限,新写入的内容会被追回到内容的最后面
        ret = write(fd, q, strlen(q));
        if (ret == -1){
            perror("write");
            exit(1);
        }
        return 0;
    }
    

    执行结果:

    yusian@SianMac2:~/Linux/source$ cat a.txt
    Hello Linux!!
    I am a function named dup!!
    yusian@SianMac2:~/Linux/source$ ./a.out
    yusian@SianMac2:~/Linux/source$ cat a.txt
    I am a student!!!
    m a function named dup!!
    Just do it and you can get it!
    yusian@SianMac2:~/Linux/source$
    

Leave a Reply