(P7-P8)文件与IO:文件共享、dup、fcntl

2024-06-08 06:18
文章标签 io 文件共享 p8 p7 fcntl dup

本文主要是介绍(P7-P8)文件与IO:文件共享、dup、fcntl,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 1.文件共享
    • 2.复制文件描述符

1.文件共享

  • 打开文件内核数据结构
    (1)内核中如何表示打开的文件的?
    每个进程都有1个文件描述符表;
    refcnt表示文件当前的引用计数;
    stat状态返回的信息都保存在v节点信息上面;
    i节点信息保存了文件存放在磁盘的哪个位置;
    在这里插入图片描述

  • 一个进程两次打开同一个文件
    一个进程两次打开同一个文件,V节点信息是共享的;
    文件状态标志是不一样的;
    在这里插入图片描述

  • eg:代码:P7fileshare.c

#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 fd1;int fd2;char buf1[1024] = {0};char buf2[1024] = {0};fd1 = open("test.txt", O_RDONLY);if (fd1 == -1)ERR_EXIT("open error");read(fd1, buf1, 5);printf("buf1=%s\n", buf1);fd2 = open("test.txt", O_RDWR);if (fd2 == -1)ERR_EXIT("open error");read(fd2, buf2, 5);printf("buf1=%s\n", buf2);write(fd2, "AAAAA", 5);memset(buf1, 0, 5);read(fd1, buf1, 5);printf("buf1=%s\n", buf1);close(fd1);close(fd2);return 0;
}
  • 测试:
    一个进程两次打开同一个文件,这两次打开文件的文件表的文件状态标志、文件偏移位置的都是独立的,互不影响,相互独立
    在这里插入图片描述
    注意:V节点信息是共享的
    文件2向test.txt文件写入了AAAAA,会改变i节点信息所指向磁盘中的数据。
    但是偏移量是从5开始的,所以往后读就读到了AAAAA
    在这里插入图片描述

  • 两个进程打开同一个文件
    每个进程的文件表都是独立的。
    进程打开文件会返回空闲的描述符,所以第一个进程的fd=3,第二个fd=4;
    文件打开的状态标志和偏移量都不是共享的;
    在这里插入图片描述

2.复制文件描述符

  • 实现IO重定向
    复制文件描述符的含义:得到一个新的文件描述符指向同一个文件
    进程2得到一个空闲的描述符4与进程1的文件描述符3指向同一个文件,引用计数refcnt为2
    在这里插入图片描述
  • eg:代码:
#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 fd1;fd1 = open("test2.txt", O_RDONLY);if (fd1 == -1)ERR_EXIT("open error");//返回一个新的文件描述符,与fd指向同一个文件//当前新的文件描述符是1,因为将fd=1关闭了close(1);//将fd=1关闭了,所以返回的文件描述符的值为1,所以1和fd指向同一个文件dup(fd);//上面的2行,等价于dup2(fd,1);//当前的标准输出不再指向屏幕,而是指向了text2printf("hello\n");return 0;
}
  • 测试:已经重定向到文件中了
    在这里插入图片描述

  • 复制文件描述符有三种方法
    (1)dup
    搜索空闲的文件描述符(从0开始搜索可用的fd),与要复制的文件描述符指向同一个文件
    (2)dup2
    强制使用文件描述符,与要复制的文件描述符指向同一个文件(dup与close的结合)
    (3)fcntl:也具有复制fd的功能
    man 2 fcntl

int fcntl(int fd, int F_DUPFD, fd_start);
fd:要复制的fd
cmd:命令,复制的话使用F_DUPFD
从fd_start开始搜索可用的fdint fcntl(int fd, int cmd, ... /* arg */ );
功能:操作文件描述符,改变已打开的文件的属性
arg是操作选项,依据cmd而定
  • fcntl常用操作
    (1)复制文件描述符
    F_DUPFD (long)
    (2)文件描述符标志
    F_GETFD(void)
    F_SETFD(long)
    (3)文件状态标志
    F_GETFL(void)
    F_SETFL(long)
    (4)文件锁
    F_GETTLK
    F_SETLK,F_SETLKW
    文件锁的结构体如下:
