ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Linux系统编程入门4——文件IO操作(这可是重头戏)

2021-12-24 13:35:04  阅读:136  来源: 互联网

标签:文件 int st 描述符 fd 重头戏 IO Linux include


文章目录

前言

文件IO 操作,个人认为这是较为重要的内容,因为程序永远不可能不合文件打交道,本篇涉及的内容都是在后面会经常用到的,也算是在变相复习了吧!总之这里很重要。本文所使用的环境为云主机CentOS8系统,使用 xshell 进行远程连接调试,gcc版本为8.5,所有示例均经过测试无误。

文件IO函数使用

标准 C库函数 的底层会调用不同系统的底层函数,实现文件操作,具有跨平台性,所以推荐使用 标准C库函数,增加跨平台性。
image

写文件需要用到缓冲区,然后再刷新到磁盘中

虚拟地址空间

程序虚拟出来的不存在的一段虚拟内存空间,为了解决程序加载内存方便理解而虚拟的空间,可以简单的分为 内核区 和 用户区,用户无法直接操作内核区,需要调用系统的API 来实现操作。
image

在内核区针对文件读写有一个单独的 PCB(进程控制块),在其中有一个文件描述符表,大小为1024,每打开一个新文件,就会占用一个文件描述符,而且占用的是空闲的最小的一个文件描述符,在 文件描述符表 中,有默认三个是 打开状态的,分别是 标准输入,标准输出和标准错误,占用 0 1 2 位。

如果一个文件被多个程序打开,那么他们返回到 文件描述符表 的FILE* 指针是不同的
image

如下例子可以理解open函数的使用

