第一章-文件IO

文件IO

从内存角度来看,文件到内存为输入,内存到文件中为输出。
在这里插入图片描述
C库函数先把数据读取到缓冲区,待到缓冲区满了之后,再读取到磁盘中,而Linux系统中每读取一次数据就调用一次磁盘在这里插入图片描述
虚拟内存空间不存在,是一种想象
在这里插入图片描述

深入理解虚拟内存管理
虚拟内存管理
在这里插入图片描述
在这里插入图片描述

open函数

*int open(const char pathname, int flags)

open.c
/*
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>
       //可变参数  不是函数重载
       //打开一个已经存在的文件
       int open(const char *pathname, int flags);
       参数:
            pathname:要打开文件的路径
            flags:对文件的操作权限设置还有其它的设置
            O_RDONLY, O_WRONLY, O_RDWR 这三个设置是互斥的
       返回值:返回一个新的文件描述符,如果调用失败,返回-1
       
       errno:属于Linux系统函数库,库里面的一个全局变量,记录的是最近的错误号
       #include <stdio.h>
       void perror(const char *s);
        s参数:用户描述,比如hello,最终输出的内容是 hello:xxx(实际的错误描述)
       作用:打印errno对应的错误描述
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(){
    //使用open函数以只读的形式打开文件
    int fd = open("a.txt",O_RDONLY);
    if(fd==-1){
        //如果不能打开,返回值为-1,调用perror函数显示错误
        perror("open");
    }
    //关闭文件描述符
    close(fd);
    return 0;
}

在没有a.txt文件的情况下编译open.c文件,perror函数显示错误信息
在这里插入图片描述
创建一个a.txt文件,并编译open.c文件,不会出现错误信息
在这里插入图片描述

*int open(const char pathname, int flags, mode_t mode)

create.c 
/*
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>

       int open(const char *pathname, int flags, mode_t mode);
       参数:
            pathname:要创建的文件的路径
            flags:文件的操作权限和其它的设置
                必选项:O_RDONLY,  O_WRONLY, O_RDWR 这三个之间是互斥的
                可选项: O_CREAT 文件不存在,创建新文件
            mode:八进制的数,表示创建出新的文件的操作权限,比如:0775 
            最终的权限是:mode & ~umask
            0777 ->111111111
        &   0775 ->111111101
        ----------------------
        按位与:0和任何书都为0
        0775       111111101 
        umask的作用是抹去某些权限,让权限更加合理些

        为什么flags参数要用按位或?
        flags参数是一个int类型的数据,占4个字节,32位
        flags 32个位,每一位就是一个标志位
*/     
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(){
    //创建一个新的文件          按位或
    int fd = open("create.txt",O_RDWR|O_CREAT,0777);
    if(fd==-1){
        perror("open");
    }
    //关闭
    close(fd);
    return 0;
}

编译create.c文件
在这里插入图片描述
总结:

open函数可以打开一个已有的文件,也可以创建一个新的文件

read和write函数

在这里插入图片描述

/*
    #include <unistd.h>
    ssize_t read(int fd, void *buf, size_t count);
    参数:
        fd:文件描述符,open得到的,可以通过文件描述符操作某个文件
        buf:需要读取数据存放的地方,数组的地址(传出参数)
        count:指定的数组的大小
    返回值:
        成功:
            >0:返回实际读取到的字节数
            =0:文件已经读取完了
        失败:-1,并且设置errno 
     #include <unistd.h>
     ssize_t write(int fd, const void *buf, size_t count);
     参数:
        fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
        buf:要往磁盘写入的数据,
        count:要写的数据的实际大小
    返回值:
        成功:实际写入的字节数
        失败:返回-1,并设置errno

*/
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(){
    //1.通过open打开english.txt文件
    int srcfd  = open("english.txt",O_RDONLY);
    if(srcfd == -1){
        perror("open");
        return -1;
    }
    //2.创建一个新的文件
    int destfd = open("cpy.txt",O_WRONLY|O_CREAT,0664);
    if(destfd == -1){
        perror("open");
        return -1;
    }
    //3.频繁的读写操作
    char buf[1024] = {0};
    int len = 0;
    while((len = read(srcfd,buf,sizeof(buf))) > 0){
        write(destfd,buf,len);
    }
    //4.关闭文件
    close(destfd);
    return 0;
}

