0723------Linux基础----------文件 IO 之 dup、dup2 和 fcntl 函数

1. dup 函数

  1.1 dup 函数用来复制一个文件描述符,复制后的文件描述符可以正常使用(见例1)。dup函数返回当前文件描述符表中一个最小的可用的文件描述符(Linux下分配文件描述符的规则是:寻找最小可用),这个过程由系统来完成。dup函数成功执行后,两个文件描述符fd_1 和 fd_2 指向同一个文件表项,因它们共享偏移量(文件数据结构图见Unix环境高级编程),在内核中的数据结构表示为:1个进程表项,1个文件表项(这里两个文件描述符指向同一个文件表项),1个V结点。文件表项中有一个属性是引用计数,调用dup后,两个文件描述符指向的是同一个文件表项,因而该表项中的引用计数为2,close 一个fd 的时候引用计数减1,只有当引用计数为 0 时,该文件表项才会被销毁。

  例1. dup 复制后的文件描述符可以正常使用。

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

/*
 * dup 的用法 复制文件描述符
 *
 */
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

int main(int argc, const char *argv[])
{
    int fd_1 = open("test.txt", O_RDONLY);
    if(fd_1 == -1){
        ERR_EXIT("open");
    }
    int fd_2 = dup(fd_1);

    printf("fd_1 = %d\t fd_2 = %d\n", fd_1, fd_2);
    char buf[1024] = {0}; //这里一定要初始化为0
    read(fd_2, buf, 1024);
    printf("buf:%s\n", buf);

    close(fd_1);
    close(fd_2);
    return 0;
}

  例2. dup复制后的两个描述符共享偏移量,因此二者交替读取时,文件内容连续。

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

/*
 * dup  复制后两个文件描述符指向同一个文件表项
 * 因而共享文件偏移量
 */
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

int main(int argc, const char *argv[])
{
    int fd_1 = open("test.txt", O_RDONLY);
    if(fd_1 == -1){
        ERR_EXIT("open");
    }
    int fd_2 = dup(fd_1);
    printf("fd_1 = %d\t fd_2 = %d\n", fd_1, fd_2);

    char buf[1024] = {0}; //从两个文件描述符表中交替读文件
    read(fd_1, buf, 3);
    printf("fd_1  buf:%s\n", buf);

    memset(buf, 0, sizeof(buf));
    read(fd_2, buf, 3);
    printf("fd_2  buf:%s\n", buf);

    close(fd_1); //关闭一个后另一个还能读

    memset(buf, 0, sizeof(buf));
    read(fd_2, buf, 3);
    printf("fd_2  buf:%s\n", buf);

    close(fd_2);
    return 0;
}

2. dup2

  2.1 dup2和dup 函数功能相同,都是复制文件描述符,但是二者的区别在于,dup由系统分配fd,而dup2由手工指定。如果dup2指定的文件描述符已经被占用,那么会先关闭该fd;如果二者相等,那么仍然返回该fd。

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

/*
 *  dup2用于复制时 可以手工指定被复制的fd
 *  如果该fd 已经被占用,那么要先关闭
 */
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

int main(int argc, const char *argv[])
{
    int fd_1 = open("test.txt", O_RDONLY);
    if(fd_1 == -1){
        ERR_EXIT("open");
    }
    int fd_2 = 5;
    dup2(fd_1, fd_2);

    char buf[1024] = {0};
    read(fd_2, buf, 20);
    printf("fd_2  buf:%s\n", buf);

    close(fd_1);
    close(fd_2);
    return 0;
}

  2.2 两个要注意的点

    a)两个常用命令 od -c filename 以ASCII 码显示文件的内容; du -h filename 显示文件中在磁盘中的大小,其中od 可以指定多种格式 如二进制 十六进制 浮点数 等等。

    b) 数组如果不初始化为0,里面存放的是随机的值。因此定义的时候一定要初始化为0。

3.重定向标准输入输出

  3.1实现标准流重定向是通过复制fd实现的。复制fd有三种方法:

    a) dup

    b) dup2

    c) fcntl,设置选项为F_DUPFD

  3.2 用 dup  重定向标准输入

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

/*
 * 重定向标准输入
 */
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

int main(int argc, const char *argv[])
{
    int fd_1 = open("test.txt", O_RDONLY);
    if(fd_1 == -1){
        ERR_EXIT("open");
    }

    close(STDIN_FILENO);
    int fd_2 = dup(fd_1);

     //此时0 和 3 指向同一个文件表项
    //printf("fd_1 = %d\t fd_2 = %d\n", fd_1, fd_2); // fd_1 = 3 fd_2 = 0

    char buf[1024] = {0};
    fgets(buf, 1024, stdin);
    fputs(buf, stdout);

    close(fd_1);
    return 0;
}

  3.3 用 dup2 重定向标准输入

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

