在linux下,一切皆文件。

文件描述符用于操作文件。

从shell中运行一个进程,默认会有3个文件描述符存在(0、1、2);)0表示标准输入,1表示标准输出,2表示标准错误。

一个进程当前有哪些打开的文件描述符可以通过/proc/进程ID/fd目录查看。

1、 dup函数

#include <unistd.h>
int dup(int oldfd);

功能:复制一个文件描述符

返回值:成功则返回一个新的文件描述符,失败则返回-1。

当复制成功时,返回值是当前进程可用的最小的文件描述符,返回的新文件描述符和参数oldfd指向同一个文件,

若有错误则返回-1,错误代码存于errno中。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> int main()
{
int fd = open("a.txt", O_RDWR | O_CREAT);
if(fd == -1)
{
perror("open");
exit(1);
}

printf("file open fd = %d\n", fd); // 找到进程文件描述表中第一个可用的文件描述符A
// 将参数指定的文件描述符B复制给A,并返回A
int fd2 = dup(fd);
if(fd2 == -1)
{
perror("dup");
exit(1);
} printf("dup fd = %d\n", fd2); char* buf = "hello";
char* buf1 = " world\n"; write(fd, buf, strlen(buf));
write(fd2, buf1, strlen(buf1)); close(fd);
return 0;
}
root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# ./ab
file open fd = 3
dup fd = 4
root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# cat a.txt
hello world
root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup#

2. dup2函数

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

功能:复制一个文件描述符,且指定文件描述符newfd为oldfd的复制版本。

返回值:成功返回newfd,失败返回-1。

dup2函数成功返回时,目标描述符(函数第二个参数,newfd)将变成源描述符(函数第一个参数,oldfd)的复制品,

即,两个文件描述符现在都指向同一个文件,并且是源描述符指向的文件。

若有错误则返回-1,错误代码存于errno中。

dup2详解:

①、如果newfd已经打开,则先将其关闭,再指定文件描述符newfd为oldfd的复制版本。
②、如果newfd等于oldfd,则dup2直接返回newfd, 而不关闭它。

//测试dup2函数

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> int main()
{
int fd = open("b.txt", O_RDWR | O_CREAT);
if(fd == -1)
{
perror("open");
exit(1);
} int fd1 = open("a.txt", O_RDWR);
if(fd1 == -1)
{
perror("open");
exit(1);
} printf("fd = %d\n", fd);
printf("fd1 = %d\n", fd1); int curfd = dup2(fd, fd1); //让fd1和fd同时指向b.txt
if(curfd == -1)
{
perror("dup2");
exit(1);
}
printf("current fd = %d\n", curfd);
char* buf = "hello\n";
char* buf1 = "world!\n";
write(fd, buf, strlen(buf));
write(fd1, buf1 , strlen(buf1)); close(fd);
close(fd1);
return 0;
}

编译运行:

root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# gcc dup2.c -o ab
root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# ./ab
fd = 3
fd1 = 4
current fd = 4
root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# cat b.txt
hello
world!
root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# cat a.txt
hello world
root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup#

实验前后,a.txt的内容未发生改变,同时根据实验后b.txt的内容,证实了dup2函数功能的有效性。

注意:
通过dup和dup2创建的文件描述符,不继承原文件描述符的close-on-exec和non-blocking属性。

细节补充:

int dup(int oldfd);

dup函数:函数执行成功时,新得到的文件描述符和oldfd,共享文件偏移量和文件状态。

共享偏移量测试代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> // off_t lseek(int fd, off_t offset, int whence);第二个参数是偏移量,第三个参数是起始地址,要注意区分
int main(int argc, char const *argv[])
{
int fd = open("b.txt", O_RDONLY); //1. 先复制fd,得到copyfd
int copyFd = dup(fd); //2. 然后对fd、copyfd中的一个文件描述符进行操作,观察另一个文件描述符的变化
//置fd对应的当前文件数据索引偏移量到文件尾
unsigned long offset = lseek(fd, 0, SEEK_END); // 打印偏移量
printf("fd = %d , 距离文件头的偏移量:%ld\n", fd, offset); //打印copyFd的文件数据索引偏移量
printf("copyFd = %d , 距离文件头的偏移量:%ld\n", copyFd, lseek(copyFd, 0, SEEK_CUR)); return 0;
}