编译copyfile.c文件,copyfile.txt和复制的cpy.txt文件字节数相同129772在这里插入图片描述

lseek函数

在这里插入图片描述

lseek.c
/*
    标准c库函数
    #include <stdio.h>
    int fseek(FILE *stream, long offset, int whence);

    Linux系统函数
    #include <sys/types.h>
    #include <unistd.h>
    off_t lseek(int fd, off_t offset, int whence);
    参数:
        fd:文件描述符,通过open得到,通过这个fd操作某个文件
        offest:偏移量
        whence:
            SEEK_SET:设置文件的偏移量
            SEEK_CUR:设置偏移量,从当前位置加上第二个参数offest的值
            SEEK_END:设置偏移量,文件大小加上第二个参数offest的值
    返回值:返回文件指针的位置
    作用:
        1.移动文件指针到文件头
        lseek(fd,0,SEEK_SET);
        2.获取当前文件指针的位置
        lseek(fd,0,SEEK_CUR);
        3.获取文件的长度
        lseek(fd,0,SEET_END);
        4.拓展文件的长度,当前文件10b,拓展到110b,增加了100字节
        lseek(fd,100,SEEK_END);
        注意:需要写入数据,lseek函数只是移动文件指针的偏移量,没有扩充数据的功能
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
int main(){
    int fd = open("hello.txt",O_RDWR);
    if(fd == -1){
        perror("open");
        return -1;
    }
    //拓展文件的长度
    int ret = lseek(fd, 100, SEEK_END);
    if(ret == -1){
        perror("lseek");
        return -1;
    }
    //写入一个空数据
    write(fd," ",1);

    //关闭文件
    close(fd);

    return 0;
}

stat指令查看文件相关信息

stat函数和lstat函数中结构体的变量

int stat(const char *pathname, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);

在这里插入图片描述
在这里插入图片描述

stat.c
/*
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
    int stat(const char *pathname, struct stat *statbuf);
    作用:获取一个文件的相关信息
    参数:
        pathname:操作文件的路径
        statbuf:结构体变量,传出参数用于保存获取到的文件的信息
    返回值:
        成功:返回0
        失败:返回-1,设置errno

    int lstat(const char *pathname, struct stat *statbuf);
    作用:获取一个文件的相关信息
    参数:
        pathname:操作文件的路径
        statbuf:结构体变量,传出参数用于保存获取到的文件的信息
    返回值:
        成功:返回0
        失败:返回-1,设置errno
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
int main(){
    struct stat statbuf;
    int ret = stat("a.txt",&statbuf);
    if(ret == -1){
        perror("stat");
        return -1;
    }
    printf("size:%ld\n",statbuf.st_size);
    return 0;
}

编译stat.c文件
在这里插入图片描述
创建软链接b.txt
lstat函数用于获取软链接文件的信息,不是软连接指向的信息
在这里插入图片描述

模拟实现ls-l命令

在这里插入图片描述

ls-l.c
/*
模拟实现ls-l命令
-rw-rw-r-- 1 ykx ykx 12 11月  1 10:14 a.txt
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <string.h>
int main(int argc, char *argv[]){
    //判断输入的参数是否正确
    if(argc < 2){
        printf("%s filename\n", argv[0])        ;
        return -1;
    }
    //通过stat函数获取传入文件的信息
    struct stat st;
    int ret = stat(argv[1], &st);
    if(ret == -1){
        perror("stat");
        return -1;
    }
    //获取文件类型和文件权限
    char perms[11] = {0};//用于保存文件类型和文件权限的字符串
    switch(st.st_mode & __S_IFMT){
        case __S_IFLNK:
            perms[0] = 'l';
            break;
        case __S_IFDIR:
            perms[0] = 'd';
            break;
        case __S_IFREG:
            perms[0] = '-';
            break;
        case __S_IFBLK:
            perms[0] = 'b';
            break;
        case __S_IFCHR:
            perms[0] = 'c';
            break;
        case __S_IFSOCK:
            perms[0] = 's';
            break;
        case __S_IFIFO:
            perms[0] = 'p';
            break;
        default:
            perms[0] = '?';
            break;
    }
    //判断文件的访问权限

    //文件所有者
    perms[1] = (st.st_mode & S_IRUSR) ? 'r' : '-';
    perms[2] = (st.st_mode & S_IWUSR) ? 'w' : '-';
    perms[3] = (st.st_mode & S_IXUSR) ? 'x' : '-';

    //文件所在组的权限
    perms[4] = (st.st_mode & S_IRGRP) ? 'r' : '-';
    perms[5] = (st.st_mode & S_IWGRP) ? 'w' : '-';
    perms[6] = (st.st_mode & S_IXGRP) ? 'x' : '-';
    //其他人
    perms[4] = (st.st_mode & S_IROTH) ? 'r' : '-';
    perms[5] = (st.st_mode & S_IWOTH) ? 'w' : '-';
    perms[6] = (st.st_mode & S_IXOTH) ? 'x' : '-';
    //硬链接数
    int linhNum = st.st_nlink;
    //文件所有者
    char *fileUser = getpwuid(st.st_uid)->pw_name;
    //文件所在组
    char *fileGrp = getgrgid(st.st_gid)->gr_name;
    //文件大小
    long int fileSize = st.st_size;
    //获取修改的时间
    char *time = ctime(&st.st_mtime);
    char mtime[512] = {0};
    strncpy(mtime, time, strlen(time) - 1);
    char buf[1024];
    sprintf(buf,"%s %d %s %s %ld %s %s",perms,linhNum,fileUser,fileGrp,fileSize,mtime,argv[1]);
    printf("%s\n",buf);
    return 0;

}

编译ls-l.c文件
在这里插入图片描述

文件属性操作函数

在这里插入图片描述

测试access函数

access.c
/*
    #include <unistd.h>
    int access(const char *pathname, int mode);
    作用:判断某个文件是否有某个权限,或者判断文件是否存在
    参数:
        pathname:判断的文件路径
        mode:
            R_OK:判断是否有读权限
            W_OK:判断是否有写权限
            X_OK:判断是否有执行权限
            F_OK:判断文件是否存在
    返回值:成功返回0,失败返回-1
*/
#include <unistd.h>
#include <stdio.h>
int main(){
    int ret = access("a.txt", F_OK);
    if(ret == -1){
        perror("access");
    }
    printf("文件存在!\n");
    return 0;
}