/*
 *用 dup2 重定向标准输入
 */
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

int main(int argc, const char *argv[])
{
    int fd_1 = open("test.txt", O_RDONLY);
    if(fd_1 == -1){
        ERR_EXIT("open");
    }

    dup2(fd_1, STDIN_FILENO);
    //  这里 首先 关闭 STDIN_FILENO ,  然后将该fd 指向 fd_1

    char buf[1024] = {0};
    fgets(buf, 1024, stdin);
    fputs(buf, stdout);

    close(fd_1);
    return 0;
}

  3.4 用 fcntl 重定向标准输入

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

/*
 *用 fcntl 重定向标准输入
 */
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

int main(int argc, const char *argv[])
{
    int fd_1 = open("test.txt", O_RDONLY);
    if(fd_1 == -1){
        ERR_EXIT("open");
    }

    close(STDIN_FILENO);
    int fd_2 = fcntl(fd_1, F_DUPFD, 0);

    char buf[1024] = {0};
    fgets(buf, 1024, stdin);
    fputs(buf, stdout);

    close(fd_1);
    return 0;
}

 4. fcntl 函数

  4.1 将标准输入描述符设置为非阻塞。这里解释一下阻塞和非阻塞。以本例中的read函数为例,默认情况下,STDIN_FILENO是阻塞的,即如果当前没有数据可读时,本进程会进入睡眠状态,一直等待,知道有数据可读时才返回。而当我们用fcntl 将该描述符设置为非阻塞时,如果当前没有数据可读,read 函数会立即返回-1,并设置相应的errno值。

  

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

/*
 * 设置标准输入为非阻塞
 *
 */
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

int main(int argc, const char *argv[])
{
    int flag = fcntl(STDIN_FILENO, F_GETFL, 0);
    flag |= O_NONBLOCK;

    fcntl(STDIN_FILENO, F_SETFL, flag);

    char buf[1024] = {0};
    int read_n = read(STDIN_FILENO, buf, 1024);
    if(read_n == -1){
        ERR_EXIT("read");
    }
    return 0;
}

  4.2 fcntl 的返回值根据第二个参数的不同而不同,比如当参数为F_DUPFD时,若成功,返回新的文件描述符;当参数为F_GETFL时,返回文件的状态标志,这里的文件状态标志是O_RDONLY,O_WRONLY,O_TRUNC 等等,增加一个标志和去掉一个标志的方式分别为 flag |= file_flag; flag &= ~file_flag;

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

/*
 *将设置非阻塞 和 阻塞封装成函数
 */
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

void set_nonblock(int fd);
void set_block(int fd);

int main(int argc, const char *argv[])
{

    set_nonblock(STDIN_FILENO);
    set_block(STDIN_FILENO);

    char buf[1024] = {0};
    int read_n = read(STDIN_FILENO, buf, 1024);
    if(read_n == -1){
        ERR_EXIT("read");
    }
    printf("buf: %s", buf);

    return 0;
}

void set_block(int fd){
    int flag = fcntl(fd, F_GETFL, 0);
    if(flag == -1){
        ERR_EXIT("fcntl getfl");
    }
    flag &= ~O_NONBLOCK;
    if(fcntl(fd, F_SETFL,flag) == -1){
        ERR_EXIT("fcntl setfl");
    }
}

void set_nonblock(int fd){
    int flag = fcntl(fd, F_GETFL, 0);
    if(flag == -1){
        ERR_EXIT("fcntl getfl");
    }
    flag |= O_NONBLOCK;
    if(fcntl(fd, F_SETFL, flag) == -1){
        ERR_EXIT("fcntl setfl");
    }
}

 

0723------Linux基础----------文件 IO 之 dup、dup2 和 fcntl 函数,布布扣,bubuko.com

时间: 2024-10-01 20:39:21

0723------Linux基础----------文件 IO 之 dup、dup2 和 fcntl 函数的相关文章

(待续)文件IO详解(十六)---fcntl函数详解

fcntl函数是用来在进程中实现对文件的多种操作的函数,通过不同的命令可以实现不同的操作.常用的操作有复制文件描述符.为文件设置建议锁和获取设置文件控制标志等. ======================================================= 函数原型: ======================================================= 操作一:复制文件描述符实现文件共享 函数参数: fd:要操作的文件描述符 cmd:F_DUPFD ar

0723------Linux基础----------文件 IO之文件的打开