/*
查看open 需要的头文件
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>  open函数在此头文件中声明

    // 打开一个已经存在的文件
    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);作用:打印errno对应的错误描述
        s参数:用户描述,比如hello,最终输出的内容是  hello:xxx(实际的错误描述)
    

    // 创建一个新的文件
    int open(const char *pathname, int flags, mode_t mode);
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main() {

    // 打开一个文件
    int fd = open("a.txt", O_RDONLY);

    //打印错误号
    if(fd == -1) {
        perror("open");
    }
    // 读写操作

    // 关闭文件
    close(fd);

    return 0;
}

目前该文件夹下是没有 a.txt 文件的,所以会打印一个错误信息:open: No such file or directory

打开关闭文件

使用如下文件来创建一个有读写权限的文件:

/*
    #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 (0代表八进制,后面三个数字代表权限)
                    (文件类型 当前用户选项 同组其他用户权限 其他组用户权限)
            最终的权限是:mode & ~umask
            0777   ->   111111111
        &   0775   ->   111111101
        ----------------------------
                        111111101
        按位与:0和任何数都为0
        umask的作用就是抹去某些权限。(防止某些误操作)

        flags参数是一个int类型的数据,占4个字节,32位。
        flags 32个位,每一位就是一个标志位。

*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {

    // 创建一个新的文件,O_CREAT 为可选选项,创建文件使用 | 为按位或 
    //主要就是可以增加一个权限
    int fd = open("create.txt", O_RDWR | O_CREAT, 0777);

    if(fd == -1) {
        perror("open");
    }

    // 关闭文件
    close(fd);

    return 0;
}

文件拓展

使用如下操作实现 对文件增大容量

/*
标准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 来操作某个文件
            - offset: 偏移量
            - whence:
                SEEK_SET:设置文件指针的偏移量
                SEEK_CUR: 设置偏移量,当前位置+第二个参数 offset 的值
                SEEK_END:设置偏移量,文件大小+ 第二个参数offset 的值
        
        返回值:返回文件指针的位置

        作用:
            1. 移动文件指针到文件头
            lseek(fd,0,SEEK_SET);
            2. 获取当前文件指针的位置
            lseek(fd,0,SEEK_CUR);
            3. 获取文件长度
            lseek(fd,0,SEEK_END);
            4. 拓展文件长度,将10b 的文件,增加100b,变为110b
            lseek(fd,100,SEEK_END);

 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
    int fd = open("hello.txt",O_RDWR);
    //获取文件长度
    if (fd==-1)
    {
        perror("open");
        return -1;
    }

    //拓展文件长度
    int res = lseek(fd,100,SEEK_END);
    if (res==-1)
    {
        perror("lseek");
        return -1;
    }

    //一定要写入一个空数据,否则不生效
    write(fd," ",1);

    //关闭文件
    close(fd);

    return 0;
}

得到结果:
image

这个操作的主要作用可以通过迅雷的例子理解:迅雷在下载文件的时候,会预先提示申请空间,然后在申请的空间内将下载的文件填满申请的空间,其中申请的空间就是通过这个操作实现的。

stat结构体

可以通过下面的 成员变量 获得文件属性

struct stat {
dev_t st_dev; // 文件的设备编号
ino_t st_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; // 块大小
blkcnt_t st_blocks; // 块数
time_t st_atime; // 最后一次访问时间
time_t st_mtime; // 最后一次修改时间
time_t st_ctime; // 最后一次改变时间(指属性)
};

对文件权限记录的说明
image

使用不同的标志位来记录不同的权限

  • 0-2 位:其他组用户的权限
  • 3-5 位:同组其他用户的权限
  • 6-8 位:使用者的权限
  • 9-11 位:特殊权限位:有三个权限位,可以设置组id,设置用户id,设置粘住位
  • 12-15 位:使用一个或两个标记位(八进制),来表示一个文件类型

如果要判断一个文件类型,就使用 该文件文件类型码 与 掩码 进行与操作,然后由得到的值与 各个类型做比较,就可以得到文件类型了

有如下例子:

/*
    #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;
}

输出该文件的大小:
image

模拟实现 ls-l 命令

代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>    //根据id 获取文件所有者 的头文件
#include <grp.h>    //获取文件所在组 的头文件
#include <time.h>    //将秒数 转化 为时间
#include <string.h>

//模拟实现 ls -l 命令
// -rw-r--r-- 1 root root    78 Nov 25 22:09 ls-l.c

//两个参数,一个是文件路径(文件名称),另一个是 命令行后的参数
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]='1';
        break;
    case S_IFDIR:   //目录 标识
        perms[0]='d';
        break;
    case S_IFREG:   //普通文件 标识
        perms[0]='-';
        break;
    case S_IFBLK:   //块设备 标识
        perms[0]='1';
        break;
    case S_IFCHR:   //字符设备 标识
        perms[0]='c';
        break;
    case S_IFSOCK:   //套接字 标识
        perms[0]='s';
        break;
    case S_IFIFO:   //管道文件 标识
        perms[0]='t';
        break;
    
    default:
        perms[0]='?';
        break;
    }

    // 判断文件权限

    //文件所有者的权限判断:与 权限标志位 做 与 操作,得到,如果为1,就有该权限,否则就是没有改权限
    perms[1] = st.st_mode & S_IRUSR ? 'r' : '-';
    perms[2] = st.st_mode & S_IWUSR ? 'w' : '-';
    perms[3] = st.st_mode & S_IXUSR ? 'x' : '-';
    //文件所在组 权限  is read group 的英文缩写
    perms[4] = st.st_mode & S_IRGRP ? 'r' : '-';
    perms[5] = st.st_mode & S_IWGRP ? 'w' : '-';
    perms[6] = st.st_mode & S_IXGRP ? 'x' : '-';
    //其他人 权限  is read other
    perms[7] = st.st_mode & S_IROTH ? 'r' : '-';
    perms[8] = st.st_mode & S_IWOTH ? 'w' : '-';
    perms[9] = st.st_mode & S_IXOTH ? 'x' : '-';

    //获取硬链接数
    int linkNum=st.st_nlink;

    //文件所有者    从 uid 获取到所有者 名字
    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); //将time的内容拷贝到mtime中,并将最后的一个回车符去掉
    
    //将内容输出
    char buf[1024];
    sprintf(buf,"%s %d %s %s %ld %s %s",perms,linkNum,fileUser,fileGrp,fileSize,mtime,argv[1]);

    //输出该信息
    printf("%s\n",buf);

    return 0;
}

运行结果:
image

文件属性操作

判断文件属性 access

access函数,可以判断文件的各种属性

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");
    }
    else
    {
        printf("文件存在\n");
    }


    return 0;
}

得到如下结果:
image

修改文件权限 chmod

chmod函数,可以修改文件权限

#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()
{
    //用三个八进制数 来表示权限     八进制数,以0 开头
    int ret=chmod("a.txt",0775);

    if(ret==-1)
    {
        perror("chmod");
        return -1;
    }
    else
    {
        printf("修改权限成功!!\n");
    }

    return 0;

}

执行文件更改:
image

chown 函数可以修改文件所在组,使用方法和以上基本相同,可以通过查询帮助文档,获取更多信息:

man 2 chown		# 查看chown 函数的详细信息

使用该命令,获得帮助文档:
image

要获取 uid 和 gid 可以通过查看 /etc 下的文件来获得:
image

修改文件大小 truncate

truncate函数,对文件 缩减 或 修改

#include <unistd.h>

#include <sys/types.h>

int truncate(const char *path, off_t length);

作用:缩减 或 扩展 文件的尺寸到指定的大小

参数:

​ - path:需要修改的文件的路径

​ - length:需要最终文件变成的大小

返回值:

​ 成功返回0,失败返回-1

有如下代码,修改 b.txt 文件的大小:

#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;
    }
    else
    {
        printf("修改文件大小成功,设置为20字节大小!\n");
    }

    return 0;
}

运行成功后:
image

拓展后的 b.txt
image

如果将 b.txt 进行缩小,则会直接发生 截断,会直接丢弃末尾文件内容
比如将其改为 5,运行结果如下:
image

使用该函数可以方便的 拓展 或 缩减 文件大小

目录操作函数

创建目录mkdir

与 shell 命令同名,可以创建目录

#include <sys/stat.h>

#include <sys/types.h>

int mkdir(const char *pathname, mode_t mode);

作用:创建一个目录

参数:

​ pathname:创建目录的路径

​ mode:权限 三个八进制数

返回值:

​ 成功返回0,失败返回-1

执行如下代码,可以在当前文件夹下,创建 aaa 目录

#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;
    }
    else
    {
        printf("创建目录成功!\n");
    }


    return 0;
}

执行该文件成功,创建 aaa 目录:
image

image

要注意创建文件的权限,即使是 八进制775,系统内部会对其做调整,因为在 赋予权限时,并不是单纯的根据代码中规定的进行权限设置的,而是 mode & ~umask & 0777 这三个结合后的结果,才是最终的权限(目的是为了系统文件安全)

删除目录 rmdir

使用函数删除目录,只能删除 空目录,具体使用方法和 mkdir 命令类似

重命名目录 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;
}

修改工作目录 chdir

chdir 函数,可以修改当前程序的工作目录,而不是更改文件夹的没给你做

#include <unistd.h>

int chdir(const char *path);

​ 作用:修改进程的工作目录

​ 程序时默认在当前路径进行操作的,可以使用 chdir 更改工作目录

​ 比如在/home/nowcoder 启动了一个可执行程序a.out, 进程的工作目录 /home/nowcoder

​ 参数:

​ path : 需要修改的工作目录

​ 返回值:

​ 成功返回0,失败返回-1

#include <unistd.h>

char *getcwd(char *buf, size_t size);

​ 作用:获取当前工作目录

​ 参数:

​ - buf : 存储的路径,指向的是一个数组(传出参数)

​ - size: 数组的大小

​ 返回值:

​ 返回的指向的一块内存,这个数据就是第一个参数

有如下程序:

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

int main() 
{

    // 获取当前的工作目录
    char buf[128];
    getcwd(buf, sizeof(buf));
    printf("当前的工作目录是:%s\n", buf);

    // 修改工作目录
    int ret = chdir("/root/clearn/lesson03");
    if(ret == -1) {
        perror("chdir");
        return -1;
    } 

    // 创建一个新的文件  八进制664
    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(buf1));
    printf("当前的工作目录是:%s\n", buf1);
    
    return 0;
}

执行后效果:
image
在 lesson03 目录下,可以找到创建的文件:
image

目录遍历函数

设计的主要几个函数:

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


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

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

在遍历目录,读取目录的时候,dirent函数会返回相关的目录信息(返回一个结构体),如下:
image

遍历目录下所有文件数量:

代码如下:

#include <sys/types.h>
#include <dirent.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h> //使用该头文件,NULL 就不会显示未定义了
//#define NULL ((void *)0)

int getFileNum(const char * path);
//argc是命令行总的参数个数 argv[]是argc个参数,其中第0个参数是程序的全名
int main(int argc,char* argv[])
{
    // 没有参数
    if(argc<2)
    {
        //输出默认的第0个参数,即程序全名
        printf("%s path\n",argv[0]);
    }

    int num = getFileNum(argv[1]);
    printf("普通文件的个数为:%d \n",num);

    return 0;
}

//用于获取目录下 所有普通文件的个数
int getFileNum(const char * path)
{
    //打开目录
    DIR* dir=opendir(path);
    //如果该文件打开失败或者为空,就输出错误信息
    if(dir==NULL)
    {
        perror("opendir");
        exit(0);
    }

    struct dirent * ptr=readdir(dir);

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

    //不断读入目录
    while((ptr=readdir(dir))!=NULL)
    {
        //获取名称
        char * dname=ptr->d_name;
        

        //忽略 . 和 .. 即忽略当前目录和父目录
        if(strcmp(dname,".")==0||strcmp(dname,"..")==0) //使用字符串比较函数 strcmp
        {
            //忽略这两个 目录
            continue;
        }

        //判断是否是普通文件 还是目录
        if(ptr->d_type==DT_DIR)
        {
            //是目录,就继续读取这个目录
            char newpath[256];
            //将新目录 格式化输出到newpath中
            sprintf(newpath,"%s/%s",path,dname);
            //递归这个过程
            toatl += getFileNum(newpath);

        }
        
        if(ptr->d_type==DT_REG)
        {
            //如果是普通文件,就 统计数量
            toatl++;
        }

    }

    //关闭目录
    closedir(dir);

    return toatl;
}

运行结果如下:
image

文件描述符

复制文件描述符

复制的文件描述符,与原本的文件描述符使用是一致的,有如下例子,可以通过复制的文件描述符,操作该文件(即使该文件已经被关闭了,但是复制的文件描述符仍然有效)

#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 <fcntl.h>
#include <sys/types.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;
    }
    else
    {
        printf("write success!\n");
    }


    return 0;
}

执行结果:
image

重定向文件描述符

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/stat.h>
#include <sys/types.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);

    //重定向 文件描述符,fd1 指向与fd相同的文件
    //fd2 指向的和 fd1 一致
    int fd2 = dup2(fd, fd1);
    if(fd2 == -1) {
        perror("dup2");
        return -1;
    }

    //通过 fd1 去写数据,实际操作的是 1.txt 而不是2.txt,因为其已经被重定向了
    char * str="hello dup2";
    //返回写入的长度
    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);

    //fd2 不用关闭,因为其和fd1 是一样的
    close(fd);
    close(fd1);

    return 0;
}

执行成功如图:
image

修改文件描述符

可以实现5种不同的功能,这里主要使用两种

#include <unistd.h>

#include <fcntl.h>

int fcntl(int fd, int cmd, … );

参数:

​ fd : 表示需要操作的文件描述符

​ cmd : 对文件描述符做 什么操作

​ F_DUPFD :复制文件描述符,复制的为第一个参数 fd,得到一个新的文件描述符

​ int ret = fcnt1(fd,F_DUPFD);

​ F_GETFL : 获取指定的文件描述符状态 flag

​ 获取的flag和通过open 函数传递的flag 为同一个

​ F_SETFL :设置文件描述符状态 flag

​ 必选项:O_RDONLY ,O_WRDOLY, O_RDWD

​ O_APPEND 表示最佳数据,在原有文件后面继续写数据

​ NONBLOK 设置为非阻塞

​ 阻塞和非阻塞:描述函数调用的行为,阻塞:调用某个函数,无法立刻得到其返回值,非阻塞:调用某个函数可以立刻得到其返回值

如下例子演示,修改文件描述符,向文件中追加数据

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

int main()
{
    //复制文件描述符
    //int fd1=open("1.txt",O_RDONLY);
    //使用fcntl 赋值文件描述符
   // int ret=fcntl(fd1,F_DUPFD);

    //修改 或 获取 文件状态 flag
    int fd=open("1.txt",O_RDWR);  //现在是以读写方式打开文件
    if(fd == -1) {
        perror("open");
        return -1;
    }
    //下面使用 fcntl修改文件描述符的 flag,加入 O_APPEND 标记
    //先 获取文件描述符 flag
    int flag=fcntl(fd,F_GETFL);
    //相当于 flag=flag|O_APPEND,将 O_APPEND 加入到 flag中
    flag|=O_APPEND;
    int ret = fcntl(fd,F_SETFL,flag);
    if(ret == -1) {
        perror("fcntl");
        return -1;
    }

    char * str = "nihao";
    write(fd, str, strlen(str));

    close(fd);

    return 0;
}

运行结果:追加信息成功
image

如果打开了文件,直接 使用 write 进行写入,会覆盖之前的信息 ,在使用fcntl 追加之后,就是其后新增其他数据了

最后

感谢观赏,一起提高,慢慢变强

标签:文件,int,st,描述符,fd,重头戏,IO,Linux,include
来源: https://blog.csdn.net/qq_45966201/article/details/122125888

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有