编译access.c文件
在这里插入图片描述
测试chmod函数

chmod.c
/*
    #include <sys/stat.h>
    int chmod(const char *pathname, mode_t mode);
    作用:修改文件的权限
    参数:
        pathname:需要修改文件的路径
        mode:需要修改的权限值
    返回值:成功返回0,失败返回-1
*/

#include <sys/stat.h>
#include <stdio.h>
int main(){
    int ret = chmod("a.txt", 0777);
    if(ret == -1){
        perror("chmod");
        return -1;
    }
    return 0;
}

编译chmod.c文件
改变a.txt文件的权限0777,a.txt文件的权限变为可读可写可执行
在这里插入图片描述
测试truncate函数

truncate.c
/*
    #include <unistd.h>
    #include <sys/types.h>
    int truncate(const char *path, off_t length);
    作用:缩减或者扩展文件的尺寸至指定的大小
    参数:
        path:需要修改文件的路径
        length:需要最终文件变成的大小
    返回值:
        成功返回0,失败返回-1
*/
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
int main(){
    int ret = truncate("b.txt", 20);
    if(ret == -1){
        perror("truncate");
        return -1;
    }

    return 0;
}

编译truncate.c文件,初始b.txt文件的字节数11,利用truncate函数变为20字节
在这里插入图片描述
填充的字节数
在这里插入图片描述

目录操作函数

在这里插入图片描述
测试mkdir()函数

