linux系统编程之文件与I/O(五):文件的内核结构file和dup实现重定向

一、打开文件内核数据结构

1、一个进程打开两个文件

文件状态标志:读、写、追加、同步、非阻塞等

2、一个进程两次打开同一文件

3、两个进程打开同一文件

示例程序:

/*************************************************************************
    > File Name: file_share.c
    > Author: Simba
    > Mail: dameng34@163.com
    > Created Time: Sat 23 Feb 2013 02:34:02 PM CST
 ************************************************************************/
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.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, fd2;
    char buf1[1024] = {0};
    char buf2[1024] = {0};
    /* 进程控制块PCB
     * struct task {
     * ...
     * struct files_struct *files;
     * }
     * 同一个进程两次打开同一个文件,一个进程拥有的一个文件描述符表其中一个fd索引对应的指针指向一个
     * 文件表(包括文件状态(读写追加同步非阻塞等),当前文件偏移量,
     * 文件引用次数(当有两个fd指向同个文件表时引用计数为2,见dup,也可用于重定向),
     * 文件操作指针, V节点指针等)不共享,
     * V节点表(包括V节点信息(struct stat), i节点信息等)共享
     */
    /* 两个进程打开同一个文件的情况与上类同*/
    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("buf2=%s\n", buf2);
    write(fd2, "AAAAA", 5);

    memset(buf1, 0, sizeof(buf1));
    read(fd1, buf1, 5);
    printf("buf1=%s\n", buf1);
    close(fd1);
    close(fd2);

    return 0;
}

假设test.txt文件的内容是 ABCDEhello

测试如下:

simba@ubuntu:~/Documents/code/linux_programming/APUE/File_IO$ ./file_share  buf1=ABCDE buf2=ABCDE buf1=AAAAA

test.txt文件内容变成 ABCDEAAAAA

分析:由上图分析可知,一个进程两次打开同一文件,文件表是不共享的,即各有自己的文件偏移量和打开文件标志,所以两次read不同的fd都是从头开始读取,但V节点表是共享的,在fd2写入(同个文件表的read和write是共享偏移的)更改了inode指向的硬盘数据块,再次read fd1得到的也是更改后的值。

二、I/O重定向

当我们执行了dup(3)之后,系统选择一个空闲的文件描述符即4,这样就有两个文件描述符指向同个文件表,所以引用计数为2。利用dup等函数可以进行重定向的步骤是先close输入输出文件描述符,然后执行dup(fd), 这样输入输出文件描述符也指向fd指向的文件,这样就实现了重定向。此外dup2, fcntl 函数也可以实现,其实不使用这些函数,而直接close(0/1/2)完再open也可以实现。如下使用cat命令实现复制文件的功能:

/*************************************************************************
    > File Name: process_.c
    > Author: Simba
    > Mail: dameng34@163.com
    > Created Time: Sat 23 Feb 2013 02:34:02 PM CST
 ************************************************************************/
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h>
#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

int main(int argc, char *argv[])
{
    close(0);
    open("Makefile", O_RDONLY);
    close(1);
    open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664);

    execlp("cat", "cat", NULL);

    return 0;
}

现在标准输入是文件Makefile,标准输出是文件test.txt ,将当前进程替换成cat,则cat会从标准输入读而后输出到标准输出,即完成了copy的功能。

dup/fcntl 函数示例程序如下:

/*************************************************************************
    > File Name: file_dup.c
    > Author: Simba
    > Mail: dameng34@163.com
    > Created Time: Sat 23 Feb 2013 02:34:02 PM CST
 ************************************************************************/
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>

#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

/* dup dup2 fcntl */
int main(int argc, char *argv[])
{
    int fd;
    fd = open("test2.txt", O_WRONLY);
    if (fd == -1)
        ERR_EXIT("open error");
    /*
        close(1);
        dup(fd);
    */
    //  dup2(fd, 1);

    close(1);
    if (fcntl(fd, F_DUPFD, 0) < 0) //从0开始搜索可用的fd
        ERR_EXIT("fcntl error");
    printf("hello\n"); // 输出重定向到test2.txt
    return 0;
}

参考:《APUE》

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java呓语

The difference between @Autowired and @ResourceAutowired (由Spring提供)Resource(由J2EE提供)结论参考地址

我更偏向于使用 @Resource 注解在 Field上,这样可以省略 setter方法。 其次,这个注解由 J2EE 提供,可以在 J2EE 环境下通用,而...

1054
来自专栏用户2442861的专栏

GDB调试程序(二)

http://blog.csdn.net/haoel/article/details/2880

962
来自专栏源码之家

终于解决WIN7共享经常连不上的问题了。

1352
来自专栏码匠的流水账

命令行一键切换java版本的几种方式

java现在版本发布相对以前比较频繁了,java9还没来得及用,java10就出来了,2018年9月份java11也要来了,于是就有需求在笔记本电脑上装多个版本...

1381
来自专栏JMCui

Netty 系列二(传输).

    上一篇文章我们提到 Netty 的核心组件是 Channel、回调、Future、ChannelHandler、EventLoop,这篇文章主要是对 C...

1182
来自专栏芋道源码1024

注册中心 Eureka 源码解析 —— Eureka-Client 初始化(二)之 EurekaClientConfig

1. 概述 本文接《Eureka 源码解析 —— Eureka-Client 初始化(一)之 EurekaInstanceConfig》,主要分享 Eureka...

3407
来自专栏钟绍威的专栏

SpringMVC基本使用关于DisptacherServlet关于ServletContainerInitializer最简单配置接收参数路径变量表单变量

springMVC是一个MVC框架,他控制着请求相应的整个流程,从请求一进入到应用服务器到相应离开,都离不开mvc框架 请求在应用服务器中 先说说请求相应在应用...

2426
来自专栏懒人开发

AndroidStudio简单使用(一):基础面板

用android studio也比较长时间了 一直没有怎么总结过, 看了 徐宜生 的 android群英传: 神兵利器 后,才感觉到, 其实 除了代码以外...

4161
来自专栏Java学习123

调试Spring Boot项目时跳转到exitCurrentThread的问题

2667
来自专栏令仔很忙

Hibernate从入门到放弃(二)----和这个世界打声招呼(HelloWorld)

2)、新建项目hw_hibernate 3)、导入Jar包 请下载hibernate_lib.zip拷贝到lib目录下 4)、新建配置文件hiber...

611

扫码关注云+社区