struct flock {...文件锁类型:读锁(就是共享锁,读锁可被多个进程增加)、写锁(就是排他锁,只允许一个进程增加锁)、清除锁short l_type;    /* Type of lock: F_RDLCK,F_WRLCK, F_UNLCK */锁的范围:whence和start决定加锁的起始点、len决定锁的长度和范围                         short l_whence;  /* How to interpret l_start:SEEK_SET, SEEK_CUR, SEEK_END */off_t l_start;   /* Starting offset for lock */off_t l_len;     /* Number of bytes to lock */pid_t l_pid;     /* PID of process blocking our lock(F_GETLK only) */当前哪个进程阻塞当前的锁...};
  • eg:代码:P8fcntl.c
#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 fd1;fd1 = open("test.txt", O_RDONLY);if (fd1 == -1)ERR_EXIT("open error");// close(1);//将fd=1关闭了,所以返回的文件描述符的值为1,所以1和fd指向同一个文件// dup(fd);// //上面的2行,等价于dup2(fd,1);//fcntl模拟复制文件描述符close(1);//关闭标准输出,因为默认0,1,2已经被打开了//因为fcntl是从0搜索的,所以当前得到可以用的fd=1if (fcntl(fd, F_DUPFD, 0) <0)ERR_EXIT("fcntl with error");printf("hello\n");//输出到标准输出,标准输出已经重定位到test.txtreturn 0;
}
  • 测试:已经重定向到文件中了
    在这里插入图片描述

  • eg:代码:P8fcntl2.c

#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;int flags;//先获取fd的状态flags = fcntl(0, F_GETFL, 0);if (flags == -1)ERR_EXIT("fcntl get flag error");//再设置fd的状态,因为若直接获取,会移除以前fd的其它状态ret = fcntl(0, F_SETTFL, flags | O_NONBLOCK);if (flags == -1)ERR_EXIT("fcntl set flag error");//set_flag(fd, O_NONBLOCK);//clr_flag(fd, O_NONBLOCK);ret =read(0, buf, 1024);if (ret == -1)ERR_EXIT("read error");printf("buf=%s\n", buf);return 0;
}void set_flag(int fd, int flags)
{//先获取fd的状态int val;val = fcntl(fd, F_GETFL, 0);if (val == -1)ERR_EXIT("fcntl get flag error");val | =flags;//再设置fd的状态,因为若直接获取,会移除以前fd的其它状态ret = fcntl(fd, F_SETFL, val);if (flags == -1)ERR_EXIT("fcntl set flag error");
}//清除fd的状态
void clr_flag(int fd, int flags)
{//先获取fd的状态int val;val = fcntl(fd, F_GETFL, 0);if (val == -1)ERR_EXIT("fcntl get flag error");val & = ~flags;//再设置fd的状态,因为若直接获取,会移除以前fd的其它状态ret = fcntl(fd, F_SETFL, val);if (flags == -1)ERR_EXIT("fcntl set flag error");
}
  • 测试:fd文件状态被改成了非阻塞模式。所以产生了EAGAIN的错误
    在这里插入图片描述
    使用set_flag(fd, O_NONBLOCK);
    clr_flag(fd, O_NONBLOCK);的结果
    因为read从非阻塞变成阻塞的,read设置成阻塞的,只要获取到输出就会退出了
    在这里插入图片描述

  • eg:代码:P8fcntl3.c

#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, int 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.len = 0;//为0表示锁定整个文件if (fcntl(fd, F_SETLK, &lock) == 0){printf("lock success\n");printf("press any key to unlock\n");getchar();//按下任意键lock.l_type = F_UNLOCK;if (fcntl(fd, F_SETLK, &lock) == 0)printf("unlock success\n");elseprintf("unlock fail\n");}else{ERR_EXIT("lock fail\n");}//就算没解锁,当进程退出的时候也会释放锁return 0;}
  • 测试:
    进程1
    在这里插入图片描述
    进程2加锁失败
    在这里插入图片描述

  • eg:代码:P8fcntl4.c

#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, int 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.len = 0;//为0表示锁定整个文件if (fcntl(fd, F_SETLKW, &lock) == 0){printf("lock success\n");printf("press any key to unlock\n");getchar();//按下任意键lock.l_type = F_UNLOCK;if (fcntl(fd, F_SETLK, &lock) == 0)printf("unlock success\n");elseprintf("unlock fail\n");}else{ERR_EXIT("lock fail\n");}//就算没解锁,当进程退出的时候也会释放锁return 0;}
  • 测试:
    加锁还可以使用F_SETLKW
    F_SETLKW区别于F_SETLK的原因是:若锁被另外一个进程锁施加了,那么其它进程试图加锁就会阻塞,直到另外一个进程解锁,才能够返回
    在这里插入图片描述
    其它进程会阻塞等待别的进程释放锁
    在这里插入图片描述
    按下任意键释放锁
    在这里插入图片描述

  • Makefifile