/*
    #include <sys/stat.h>
    #include <sys/types.h>
    int mkdir(const char *pathname, mode_t mode);
    作用:创建一个目录
    参数:
        pathname:创建的目录的路径
        mode:权限,八进制的数
    返回值:成功返回0,失败返回-1
*/
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
int main(){
    int ret = mkdir("aaa",0777);
    if(ret == -1){
        perror("mkdir");
        return -1;
    }
    return 0;
}

编译mkdir.c文件
注意:只有文件的权限是可执行时,才能进入文件目录
在这里插入图片描述
测试rename函数

/*
    #include <stdio.h>
    int rename(const char *oldpath, const char *newpath);
*/
#include <stdio.h>
int main(){
    int ret = rename("aaa", "bbb");
    if(ret == -1){
        perror("rename");
        return -1;
    }
    return 0;
}

编译rename.c文件
在这里插入图片描述
修改工作路径chdir()函数和获当前工作路径getcwd()函数

chdir.c
/*
    #include <unistd.h>
    int chdir(const char *path);
    作用:修改进程的工作目录
    参数:
        path:需要修改的工作目录

    //当前的工作路径
    #include <unistd.h>
    char *getcwd(char *buf, size_t size);
    作用:
        获取当前工作目录
    参数:
        buf:存储的路径,指向的是一个数组
        size:指定数组的大小
    返回值:返回的指向的一块内存,这个数据就是第一个参数
*/
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(){
    //获取当前的工作目录
    char buf[128];
    getcwd(buf, sizeof(buf));
    printf("当前的工作目录是:%s\n", buf);
    //修改工作目录
    int ret = chdir("/home/ykx/linux/lesson13");
    if(ret == -1){
        perror("chdir");
        return -1;
    }
    //创建一个新的文件
    int fd = open("chdir.txt",O_CREAT | O_RDWR, 0664);
    if(fd == -1){
        perror("open");
        return -1;
    }
    close(fd);
    //获取当前的工作目录
    char buf1[128];
    getcwd(buf1, sizeof(buf));
    printf("当前的工作目录是:%s\n", buf1);
    return 0;
}

将当前工作路径lesson14改为lesson13
在这里插入图片描述

目录遍历函数

在这里插入图片描述
在这里插入图片描述

readFileNum.c
/*
    // 打开一个目录
    #include <sys/types.h>
    #include <dirent.h>
    DIR *opendir(const char *name);
        参数:
            - name: 需要打开的目录的名称
        返回值:
            DIR * 类型,理解为目录流a
            错误返回NULL


    // 读取目录中的数据
    #include <dirent.h>
    struct dirent *readdir(DIR *dirp);
        - 参数:dirp是opendir返回的结果
        - 返回值:
            struct dirent,代表读取到的文件的信息
            读取到了末尾或者失败了,返回NULL

    // 关闭目录
    #include <sys/types.h>
    #include <dirent.h>
    int closedir(DIR *dirp);

*/
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int getFileNum(const char * path);

// 读取某个目录下所有的普通文件的个数
int main(int argc, char * argv[]) {

    if(argc < 2) {
        printf("%s path\n", argv[0]);
        return -1;
    }

    int num = getFileNum(argv[1]);

    printf("普通文件的个数为:%d\n", num);

    return 0;
}

// 用于获取目录下所有普通文件的个数
int getFileNum(const char * path) {

    // 1.打开目录
    DIR * dir = opendir(path);

    if(dir == NULL) {
        perror("opendir");
        exit(0);
    }

    struct dirent *ptr;

    // 记录普通文件的个数
    int total = 0;

    while((ptr = readdir(dir)) != NULL) {

        // 获取名称
        char * dname = ptr->d_name;

        // 忽略掉. 和..
        if(strcmp(dname, ".") == 0 || strcmp(dname, "..") == 0) {
            continue;
        }

        // 判断是否是普通文件还是目录
        if(ptr->d_type == DT_DIR) {
            // 目录,需要继续读取这个目录
            char newpath[256];
            sprintf(newpath, "%s/%s", path, dname);
            total += getFileNum(newpath);
        }

        if(ptr->d_type == DT_REG) {
            // 普通文件
            total++;
        }


    }

    // 关闭目录
    closedir(dir);

    return total;
}

编译文件,查看路径下的文件个数
在这里插入图片描述

