• linux系统编程之文件与io(五)


     上一节中已经学习了文件描述符的复制,复制方法有三种,其中最后一种fcntl还并未使用到,关于这个函数,不光只有复制文件描述符的功能,还有其它一些用法,本节就对其进行一一剖析:

    fcntl常用操作:

    这里,我们将上节当中用dup或dup2实现复制文件描述符改用fcntl,程序如下:

    先将test2.txt的内容清空,以便进行测试,编译运行:

    通过man来查看下它的说明:

    【说明:关于这一的操作命令,等之后学到进程时再来学习,先这边记录一下】

    上一节也有介绍过,先回顾一下都有哪些状态标志:

    也就是说,通过这个命令,能更改文件状态标志,说来有些难理解,下面以实例代码来进行一一说明:

    编译运行:

    当输入内容时,read才读取完,并打印出输入的内容:

    这时本来的文件状态,但是,可以fcntl函数,来改变这种阻塞状态为非阻塞状态,如下:

    具体代码:

    #include <unistd.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #define ERR_EXIT(m) 
        do 
        { 
            perror(m); 
            exit(EXIT_FAILURE); 
        } while(0)
    int main(int argc, char *argv[])
    {
        char buf[1024] = {0};
        int ret;
        int flags;
        flags = fcntl(0, F_GETFL, 0);//通过F_GETFL来获得标准输入的状态
        if (flags == -1)
            ERR_EXIT("fcntl get flag error");
    
        ret = fcntl(0, F_SETFL, flags | O_NONBLOCK);//通过F_SETFL来改变文件的状态为非阻塞0_NONBLOCK,但是为了保留其它状态,所以设置之前需获得状态,再进行与操作
        if (ret == -1)
            ERR_EXIT("fcntl set flag error");
    
        ret = read(0, buf, 1024);
        if (ret == -1)
            ERR_EXIT("read error");
    
        printf("buf=%s
    ", buf);
        return 0;
    }

    编译运行:

    实际上,对于上面这个错误,对应的错误代码是:

    注意:在设置状态时,一定得先用F_GETFL获取状态,然后再去用F_SETFL去设置,因为我们只想设置非阻塞的状态,对于其它状态如:写状态,读状态等想保留
    关于上面这段设置状态的代码,可以进行封装,以便进行复用,如下:

    #include <unistd.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    
    
    #define ERR_EXIT(m) 
        do 
        { 
            perror(m); 
            exit(EXIT_FAILURE); 
        } while(0)
    
    void set_flag(int fd, int flags);//设置文件状态标志函数声明
    
    int main(int argc, char *argv[])
    {
        char buf[1024] = {0};
        int ret;
    
        set_flag(0, O_NONBLOCK);//这时,经过代码封装之后,代码就显得比较干净了
        
        ret = read(0, buf, 1024);
        if (ret == -1)
            ERR_EXIT("read error");
    
        printf("buf=%s
    ", buf);
        return 0;
    }
    
    //设置文件状态标志
    void set_flag(int fd, int flags) { int val; val = fcntl(fd, F_GETFL, 0); if (val == -1) ERR_EXIT("fcntl get flag error"); val |= flags; if (fcntl(fd, F_SETFL, val) < 0) ERR_EXIT("fcntl set flag error"); }

    另外,我们还可以封装一个清除文件状态标志的函数,跟设置很类似,如下:

    #include <unistd.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    
    
    #define ERR_EXIT(m) 
        do 
        { 
            perror(m); 
            exit(EXIT_FAILURE); 
        } while(0)
    
    void set_flag(int fd, int flags);
    void clr_flag(int fd, int flags);//清除文件状态标志函数声明
    
    int main(int argc, char *argv[])
    {
        char buf[1024] = {0};
        int ret;
    
        set_flag(0, O_NONBLOCK);
        clr_flag(0, O_NONBLOCK);//清除了非阻塞的状态标记,也就是等于最终还是阻塞状态
        
        ret = read(0, buf, 1024);
        if (ret == -1)
            ERR_EXIT("read error");
    
        printf("buf=%s
    ", buf);
        return 0;
    }
    
    void set_flag(int fd, int flags)
    {
        int val;
        val = fcntl(fd, F_GETFL, 0);
        if (val == -1)
                    ERR_EXIT("fcntl get flag error");
        val |= flags;
        if (fcntl(fd, F_SETFL, val) < 0)
            ERR_EXIT("fcntl set flag error");
    }
    
    //清除文件状态标志
    void clr_flag(int fd, int flags) { int val; val = fcntl(fd, F_GETFL, 0); if (val == -1) ERR_EXIT("fcntl get flag error"); val &= ~flags; if (fcntl(fd, F_SETFL, val) < 0) ERR_EXIT("fcntl set flag error"); }

    编译运行:

    提示:设置状态中的:val |= flags;和清除状态中的:val &= ~flags;是怎么一回事,可以看一下C程序中的位操作,这个也可以参考:http://www.cnblogs.com/webor2006/p/3440026.html

     

    先来解释一下结构体字段:

     

     

    说了这么多,可能还是有点不是很好理解,下面以实际代码来进行说明:

    #include <unistd.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    
    
    #define ERR_EXIT(m) 
        do 
        { 
            perror(m); 
            exit(EXIT_FAILURE); 
        } while(0)
    
    int main(int argc, char *argv[])
    {
        int fd;
        fd = open("test2.txt", O_CREAT | O_RDWR | O_TRUNC, 0644);
        if (fd == -1)
            ERR_EXIT("open error");
    
        struct flock lock;
        memset(&lock, 0, sizeof(lock));
        lock.l_type = F_WRLCK;//加上排它锁
        lock.l_whence = SEEK_SET;
        lock.l_start = 0;
        lock.l_len = 0;
    
        if (fcntl(fd, F_SETLK, &lock) == 0)
        {
            printf("lock success
    ");
            printf("press any key to unlock
    ");
            getchar();
            lock.l_type = F_UNLCK;//释放锁
            if (fcntl(fd, F_SETLK, &lock) == 0)
                printf("unlock success
    ");
            else
                ERR_EXIT("unlock fail");
        }
        else
            ERR_EXIT("lock fail");
        return 0;
    }

    编译运行:

    实际上,上面出错的错误代码也是EAGAIN,查看一下fcntl函数:

    注意:如果要给文件加读锁,则文件需要有读的权限;如果要给文件写写锁,则文件也需要有写的权限

    另外,设置文件锁,还有另外一种操作命令:F_SETLKW,它跟F_SETLK有啥区别呢?且看下代码:

    编译运行:

    总结:F_SETLK设置锁时,如果进程没有成功设置上锁,则会立马给出错误提示;F_SETLKW设置锁时,如果进程没有成功设置上锁,会阻塞,类似于线程的同步一样,当对方的锁释放时,则才可以对文件进行上锁】

    另外,如果想获得阻塞进程的ID,可以用F_GETLK,它会将id保存在flock结构体中的l_pid当中,关于这个,就不做实验了,比较简单,好了,关于linux系统编程中的文件/IO,就告一段落了,下回会进入linux系统编程的新的东东,下回见!

  • 相关阅读:
    Fatal error compiling: invalid target release: 11 -> [Help 1]
    java -jar 设置日志位置
    wordpress 添加 显示磁盘剩余空间百分比的插件
    记录我个人对Telegram的了解
    Ubuntu 18.04 切换使用Python3
    基于 CI 1.7.x 的 项目使用新版本CI的文件缓存类库
    使用sqlyog工具同步两个相同结构的数据库之间的数据
    了解GTIN小记
    华为手机设置桌面图标角标提醒的实现
    Quill编辑器IOS下无法获取焦点的解决方法
  • 原文地址:https://www.cnblogs.com/webor2006/p/3500354.html
Copyright © 2020-2023  润新知