文件描述符的复制,访问测试,修改文件大小,文件锁

news2024/11/24 4:38:52

 指定文件描述符

#include <unistd.h>
int dup2(int oldfd, int newfd);

->功能:复制文件描述符表的特定条目到指定项,
->参数:oldfd:   源文件描述符
           newfd:  目标文件描述符
->返回值:成功返回目标文件描述符(newfd),失败返回-1。
->dup2函数在复制由oldfd参数所标识的源文件描述符表项时,会先检查由newfd参数所标识的目标文件描述符表项是否空闲,若空闲则直接将前者复制给后者,否则会先将目标文件描述符newfd关闭,使之成为空闲项,再行复制。

 代码实现

//文件描述符的复制
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(){
    //打开文件,得到文件描述符oldfd
    int oldfd = open("./dup.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664);
    if(oldfd == -1){
        perror("open");
        return -1;
    }
    printf("oldfd = %d\n", oldfd);
    //通过复制文件描述符oldfd,得到新的文件描述符newfd
    int newfd = dup2(oldfd,STDOUT_FILENO);
    if(newfd == -1){
        perror("dup2");
        return -1;  
    }
    printf("newfd = %d\n", newfd);
    //通过oldfd向文件写入数据,hello world!
    if(write(oldfd, "hello world!", 12) == -1){
        perror("write");
        return -1;
    }
    //通过newfd修改文件读写位置lseek
    if(lseek(newfd, -6, SEEK_END) == -1){
        perror("lseek");
        return -1;
    }
    //通过oldfd向文件写入数据,hello linux!
    char* buf = "linux!";
    if(write(oldfd, buf, 6) == -1){
        perror("write");
        return -1;
    }

    //关闭文件
    close(oldfd);
    close(newfd);



    return 0;
}

 执行结果

oldfd = 3
[1] + Done                       "/usr/bin/gdb" --interpreter=mi --tty=${DbgTerm} 0<"/tmp/Microsoft-MIEngine-In-3jfxpdbu.c5g" 1>"/tmp/Microsoft-MIEngine-Out-gykxl3f2.tnf"
day03$cat dup.txt
newfd = 1
hello linux!day03$

 

访问测试

#include <unistd.h>
int access(char const* pathname, int mode);

->功能:判断当前进程是否可以对某个给定的文件执行某中访问

->参数:pathname 文件路径
            mode被测试权限,可从一下取值
                R_OK        -可读否
                W_OK       -可写否
                X_OK        -可执行否
                F_OK         -存在否
->返回值:成功返回0,失败返回-1

 代码实现(从终端获取输入)

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

int main(int argc,char*argv[]){ 
    if(argc != 2) {
         printf("Usage: %s <filename>\n", argv[0]);
         return 1;
    }
    if(access(argv[1],F_OK) == -1){
        printf("文件%s不存在\n",argv[1]);
        return 0;
    }
    int readable = access(argv[1],R_OK) == 0;
    int writable = access(argv[1],W_OK) == 0;
    int executable = access(argv[1],X_OK) == 0;
   
    printf("文件%s", argv[1]);
    if(readable){
        printf("可读,");
    }else{
        printf("不可读,");
    }

    if(writable){
        printf("可写,");
    }else{
        printf("不可写,");
    }

    if(executable){
        printf("可执行\n");
    }else{
        printf("不可执行\n");
    }
    return 0;
}

 执行结果

day03$./access dup.txt
文件dup.txt可读,可写,不可执行
day03$./access qqq.txt
文件qqq.txt不存在

修改文件大小 

#include <unistd.h>
int truncate(char const* path, off_t length);

int ftruncate(int fd, off_t length);
->功能:修改指定文件的大小
->参数:path     文件路径
            length  文件大小

            fd         文件描述符
->返回值:成功返回0,失败返回-1。
->该函数既可以把文件截短,也可以把文件加长,所有的改变均发生在文件的尾部,新增加的部分用数字0填充。

代码实现

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

int main(){
    //打开文件
    int fd = open("./trunc.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664);
    if(fd == -1){
        perror("open");
        return -1;
    }
    //向文件写入数据abcde 5
    char buf[] = "abcde";
    if(write(fd, buf, strlen(buf)) == -1){
        perror("write");
        return -1;
    }
    //修改文件大小 abc  3
    if(truncate("./trunc.txt", 3) == -1){
        perror("ftruncate");
        return -1;                 //执行结果:abc
    }
    //再次修改文件大小 5
    if (ftruncate(fd, 5) == -1){
        perror("ftruncate");
        return -1;                  //执行结果:abc^@^@
    }
    //关闭文件
    close(fd);
    return 0;
}

文件锁

        为了避免多个进程在读写同一个文件的同一个区域时发生冲突,Unix/Linux系统引入了文件锁机制,并把文件锁分为读锁和写锁两种,它们的区别在于,对一个文件的特定区域可以加多把读锁,对一个文件的特定区域只能加一把写锁
        基于锁的操作模型是:读/写文件中的特定区域之前,先加上读/写锁,锁成功了再读/写,读/写完成以后再解锁

        当通过close函数关闭文件描述符时,调用进程在该文件描述符上所加的一切锁将被自动解除;
        当进程终止时,该进程在所有文件描述符上所加的一切锁将被自动解除。

读写冲突

  •        如果两个或两个以上的进程同时向一个文件的某个特定区域写入数据,那么最后写入文件的数据极有可能因为写操作的交错而产生混乱
  •        如果一个进程写而其它进程同时在读一个文件的某个特定区域,那么读出的数据极有可能因为读写操作的交错而不完整

代码演示

写入冲突
//写入冲突演示
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
int main(int argc,char* argv[]){
    //打开文件
    int fd = open("./conflict.txt",O_WRONLY | O_CREAT |O_APPEND,0664);
    if(fd == -1){
        perror("open");
        return -1;
    }
    //写入数据 
    for(int i = 0;i < strlen(argv[1]);i++){
        if(write(fd,&argv[1][i],sizeof(argv[1][i])) == -1){
            perror("write");
            return -1;
        }
        sleep(1);
    }
    //关闭文件
    close(fd);
    return 0;
}

        同时执行,分别执行写入hello和world,接下来看写入结果

写入结果
 hewlolrold

文件锁的使用

#include <fcntl.h>
int fcntl(int fd,F_SETLK/F_SETLKW,
struct flock* lock);
->功能:加解锁
->参数:F_SETLK 非阻塞模式加锁F_SETLKV,阳塞模式加锁
            lock 对文件要加的锁
->返回值:成功返回0,失败返回-1

struct flock {
short l_type; //锁类型:F _RDLCK/F_WRLCK/F_UNLCK

short l_whence; // 锁区偏移起点:SEEK_SET/SEEK_CUR/SEEK_END

off_t I_start; // 锁区偏移字节
off_t  l_len; // 锁区字节数
pid_t I_pid;//加锁进程的PID,-1表示自动设置

};
通过对该结构体类型变量的赋值,再配合fcntl函数,以完成对文件指定区域的加解锁操作

 阻塞模式加锁

代码实现
//写入冲突演示
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
int main(int argc,char* argv[]){
    //打开文件
    int fd = open("./conflict.txt",O_WRONLY | O_CREAT |O_APPEND,0664);
    if(fd == -1){
        perror("open");
        return -1;
    }
    //加锁
    struct flock lock;
    lock.l_type = F_WRLCK;//写锁
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;//一直锁到文件尾
    lock.l_pid = -1;
    if(fcntl(fd,F_SETLKW,&lock) == -1){
        perror("fcntl");
        return -1;
    }
    //写入数据 
    for(int i = 0;i < strlen(argv[1]);i++){
        if(write(fd,&argv[1][i],sizeof(argv[1][i])) == -1){
            perror("write");
            return -1;
        }
        sleep(1);
    }
    //解锁
    struct flock unlock;
    lock.l_type = F_UNLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;//一直锁到文件尾
    lock.l_pid = -1;
    if(fcntl(fd,F_SETLK,&unlock) == -1){
        perror("fcntl");
        return -1;
    }
    //关闭文件
    close(fd);
    return 0;
}
执行结果
hewlolrold
helloworld

非阻塞模式加锁

代码实现
//写入冲突演示
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
int main(int argc,char* argv[]){
    //打开文件
    int fd = open("./conflict.txt",O_WRONLY | O_CREAT |O_APPEND,0664);
    if(fd == -1){
        perror("open");
        return -1;
    }
    //加锁
    struct flock lock;
    lock.l_type = F_WRLCK;//写锁
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;//一直锁到文件尾
    lock.l_pid = -1;
    // if(fcntl(fd,F_SETLKW,&lock) == -1){
    //     perror("fcntl");
    //     return -1;
    // }
    //非阻塞加锁
    while(fcntl(fd,F_SETLK,&lock) == -1){
        if(errno == EACCES || errno == EAGAIN){
        printf("文件被锁定,请等待\n");
        sleep(1);
        }else{
            perror("fcntl");
            return -1;
        }
        
    }
    //写入数据 
    for(int i = 0;i < strlen(argv[1]);i++){
        if(write(fd,&argv[1][i],sizeof(argv[1][i])) == -1){
            perror("write");
            return -1;
        }
        sleep(1);
    }
    //解锁
    struct flock unlock;
    lock.l_type = F_UNLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;//一直锁到文件尾
    lock.l_pid = -1;
    if(fcntl(fd,F_SETLK,&unlock) == -1){
        perror("fcntl");
        return -1;
    }
    //关闭文件
    close(fd);
    return 0;
}
窗口一执行结果
day03$./wlock hello
day03$
窗口二执行结果
day03$./wlock world
文件被锁定,请等待
文件被锁定,请等待
文件被锁定,请等待
文件被锁定,请等待
文件被锁定,请等待
day03$cat conflict.txt
hewlolrold
helloworldhelloworldday03$

文件锁的内核结构

        每次对给定文件的特定区域加锁,都会通过fcntl函数向系统内核传递flock结构体,该结构体中包含了有关锁的一切细节,诸如锁的类型(读锁/写锁),锁区的起始位置和大小,甚至加锁进程的PID(填-1由系统自动设置)                                                                                                                          系统内核会收集所有进程对该文件所加的各种锁,并把这些flock结构体中的信息,以链表的形式组织成一张锁表,而锁表的起始地址就保存在该文件的v节点中
        任何一个进程通过fcntl函数对该文件加锁,系统内核都要遍历这张锁表,旦发现有与欲加之锁构成冲突的锁即阻塞或报错,否则即将欲加之锁插入锁表而解锁的过程实际上就是调整或删除锁表中的相应节点

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2064017.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++ 面试 - 面向对象】每日 3 题(八)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…

P(查准率) R(查全率) AP mAP最通俗准确的讲解

学习YOLO的过程中遇到了mAP指标&#xff0c;在网上看了很多关于mAP的讲解&#xff0c;不是很理解其计算过程&#xff0c;于是总结了各个帖子及自己的理解&#xff0c;给出mAP计算的规律&#xff0c;这样就能很好的记忆。 目录 一、P&#xff08;精确率&#xff09;、R&#x…

I2C通信协议(软件I2C和硬件I2C)

相比于之前学的异步全双工且需要两条通信线的串口通信&#xff0c;I2C则为同步半双工&#xff0c;仅需要一条通信线&#xff0c;全双工与半双工区别如下&#xff1a; 全双工&#xff08;Full Duplex&#xff09;半双工&#xff08;Half Duplex&#xff09;数据传输方式同时双向…

linux 磁盘满了,程序运行失败,如何处理?df -h

场景&#xff1a;紧急呼救&#xff0c;上传图片失败了。我一脸懵&#xff0c;服务器这是又咋地了&#xff0c;别邪乎姐姐&#xff0c;姐姐胆子小啊。 一、寻找问题原因 1、OSS出问题了&#xff1f; 然后我尝试了 IOS 的APP是没问题的&#xff0c;Android提示上传失败&#xf…

Excel技巧(一)

快捷键技巧 原文链接 选取某一行的数据直到最后一行&#xff1a;【CTRL SHIFT ↓ 】或者选取一行后按住SHIFT键&#xff0c;双击下边线就可以快速选取区域。 如果表格中有多行空行&#xff0c;可以先按CTRL SHIFT END&#xff0c;再按CTRL SHIFT 上下键调整&#xff0c;…

CodeLLDB的快速安装

1、CodeLLDB很难安装 ‌‌CodeLLDB插件是一个基于‌LLDB的调试器插件&#xff0c;专为‌Visual Studio Code设计&#xff0c;旨在提供类似于传统集成开发环境&#xff08;IDE&#xff09;的调试体验。‌ 它支持‌C、‌C和‌Objective-C程序的调试&#xff0c;包括设置断点、查…

这4款工具可以教会你如何用AI写ppt.

随着AI工具的盛行&#xff0c;AI办公工具也组建越来越多&#xff0c;我们可以用AI帮助生成各种文档&#xff0c;包括PPT。今天我就帮大家总结一波专业又好用的ai生成PPT的工具&#xff0c;一共有4款。 1、笔灵ppt 直通车&#xff1a;https://ibiling.cn/ppt-zone 首先要说的这…

颠茄和草本曼陀罗参考基因组(茄科)

茄科的代表性物种之二&#xff1a;颠茄和草本曼陀罗 来源文献 Revealing evolution of tropane alkaloid biosynthesis by analyzing two genomes in the Solanaceae family 推荐 惕佫酰假托品合酶的发现-文献精读28 参考链接 https://ngdc.cncb.ac.cn/gwh/Assembly/29467/s…

【Python零基础】Python中的函数

文章目录 前言一、函数定义二、传递实参三、返回值四、传递列表五、传递任意数量实参六、把函数存储在模块中七、函数定义指南总结 前言 在Python中&#xff0c;函数是一段可重用的代码块&#xff0c;用于执行特定任务。函数可以通过定义关键字 def 来创建&#xff0c;并且通常…

网络安全-防火墙安全策略初认识

文章目录 前言理论介绍1. 安全策略1.1 定义&#xff1a;1.2 关键术语&#xff1a; 2. 防火墙状态监测 实战步骤1&#xff1a;实验环境搭建步骤2&#xff1a;配置实现 总结1. 默认安全策略2. 自定义安全策略3. 防火墙状态会话表 前言 who&#xff1a;本文主要写给入门防火墙的技…

容器篇(Java - 集合)

目录 有意者可加 一、集合 1. 出现的背景 2. 带大家具体了解下集合 3. 集合带来了哪些好处 4. 集合的特点 5. 集合和数组对比 6. 数组和集合应用场景&#xff08;对比&#xff09; 6.1 数组的应用场景 1. 存储一组数据 2. 图像处理 3. 矩阵运算 4. 缓存 6.2 集合…

基于Python的mediapipe和opencv的人体骨骼、人体姿态关键点的实时跟踪项目

随着计算机视觉技术的发展&#xff0c;人体姿态估计在虚拟现实、运动分析、人机交互等领域得到了广泛应用。传统的姿态估计方法通常依赖于深度学习模型&#xff0c;需要大量的计算资源。而 Google 开发的 MediaPipe 框架则提供了高效且易于使用的解决方案&#xff0c;它可以在各…

78、 ansible----playbook

一、ansible模块 11、防火墙和网络模块&#xff1a; [roottest1 ~]# ansible 192.168.168.23 -m iptables -a chainINPUT protocolICMP source192.168.168.22 jumpREJECT -b ##-b后台&#xff0c;拒绝[roottest3 ~]# yum -y install nginx[roottest3 ~]# systemctl restart …

论文合集下载丨第十九届全国人机语音通讯学术会议

2024年8月15日至18日&#xff0c;第十九届全国人机语音通讯学术会议&#xff08;NCMMSC&#xff09;暨CCF语音对话与听觉专委会2024年学术年会在新疆乌鲁木齐成功召开。 会议论文集下载方式在文末 &#x1f447; 全国人机语音通讯学术会议作为语音技术领域内的专家、学者及科研…

为什么需要文献综述模板和创建文献综述技巧

为什么需要文献综述模板&#xff1f; 文献综述模板可以作为特定主题的指南。如果您的时间有限&#xff0c;无法进行更多研究&#xff0c;文献综述大纲示例可以为您提供帮助&#xff0c;因为它可以为您提供您打算研究的内容的概述。 甚至各个领域的专业人士也依赖文学评论来了解…

易灵思FPGA-Trion的MIPI设置使用

一、MIPI简介&#xff1f; 不需要各种资料上一大堆对物理层MIPI传输协议的讲解&#xff0c;实话说&#xff0c;我也不是特别能吃透其中的时序&#xff0c;所以不多研究&#xff1b;所有的FGPA如果带MIPI &#xff0c;那就只研究控制器就行了 &#xff0c;没必要舍近求远 二、硬…

NVM安装管理node.js版本(简单易懂)

一、前言 1.1 简介 NVM&#xff08;Node Version Manager&#xff09;是 node.js 的版本管理器&#xff0c;用 shell 脚本切换机器中不同版本的 nodejs。 Nodejs为什么需要多个版本&#xff1f; 有经验的开发者可能遇到过&#xff0c;某个依赖包明确nodejs是某个版本&#…

深度学习--RNN以及RNN的延伸

循环神经网络&#xff08;Recurrent Neural Network, RNN&#xff09;是一类能够处理序列数据的神经网络&#xff0c;在自然语言处理、时间序列分析等任务中得到了广泛应用。RNN能够通过其内部的循环结构&#xff0c;捕捉到序列中前后项之间的关系。下面我将从原理、作用、应用…

【AI绘画】Midjourney提示词详解:精细化技巧与高效实践指南

文章目录 &#x1f4af;Midjourney提示词基础结构1 图片链接1.1 上传流程 2 文字描述3 后置参数 &#x1f4af;Midjourney提示词的文字描述结构全面剖析1 主体主体细节描述2 环境背景2.1 环境2.2 光线2.3 色彩2.4 氛围 3 视角4 景别构图5 艺术风格6 图片制作方法7 作品质量万能…

振动分析-20-振动三要素的理解及决定设备分析水平的因素

1 对频谱分析的定位 一般咨询一个振动问题,很多人往往是要求把各种频谱贴上来看看,却很少有人问,振动过程、检修经历、设备结构、振动位置、振动方向、负荷关系、油温变化、转速影响等等。 刚刚接触振动检测时,对有关频谱分析方面资料如饥似渴,对于早期从事振动诊断的朋…