1.文件的打开  1.1 open 和 fopen .open 返回的是文件描述符,而fopen 返回的是文件指针,二者的第二个参数也不同,一个是宏定义的,一个是字符串.因此在书写的时候要特别注意. int fd = open("test.txt", O_RDONLY); FILE *fp = fopen("test.txt", "r"); 1.2 FILE * 和 fd 之间的转化 1.2.1 从FILE *  转化成 fd. fileno函数

0723------Linux基础----------文件 IO 之 read 和 write (readn 、writen、readline)

1. readn 和 writen 1.1 基础巩固: read 和 write 函数的返回值 1.1.1 read 函数原型为:ssize_t  read(int fd, void* buf, size_t count); (这里的 void *在标准 C 中表示通用指针即任意类型的指针都可以对它赋值,ssize_t 是有符号整数)它的返回值如下: a)成功返回读取的字节数,这里可能等于 count 或者小于 count (当 count > 文件 size 的时候,返回实际读到的字节数):

linux内核文件IO的系统调用实现分析(open)

http://blog.chinaunix.net/uid-23969156-id-3086824.html 1.          引言      从事Linux环境工作2年有余,一直懵懵懂懂,1年前拜读了<莱昂氏UNIX源代码分析>一书,感觉自己的学习道路漫漫且修远.最近受chinaunix的精华文帖启发,拟将近来的部分内核调用分析笔记拿出来与各前辈先进共同探讨学习,以壮个人学习之路.      本部分主要讲述的是文件I/O操作的2.6.11内核版本实现,包括了主要的数据结构.宏定义和函数

Linu基础 文件IO(读写操作)

前言 本章讨论普通文件的读写.读写效率.简单介绍文件描述符.IO效率.文件共享和原子操作.dup.文件映射.临时文件. 文件描述符 在Linux系统中,打开的文件是用一个整数来表示的,表示打开文件的整数,称之为文件描述符.当需要往写数据/读数据时,读写函数都需要文件描述符作为参数,以便系统知道用户操作的时哪个文件. 文件基本操作 open/creat mode选项 解释 O_RDONLY 读方式打开 O_WRONLY 写方式打开 O_RDWR 读写方式打开 O_CREAT 创建文件,如果文件存在

Linux基础入门--IO重定向及管道

IO重定向及管道 一直都提到,程序:指令+数据 其实程序也有IO,数据的来源有多个地方:文件.外部 可用于输入的设备:文件(linux一切皆文件) 键盘设备.文件系统上的常规文件加载内容.网卡等: 可用于输出的设备:文件(linux一切皆文件) 显示器.文件系统上的常规文件输出保存.网卡等: 程序的数据流有三种: 输入的数据流:<-- 标准输入(stdin),是键盘: 输出的数据流:--> 标准输出(stdout),显示器: 错误输出流:  --> 错误输出(stderr),显示器: f

Linux基础之IO重定向及管道详解(涉及tr、tee命令)

我在刚开始接触IO重定向时,经历了由懂到不懂然后了然的过程,当然现在的我也不可能说对于IO重定向就彻底懂了,只是在解决一些问题或实现某些结果时不会因为IO重定向的问题而迷惑了.     什么叫IO重定向? 为了解释这个问题,我们要先明白什么叫IO,什么叫做程序的数据流. 什么叫IO? 在Linux或计算机领域IO指的是信息的输入和输出. 在Linux中可用于输入的设备:文件(Linux中一切皆文件) 具体包括:键盘设备.文件系统上的常规文件.网卡等. 在Linux中用于输出的设备:文件 具体包括

【C++】linux下头文件io.h的巨坑

摘要:采用 io.h 头文件提供的函数读取指定文件夹中多个文件(文件名没有规律) 系统配置:ubuntu16.04, cmake编译 读取文件的代码如下, void getFilesAll(string path, vector<string>& files) { //文件句柄 long hFile = 0; //文件信息 struct _finddata_t fileinfo; string p; if ((hFile = _findfirst(p.assign(path).appe

Linux基础 文件系统目录标准FHS

概述 本文前半部分介绍了 Linux 文件系统目录标准 FHS (Filesystem Hierarchy Standard).后半部分整理了个人学习 Linux 过程中涉及到的部分文件和路径,激励自己坚持更新,以期完善. 背景 由于Linux是遵循开源协议,任何人都可以根据Linux的核心代码制作和发行版本.如果每个人都按自己的喜好,在/目录下创建目录.存放文件,必将导致个人不能使用他人的linux系统.因为你根本不知道一些基本的配置和文件在哪里...这就造成了混乱,不利于Linux的发展.为