编译运行:

root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# gcc dup_2.c -o ab
root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# ./ab
fd = 3 , 距离文件头的偏移量:14
copyFd = 4 , 距离文件头的偏移量:14
root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup#
root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup#

现象和结论:使用了dup,操纵fd的文件数据索引偏移,导致copyfd的偏移量也跟着移动到了文件末尾。

int dup2(int oldfd, int newfd);

dup2函数:函数执行成功时,新得到的文件描述符newfd和oldfd,共享文件偏移量和文件状态。

共享偏移量测试代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> int main(int argc, char const *argv[])
{
int fd = open("b.txt", O_RDONLY);
int newfd; //1. 获取有效的newfd
newfd = dup2(fd, 14); //2. 然后对fd、newfd中的一个文件描述符进行操作,观察另一个文件描述符的变化
//置fd对应的文件数据索引偏移到文件尾
unsigned long offset = lseek(fd, 0, SEEK_END); // 打印偏移量
printf("fd = %d , 距离文件头的偏移量:%ld\n", fd, offset); //打印newfd的文件数据索引偏移
printf("newfd = %d , 距离文件头的偏移量:%ld\n", newfd, lseek(newfd, 0, SEEK_CUR)); return 0;
}

编译运行:

root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# gcc dup_2.c -o ab
root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup# ./ab
fd = 3 , 距离文件头的偏移量:14
newfd = 14 , 距离文件头的偏移量:14
root@lmw-virtual-machine:/home/lmw/桌面/linux_system_program/dup2_dup#

现象和结论:使用了dup2,操纵fd的文件数据索引偏移,导致newfd的偏移量也跟着移动到了文件末尾。

.