.PHONY:clean all
CC=gcc
CFLAGS=-Wall -g
BIN=01fcntl 02fcntl 03fcntl
all:$(BIN)
%.o:%.c$(CC) $(CFLAGS) -c $< -o $@
clean:rm -f *.o $(BIN)

这篇关于(P7-P8)文件与IO:文件共享、dup、fcntl的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1041404

相关文章

虚拟机与物理机的文件共享方式

《虚拟机与物理机的文件共享方式》文章介绍了如何在KaliLinux虚拟机中实现物理机文件夹的直接挂载,以便在虚拟机中方便地读取和使用物理机上的文件,通过设置和配置,可以实现临时挂载和永久挂载,并提供... 目录虚拟机与物理机的文件共享1 虚拟机设置2 验证Kali下分享文件夹功能是否启用3 创建挂载目录4

Java IO 操作——个人理解

之前一直Java的IO操作一知半解。今天看到一个便文章觉得很有道理( 原文章),记录一下。 首先,理解Java的IO操作到底操作的什么内容,过程又是怎么样子。          数据来源的操作: 来源有文件,网络数据。使用File类和Sockets等。这里操作的是数据本身,1,0结构。    File file = new File("path");   字

springboot体会BIO(阻塞式IO)

使用springboot体会阻塞式IO 大致的思路为: 创建一个socket服务端,监听socket通道,并打印出socket通道中的内容。 创建两个socket客户端,向socket服务端写入消息。 1.创建服务端 public class RedisServer {public static void main(String[] args) throws IOException {

Java基础回顾系列-第七天-高级编程之IO

Java基础回顾系列-第七天-高级编程之IO 文件操作字节流与字符流OutputStream字节输出流FileOutputStream InputStream字节输入流FileInputStream Writer字符输出流FileWriter Reader字符输入流字节流与字符流的区别转换流InputStreamReaderOutputStreamWriter 文件复制 字符编码内存操作流(

android java.io.IOException: open failed: ENOENT (No such file or directory)-api23+权限受权

问题描述 在安卓上,清单明明已经受权了读写文件权限,但偏偏就是创建不了目录和文件 调用mkdirs()总是返回false. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.READ_E

JavaEE-文件操作与IO

目录 1,两种路径 二,两种文件 三,文件的操作/File类: 1)文件系统操作 File类 2)文件内容操作(读文件,写文件) (1)打开文件 (2)关闭文件 (3)读文件/InputStream (4)写文件/OutputStream (5)读文件/reader (6)写文件/writer (7)Scanner 四,练习: 1,两种路径 1)绝对路径

Python---文件IO流及对象序列化

文章目录 前言一、pandas是什么?二、使用步骤 1.引入库2.读入数据总结 前言 前文模块中提到加密模块,本文将终点介绍加密模块和文件流。 一、文件流和IO流概述         在Python中,IO流是用于输入和输出数据的通道。它可以用于读取输入数据或将数据写入输出目标。IO流可以是标准输入/输出流(stdin和stdout),也可以是文件流,网络流等。

标准IO与系统IO

概念区别 标准IO:(libc提供) fopen fread fwrite 系统IO:(linux系统提供) open read write 操作效率 因为内存与磁盘的执行效率不同 系统IO: 把数据从内存直接写到磁盘上 标准IO: 数据写到缓存,再刷写到磁盘上

Linux函数fcntl/system学习

本文针对项目中用到的几个函数进行详细分析,并尽可能的添加示例进行验证学习。比如fcntl/ioctl函数、system/exec函数、popen/pclose函数、mmap函数等。 重点参考了《UNP》和《Linux程序设计》第四版。 一、fcntl函数 fcntl函数可以改变或者查看已打开文件的性质。该函数的定义如下: #include <fcntl.h> int fcntl(

linux基础IO——动静态库——进程编址、进程执行、动态库加载

前言:本节内容为基础IO部分的最后一节, 主要是为了讲一下动静态库里面的动态库如何加载到内存, 动态库的地址等等。 但是,这些内容牵扯到了程序的编址, 程序的加载, 进程的执行等等知识点, 所以,我们会从程序的编址讲起, 一直到进程的执行, 以及动态库加载结束。         ps:本节内容涉及到了进程地址空间, 磁盘的内容, 建议友友们了解相关知识后再来观看。 目录