dup函数和dup2函数

在这里插入图片描述
dup函数

dup.c
/*
    #include <unistd.h>
    int dup(int oldfd);
    作用:复制一个新的文件描述符,指向同一个文件
    fd = 3 表示文件描述符打开一个新的文件 
    int fd1 = dup(fd)
    fd 指向的是a.txt, fd1 也是指向a.txt
    从空闲的文件描述符表中找一个最小的,作为新的拷贝的文件描述符
*/
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
int main(){
    int fd = open("a.txt", O_RDWR | O_CREAT, 0664);
    int fd1 = dup(fd);
    if(fd1 == -1){
        perror("dup");
        return -1;
    }
    printf("fd : %d, fd1 : %d\n", fd, fd1);
    close(fd);
    char *str = "hello world";
    int ret = write(fd1,str,strlen(str));
    if(ret == -1){
        perror("write");
        return -1;
    }
    close(fd1);
    return 0;
}

在这里插入图片描述
通过fd1写入
在这里插入图片描述

dup2函数

/*
    #include <unistd.h>
    int dup2(int oldfd, int newfd);
    作用:重定向文件描述符
    oldfd 指向a.txt, newfd 指向b.txt
    调用函数成功后, newfd 和 b.txt 做close, newfd 指向了a.txt
    oldfd 必须是一个有效的文件描述符
    oldfd 和 newfd值相同, 相当于什么都没有做
*/
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(){
    int fd = open("1.txt", O_RDWR | O_CREAT, 0664);
    if(fd == -1){
        perror("open");
        return -1;
    }
    int fd1 = open("2.txt", O_RDWR | O_CREAT, 0664);
    if(fd1 == -1){
        perror("open");
        return -1;
    }
    printf("fd : %d, fd1 : %d\n", fd, fd1);
    int fd2 = dup2(fd, fd1);
    if(fd2 == -1){
        perror("dup2");
        return -1;
    }
    //通过 fd1 去写数据,实际操作的是1.txt,而不是2.txt
    char *str = "hello";
    int len = write(fd1, str, strlen(str));
    if(len == -1){
        perror("write");
        return -1;
    }
    printf("fd : %d, fd1 : %d, fd2 : %d\n", fd, fd1, fd2);
    close(fd);
    close(fd1);
    return 0;
}

fd1与fd2相同

在这里插入图片描述

fcntl函数

在这里插入图片描述

/*
    #include <unistd.h>
    #include <fcntl.h>
    int fcntl(int fd, int cmd, ... arg);
    参数:
        fd:表示需要操作的文件描述符
        cmd:表示对文件描述符进行如何操作
             1.F_DUPFD:复制文件描述符,复制的是第一个参数fd,得到一个新的文件描述符(返回值)
             int ret = fcntl(fd,F_DUPFD);
             2.F_GETFL:设置文件描述的状态flag
               获取的flag和通过open函数传递的flag是一个东西
             3.FSETFL:设置文件描述符文件状态flag
               必选项:O_RDONLY, O_WRONLY, O_RDWR, 不可以被修改
               可选项:O_APPEND, O_NONBLOCK
               O_APPEND 表示追加数据
               O_NONBLOCK 设置成非阻塞
*/
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
int main(){
    //1.复制文件描述符
    //int fd = open("1.txt", O_RDONLY);
    //int ret = fcntl(fd, F_DUPFD);

    //2. 修改或获取文件状态flag
    int fd = open("1.txt", O_RDWR);
    if(fd == -1){
        perror("open");
        return -1;
    }
    //获取文件描述符的状态flag
    int flag = fcntl(fd, F_GETFL);
    if(flag == -1){
        perror("fcntl");
        return -1;
    }
    flag |= O_APPEND; //按位或

    //修改文件描述符状态的flag,给flag加入O_APPEND这个标记
    int ret = fcntl(fd, F_SETFL, flag);
    if(ret == -1){
        perror("fcntl");
        return -1;
    }

    char *str = "hello";
    write(fd, str, strlen(str));
    close(fd);
    return 0;
}

在这里插入图片描述

显示 1.txt文件 追加后的数据
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值