系统编程-文件IO-dup和dup2系统调用的更多相关文章

  1. linux系统编程--文件IO

    系统调用 什么是系统调用: 由操作系统实现并提供给外部应用程序的编程接口.(Application Programming Interface,API).是应用程序同系统之间数据交互的桥梁. C标准函 ...

  2. 系统编程--文件IO

    1.文件描述符 文件描述符是一个非负整数,当打开一个现有文件或创建一个新文件时候,内核向进程返回一个文件描述符,新打开文件返回文件描述符表中未使用的最小文件描述符.Unix系统shell使用文件描述符 ...

  3. Linux系统编程--文件IO操作

    Linux思想即,Linux系统下一切皆文件. 一.对文件操作的几个函数 1.打开文件open函数 int open(const char *path, int oflags); int open(c ...

  4. Linux系统编程--文件描述符的复制dup()和dup2()【转】

    本文转载自:http://blog.csdn.net/tennysonsky/article/details/45870459 dup() 和 dup2() 是两个非常有用的系统调用,都是用来复制一个 ...

  5. Linux系统编程@文件操作(一)

    只总结了部分常用的内容,详细内容参考<UNIX环境高级编程>及相关书籍. Linux中文件编程可以使用两种方法 Linux系统调用(依赖于系统) C语言库函数(不依赖于系统) Linux系 ...

  6. 五、文件IO——dup 函数

    5.1 dup 函数---复制文件描述符 5.1.1 简单cat实现及输入输出重定向 io.c #include <sys/types.h> #include <sys/stat.h ...

  7. 系统编程--标准IO

    1.流和FILE对象 对于国际字符集,一个字符可以由一个以上的字节来表示.标准I/O文件流可以用来操作单字节和多字节(宽,wide)字符集.一个流的方向(orientation)决定了字符是以单字节还 ...

  8. 系统编程-文件IO-IO处理方式

    IO处理五种模型 .

  9. Linux系统编程@终端IO

    Linux系统中终端设备种类  终端是一种字符型设备,有多种类型,通常使用tty 来简称各种类型的终端设备.终端特殊设备文件一般有以下几种: 串行端口终端(/dev/ttySn ) ,伪终端(/dev ...

  10. linux系统编程:IO读写过程的原子性操作实验

    所谓原子性操作指的是:内核保证某系统调用中的所有步骤(操作)作为独立操作而一次性加以执行,其间不会被其他进程或线程所中断. 举个通俗点的例子:你和女朋友OOXX的时候,突然来了个电话,势必会打断你们高 ...

随机推荐

  1. 解决“网页源代码编码形式为utf-8,但爬虫代码设置为decode('utf-8')仍出现汉字乱码”的问题

    为了用爬虫获取百度首页的源代码,检查了百度的源代码,显示编码格式为utf-8 但这样写代码,却失败了-.. (这里提示:不要直接复制百度的URL,应该是http,不是https!!!) # 获取百度首 ...

  2. vue中封装api数据层访问层

    api封装的是通过封装get/post/jsonp等请求,使得页面无需直接访问后代而是调用相关方法直接获取相关的后代数据,避免过多的数据处理逻辑,将重点放在数据渲染上. 1,准备阶段 a,首先创建ap ...

  3. Python | 解决方案 | 多个文件共用logger,重复打印问题

    项目中封装了logging库为log.py,实现既把日志输出到控制台, 又写入日志文件文件. 环境:python3.7.3 项目中,多个文件共用logger,出现重复打印问题,解决流程记录如下: 文件 ...

  4. 【云服务器】记录使用腾讯云服务器搭建个人blog网站-【1】服务器配置

    服务器购买 第一次写博客,写的不好请见谅 腾讯云教育活动 配置还行,能搭建个网站了果断下单 选择系统 缺点(对我来说):参考于:人生不开窍:Windows Server各版本差异 不能安装window ...

  5. mybatis关于大于小于:元素内容必须由格式正确的字符数据或标记组成。

    首先是原因: mybatis中< >这两个符号会被识别为标签的开始和结束,用了就会报解析的错误 会报错类似下面这些 1.元素内容必须由格式正确的字符数据或标记组成. 2.Error cre ...

  6. 题解 WD与数列

    P5161 WD与数列 可以想到原条件是一个差分形式,所以我们对原数组差分.然后发现答案其实就是 \(\sum_{i<j} \min(lcp(i+1,j+1)+1,j-i)\). 这个东西先跑 ...

  7. linux学习(7):Linux最常用150个命令汇总

    Linux最常用150个命令汇总 线上查询及帮助命令(2个) man 查看命令帮助,命令的词典,更复杂的还有info,但不常用. help 查看Linux内置命令的帮助,比如cd命令. 文件和目录操作 ...

  8. U盘安装Ubuntu18.04系统

    U盘安装Ubuntu18.04系统 一.安装盘制作 根据您当前使用系统的不同,需要不同的工具制作 U 盘启动安装盘.目前主要有 Linux 系统和 Windows 系统两类. 1.Linux 系统 ( ...

  9. .NET 结果与错误处理利器 FluentResults

    前言 在项目开发中,方法返回的结果(成功或失败)对我们开发来说很重要.传统方法,如通过异常来指示错误或使用特定的返回类型(如布尔值加输出参数),虽然有效,但可能缺乏直观性和灵活性. FluentRes ...

  10. 【ElasticSearch】突破深度分页限制的分页方案

    一.场景需求 最近在忙一个新的项目,数据源是ES,但是功能就是对文档进行翻页查询 ES提供了分页查询,就是from + size深度查找,但是使用有限制,只能在1万条内 我和同事的意见是1万条之后的数 ...