当前位置: 首页 > 编程日记 > 正文

Linux文件,文件描述符以及dup()和dup2()

一.Linux中文件

可以分为4种:普通文件、目录文件、链接文件和设备文件。


1、普通文件


是用户日常使用最多的文件,包括文本文件、shell脚本、二进制的可执行和各种类型的数据。


ls -lh 来查看某个文件的属性,可以看到有类似 -rw-r--r-- ,值得注意的是第一个符号是 - ,这样的文件在Linux中就是普通文件。这些文件一般是用一些相关的应用程序创建,比如图像工具、文档工具、归档工具... .... 或 cp工具等。这类文件的删除方式是用rm 命令;


2、目录文件


在linux中,目录也是文件,它们是包含文件名和子目录名以及指向那些文件和子目录的指针


当我们在某个目录下执行,看到有类似 drwxr-xr-x ,这样的文件就是目录,目录在Linux是一个比较特殊的文件。注意它的第一个字符是d。创建目录的命令可以用 mkdir 命令,或cp命令,cp可以把一个目录复制为另一个目录。删除用rm 或rmdir命令。


3、链接文件


链接文件类似于Windows中的“快捷方式”。


是通过ln -s 源文件名 新文件名   来创建的。


4、设备文件


包括两种,块设备文件,另一种是字符设备文件


块设备文件是指数据的读写,它们是以块为单位的设备,如硬盘光驱


字符设备主要是指串行端口的接口设备,如网卡等。

二、文件描述符

1、文件描述符及其作用


内核(kernel)利用文件描述符(file descriptor)来访问文件。文件描述符是非负整数。打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。 对于 Linux 而言,所有对设备和文件的操作都使用文件描述符来进行的。文件描述符是一个非负的整数,它是一个索引值,并指向内核中每个进程打开文件的记录表。当打开一个现存文件或创建一个新文件时,内核就向进程返回一个文件描述符;当需要读写文件时,


也需要把文件描述符作为参数传递给相应的函数。


通常,一个进程启动时,都会打开 3 个文件:标准输入、标准输出和标准出错处理。这3 个文件分别对应文件描述符为 0、1和2也就是宏替换 STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO,鼓励读者使用这些宏替换)。



查看LINUX默认的文件描述符,总共有1024个,对于大多数情况下是够用的:

# ulimit -n


查看进程id

#ps aux


获取某进程文件描述符

cd /proc/[pid]/fd
[pid] 是对应的进程的pid.


#cd /proc/1473/fd



#sysctl -a | grep fs.file

nr就是已经用的

参考:百科http://baike.baidu.com/view/1303430.htm


三.dup和dup2

dup和dup2也是两个非常有用的调用,它们的作用都是用来复制一个文件的描述符。
它们经常用来重定向进程的stdin、stdout和stderr。
这两个函数的 原形如下:
#include <unistd.h>
int dup( int oldfd );
int dup2( int oldfd, int targetfd )
利用函数dup,我们可以复制一个描述符。传给该函数一个既有的描述符,它就会返回一个新的描述符,
这个新的描述符是传给它的描述符的拷贝。这意味着,这两个描述符共享同一个数据结构。例如,
如果我们对一个文件描述符执行lseek操作,得到的第一个文件的位置和第二个是一样的。
下面是用来说明dup函数使用方法的代码片段:
int fd1, fd2;
    ...
fd2 = dup( fd1 );


需要注意的是,我们可以在调用fork之前建立一个描述符,这与调用dup建立描述符的效果是一样的,
子进程也同样会收到一个复制出来的描述符。
dup2函数跟dup函数相似,但dup2函数允许调用者规定一个有效描述符和目标描述符的id。dup2函数成功返回时,
目标描述符(dup2函数的第二个参数)将变成源描述符(dup2函数的第一个参数)的复制品,换句话说,
两个文件描述符现在都指向同一个文件,并且是函数第一个参数指向的文件。下面我们用一段代码加以说明:      
int oldfd;
oldfd = open("app_log", (O_RDWR | O_CREATE), 0644 );
dup2( oldfd, 1 );
close( oldfd );
本例中,我们打开了一个新文件,称为“app_log”,并收到一个文件描述符,该描述符叫做fd1。我们调用dup2函数,
参数为oldfd和1,这会导致用我们新打开的文件描述符替换掉由1代表的文件描述符(即stdout,因为标准输出文件的id为1)。
任何写到stdout的东西,现在都将改为写入名为“app_log”的文件中。
需要注意的是,dup2函数在复制了oldfd之后,会立即将其关闭,但不会关掉新近打开的文件描述符,因为文件描述符1现在也指向它。
下面我们介绍一个更加深入的示例代码。回忆一下本文前面讲的命令行管道,在那里,我们将ls –1命令的标准输出作为标准输入
连接到wc –l命令。接下来,我们就用一个C程序来加以说明这个过程的实现。代码如下面的示例代码3所示。 
在示例代码3中,首先在第9行代码中建立一个管道,然后将应用程序分成两个进程:一个子进程(第13–16行)
和一个父进程(第20–23行)。接下来,在子进程中首先关闭stdout描述符(第13行),然后提供了ls –1命令功能,
不过它不是写到stdout(第13行),而是写到我们建立的管道的输入端,这是通过dup函数来完成重定向的。在第14行,
使用dup2 函数把stdout重定向到管道(pfds[1])。之后,马上关掉管道的输入端。然后,使用execlp函数把子进程的
映像替换为命令ls –1的进程映像,一旦该命令执行,它的任何输出都将发给管道的输入端。
现在来研究一下管道的接收端。从代码中可以看出,管道的接收端是由父进程来担当的。首先关闭stdin描述符(第20行),
因为我们不会从机器的键盘等标准设备文件来接收数据的输入,而是从其它程序的输出中接收数据。然后,再一次用到dup2函数(第21行),
让stdin变成管道的输出端,这是通过让文件描述符0(即常规的stdin)等于pfds[0]来实现的。关闭管道的stdout端(pfds[1]),
因为在这里用不到它。最后,使用 execlp函数把父进程的映像替换为命令wc -1的进程映像,命令wc -1把管道的内容作为它的输入(第23行)。
示例代码:利用C实现命令的流水线操作的代码
      #include <stdio.h>
     #include <stdlib.h>
     #include <unistd.h>
     
            int main()
           {
              int pfds[2];
     
              if ( pipe(pfds) == 0 ) {   //建立一个管道
     
                if ( fork() == 0 ) {   //子进程
     
                 close(1);     //关闭stdout描述符
                 dup2( pfds[1], 1 );   //把stdout重定向到管道(pfds[1])
                 close( pfds[0] );    //关掉管道的输入端
                  execlp( "ls", "ls", "-1", NULL ); //把子进程的映像替换为命令ls –1的进程映像
     
                } else {     //父进程
     
                 close(0);     //关闭stdin描述符
                  dup2( pfds[0], 0 );   //让stdin变成管道的输出端
                 close( pfds[1] );    //关闭管道的stdout端(pfds[1])
                  execlp( "wc", "wc", "-l", NULL ); //把父进程的映像替换为命令wc -1的进程映像
     
               }
     
            }
     
              return 0;
           }


在该程序中,需要格外关注的是,我们的子进程把它的输出重定向的管道的输入,然后,父进程将它的输入重定向到管道的输出。
这在实际的应用程序开发中是非常有用的一种技术。
1. 文件描述符在内核中数据结构
    在具体说dup/dup2之前, 我认为有必要先了解一下文件描述符在内核中的形态。
一个进程在此存在期间,会有一些文件被打开,从而会返回一些文件描述符,从shell
中运行一个进程,默认会有3个文件描述符存在(0、1、2), 0与进程的标准输入相关联,
1与进程的标准输出相关联,2与进程的标准错误输出相关联,一个进程当前有哪些打开
的文件描述符可以通过/proc/进程ID/fd目录查看。 下图可以清楚的说明问题:
  进程表项


————————————————
   fd标志 文件指针
       _____________________


fd 0:|________|____________|------------> 文件表


fd 1:|________|____________|


fd 2:|________|____________|


fd 3:|________|____________|


      |     .......         |


      |_____________________|


                图1
文件表中包含:文件状态标志、当前文件偏移量、v节点指针,这些不是本文讨论的
重点,我们只需要知道每个打开的文件描述符(fd标志)在进程表中都有自己的文件表
项,由文件指针指向。
2. dup/dup2函数
APUE和man文档都用一句话简明的说出了这两个函数的作用:复制一个现存的文件描述符。
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
从图1来分析这个过程,当调用dup函数时,内核在进程中创建一个新的文件描述符,此
描述符是当前可用文件描述符的最小数值,这个文件描述符指向oldfd所拥有的文件表项。
  进程表项


————————————————


   fd标志 文件指针


       _____________________


fd 0:|________|____________|                   ______


fd 1:|________|____________|----------------> |      |


fd 2:|________|____________|                  |文件表|


fd 3:|________|____________|----------------> |______|


      |     .......         |


      |_____________________|


                图2:调用dup后的示意图
如图2 所示,假如oldfd的值为1, 当前文件描述符的最小值为3, 那么新描述符3指向
描述符1所拥有的文件表项。
dup2和dup的区别就是可以用newfd参数指定新描述符的数值,如果newfd已经打开,则
先将其关闭。如果newfd等于oldfd,则dup2返回newfd, 而不关闭它。dup2函数返回的新
文件描述符同样与参数oldfd共享同一文件表项。
APUE用另外一个种方法说明了这个问题:
实际上,调用dup(oldfd);
等效与
        fcntl(oldfd, F_DUPFD, 0)
而调用dup2(oldfd, newfd);
等效与
        close(oldfd);
        fcntl(oldfd, F_DUPFD, newfd);
3. CGI中dup2
写过CGI程序的人都清楚,当浏览器使用post方法提交表单数据时,CGI读数据是从标准
输入stdin, 写数据是写到标准输出stdout(c语言利用printf函数)。按照我们正常的理
解,printf的输出应该在终端显示,原来CGI程序使用dup2函数将STDOUT_FINLENO(这个
宏在unitstd.h定义,为1)这个文件描述符重定向到了连接套接字。
dup2(connfd, STDOUT_FILENO); /*实际情况还涉及到了管道,不是本文的重点*/
如第一节所说, 一个进程默认的文件描述符1(STDOUT_FILENO)是和标准输出stdout相
关联的,对于内核而言,所有打开的文件都通过文件描述符引用,而内核并不知道流的
存在(比如stdin、stdout),所以printf函数输出到stdout的数据最后都写到了文件描述
符1里面。至于文件描述符0、1、2与标准输入、标准输出、标准错误输出相关联,这
只是shell以及很多应用程序的惯例,而与内核无关。
用下面的流图可以说明问题:(ps: 虽然不是流图关系,但是还是有助于理解)
printf -> stdout -> STDOUT_FILENO(1) -> 终端(tty)
printf最后的输出到了终端设备,文件描述符1指向当前的终端可以这么理解:
STDOUT_FILENO = open("/dev/tty", O_RDWR);
使用dup2之后STDOUT_FILENO不再指向终端设备, 而是指向connfd, 所以printf的
输出最后写到了connfd。是不是很优美?:)
4. 如何在CGI程序的fork子进程中还原STDOUT_FILENO
如果你能看到这里,感谢你的耐心, 我知道很多人可能感觉有点复杂, 其实
复杂的问题就是一个个小问题的集合。所以弄清楚每个小问题就OK了,第三节中
说道,STDOUT_FILENO被重定向到了connfd套接字, 有时候我们可能想在CGI程序
中调用后台脚本执行,而这些脚本中难免会有一些输入输出, 我们知道fork之后,
子进程继承了父进程的所有文件描述符,所以这些脚本的输入输出并不会如我们愿
输出到终端设备,而是和connfd想关联了,这个显然会扰乱网页的输出。那么如何
恢复STDOUT_FILENO和终端关联呢?
方法1:在dup2之前保存原有的文件描述符,然后恢复。
代码实现如下:
savefd = dup(STDOUT_FILENO); /*savefd此时指向终端*/
dup2(connfd, STDOUT_FILENO);   /*STDOUT_FILENO(1) 被重新指向connfd*/
..... /*处理一些事情*/
dup2(savefd, STDOUT_FILENO); /*STDOUT_FILENO(1) 恢复指向savefd*/
很遗憾CGI程序无法使用这种方法, 因为dup2这些不是在CGI程序中完成的,而是在
web server中实现的,修改web server并不是个好主意。
方法2: 追本溯源,打开当前终端恢复STDOUT_FILENO。
分析第三节的流图, STDOUT_FILENO是如何和终端关联的? 我们重头做一遍不就行
了, 代码实现如下:
ttyfd = open("/dev/tty", O_RDWR);
dup2(ttyfd, STDOUT_FILENO);
close(ttyfd);
/dev/tty是程序运行所在的终端, 这个应该通过一种方法获得。实践证明这种方法
是可行的,但是我总感觉有些不妥,不知道为什么,可能一些潜在的问题还没出现。

相关文章:

摩尔线程推出首款数据中心级全栈功能GPU:MTT S2000

2022年3月30日&#xff0c;北京——摩尔线程正式推出首款基于其先进架构MUSA统一系统架构&#xff08;Moore Threads Unified System Architecture&#xff09;打造的数据中心级多功能GPU产品MTT S2000。摩尔线程MTT S2000基于其第一代MUSA架构GPU芯片苏堤研发制成&#xff0c;…

jquery 获取 outerHtml 包含当前节点本身的代码

在开发过程中&#xff0c;jQuery.html() 是获取当前节点下的html代码&#xff0c;并不包含当前节点本身的代码&#xff0c;然后我们有时候确需要&#xff0c;找遍jQuery api文档也没有任何方法可以拿到。 看到有的人通过parent().html()&#xff0c;如果当前元素没有兄弟元素还…

修改CentOS yum源

解决在CentOS yum源下载慢的办法最近在虚拟机下面安装了个CentOS 5.5&#xff0c;使用yum更新时发现下载速度异常慢。可以修改yum的配置文件&#xff0c;把其镜像指向国内的服务器即可。 方案一&#xff1a; # cd /etc/yum.repos.d/ # mv CentOS-Base.repo CentOS-Base.repo.b…

带monkey的测流量!

为什么80%的码农都做不了架构师&#xff1f;>>> //public static void flow(){ //String command1 "adb shell monkey -p com.netease.newsreader.activity -s 500 -v 2000"; //String command2 "adb shell ps"; //String str"com.nete…

实现AI技术自立自强,国产深度学习框架面临三大难题

作为推动AI应用大规模落地的关键力量&#xff0c;深度学习框架的重要性日益凸显。它不仅关系国计民生的行业和领域广泛的应用&#xff0c;同样也对信息系统的科技安全有着决定性的意义。 “深度学习框架在人工智能技术体系中&#xff0c;处于贯通上下的腰部位置&#xff0c;它下…

关于Android H5混合开发遇到的问题

2019独角兽企业重金招聘Python工程师标准>>> 添加WebChromeClient&#xff0c;复写onJsAlert、onJsConfirm、onJsPrompt方法后&#xff0c;弹框异常退出问题 项目经理说&#xff0c;Android没有处理弹框&#xff0c;点击按钮没有反应&#xff0c;iOS就可以。于是就复…

手动配置lnmp环境

做php开发的&#xff0c;想要进一步提升自己&#xff0c;手动搭建开发环境&#xff0c;我想是必须经历的一个坎。虽然说有很多第三方集成环境可供使用&#xff0c;但我想说的是在你没有自己搭建过一次环境的时候&#xff0c;你没有太多的资本去“偷懒”。虽然我自己也是个菜鸟&…

负载均衡,会话保持,session同步

一&#xff0c;什么负载均衡一个新网站是不要做负载均衡的&#xff0c;因为访问量不大&#xff0c;流量也不大&#xff0c;所以没有必要搞这些东西。但是随着网站访问量和流量的快速增长&#xff0c;单台服务器受自身硬件条件的限制&#xff0c;很难承受这么大的访问量。在这种…

终于“打造”出了一个可以随时随地编程的工具

作者 | 老表来源丨简说Python分享概要 系统&#xff1a;阿里云ECS共享型n4服务器 1核2g 存储50g&#xff08;双十一便宜买的&#xff0c;180元/3年&#xff09;环境&#xff1a;自带python3.6.8 方便演示&#xff0c;直接使用它开始动手动脑 首先我们需要连接上服务器&#xff…

JVM堆 栈 方法区详解

一、栈 每当启用一个线程时&#xff0c;JVM就为他分配一个JAVA栈&#xff0c;栈是以帧为单位保存当前线程的运行状态 栈是由栈帧组成&#xff0c;每当线程调用一个java方法时&#xff0c;JVM就会在该线程对应的栈中压入一个帧 只有在调用一个方法时&#xff0c;才为当前栈分配一…

ECSHOP学习笔记

帮助 http://help.ecshop.com/index.phpECSHOP各文件夹功能说明 1、根目录&#xff1a;前台程序文件2、admin&#xff1a;后台程序文件夹 --根目录&#xff1a;后台程序文件 *.php文件 --help\zh_cn&#xff1a;各功能的帮助文件 *.xml文件 --images&#xff1a;后台页面…

Redis主从复制配置

环境描述Redis Master&#xff1a;192.168.1.100 6379(Ubuntu系统)Redis Slave1&#xff1a;192.168.1.101 6380(Ubuntu系统)Redis Slave2&#xff1a;192.168.1.102 6381(Ubuntu系统) 安装redis分别在192.168.1.100、192.168.1.101、192.168.1.102三台机器上安装redis&#xf…

利用 Python 打造一个语音合成系统

作者 | thedaydreamer来源丨CSDN博客背景一直对语音合成系统比较感兴趣&#xff0c;总想能给自己合成一点内容&#xff0c;比如说合成小说&#xff0c;把我下载的电子书播报给我听等等。语音合成系统其实就是一个基于语音合成的工具&#xff0c;但是这个东西由于很多厂家都提供…

干货:排名前 16 的 Java 工具类!

2019独角兽企业重金招聘Python工程师标准>>> 干货&#xff1a;排名前 16 的 Java 工具类&#xff01; 在Java中&#xff0c;工具类定义了一组公共方法&#xff0c;这篇文章将介绍Java中使用最频繁及最通用的Java工具类。以下工具类、方法按使用流行度排名&#xf…

使用ecshop电子商务系统的100个小问题

总结100条关于操作ecshop电子商务系统的小问题。1:如何修改网站"欢迎光临本店"回答:languages\zh_cn\common.php文件中&#xff0c; $_LANG[welcome] 欢迎光临本店;将他修改成你需要的字样。2:如何修改首页"热门搜索关键字"回答:后台->系统设置->网…

MyCAT常用分片规则之分片枚举

MyCAT支持多种分片规则&#xff0c;下面测试的这种是分片枚举。适用场景&#xff0c;列值的个数是固定的&#xff0c;譬如省份&#xff0c;月份等。 在这里&#xff0c;需定义三个值&#xff0c;规则均是在rule.xml中定义。 1. tableRule 2. function 3. mapFile 首先&#xff…

手把手带你打造一款 签名设计 的GUI图形界面!

作者 | 黄伟呢来源丨数据分析与统计学之美1.概述 整体布局呢我们已经搭建起来&#xff0c;唯一没有实现的一个步骤就是&#xff0c;用户每输入一个名字&#xff0c;就会将个性签名一并显示在这个窗口界面中&#xff0c;今天我就带着大家一起完成这个需求。今天的文章可以看成是…

跨域资源共享 CORS

简介 CORS是一个W3C标准&#xff0c;全称是"跨域资源共享"&#xff08;Cross-origin resource sharing&#xff09;。它允许浏览器向跨源服务器&#xff0c;发出XMLHttpRequest请求&#xff0c;从而克服了AJAX只能同源使用的限制。 CORS需要浏览器和服务器同时支持。…

SMARTY核心

http://www.smarty.net/http://smarty.php.net/manual/en/1.配置define("ROOTPATH",dirname(__FILE__)."/../");require_once("smarty/Smarty.class.php");/*** Smarty Template Class Initializtion*/if( constant( "ENABLED_TPL" ) …

5G+XR:让视频增强技术在工业领域大有所为

据工业和信息化部统计显示&#xff0c;目前中国累计建成并开通5G基站142.5万个&#xff0c;基站总数今年有望突破200万个。自5G正式商用以来&#xff0c;凭借其高带宽、广连接、低延时等优势&#xff0c;5G应用的实践逐渐从最初的单一化业务触及至更广泛的行业应用场景中。其中…

IE的安全性设定增加“我的电脑”的安全性设定

HKEY_CURRE-NT_USER\Software\Microsoft\Windows\CurrentVersion\InternetSettings\Zones\0&#xff0c;在右边窗口中找到DWORD值“Flags”&#xff0c;默认键值为十六进制的21(十进制33)&#xff0c;双击“Flags”&#xff0c;在弹出的对话框中将它的键值改为“1”即可&#x…

F# 4.5提供Spans、Match!等特性

F# 4.5预览版现已发布&#xff0c;其中提供了一系列新特性&#xff0c;包括对.NET Core 2.1的新原生类型Span\u0026lt;T\u0026gt;的支持、新关键字Match!等。\\类型Span意在实现底层代码指针操作的安全性和可预测性&#xff0c;这可使得很多情况下不必再分配内存&#xff0c;进…

ecshop transport.js/run() error:undefined

在使用ECshop的AJAX(即&#xff1a;transport.js) IE有时候会出现&#xff1a;ReferenceError: process_request is not defined&#xff0c;FF则出现&#xff1a;transport.js/run() error:undefined&#xff0c;其实这完全和transport.js无关。那么问题出在哪里呢&#xff1f…

为什么你不应该自行更新 Drupal 网站?

&#xff08;译注&#xff1a;这篇文章主要还是针对于非专业人员及个人Drupal站长&#xff0c;对于专业的 Drupal 团队和公司而言 Drupal 的升级更新都有规范的操作流程&#xff0c;完全是家常便饭&#xff0c;不可能出现文中出现的这些情况。尽管如此&#xff0c;里面也还是有…

用友发布新一代企业智能商旅及费控服务平台

3月31日&#xff0c;“便捷商旅 智能费控—2022用友BIP|商旅及费控服务新品发布会”成功举行。作为新一代企业智能商旅及费控服务平台&#xff0c;用友BIP商旅及费控服务以“连接 高效 智能 合规”为核心价值理念&#xff0c;致力于让5000万报销人拥有极致的体验&#xff0c;让…

RNN,LSTM,GRU简单图解:

一篇经典的讲解RNN的&#xff0c;大部分网络图都来源于此&#xff1a;http://colah.github.io/posts/2015-08-Understanding-LSTMs/ 每一层每一时刻的输入输出&#xff1a;https://www.cnblogs.com/lovychen/p/9368390.html 带有权重标识的图&#xff1a;

ecshop模板smarty foreach详解 [ECshop]

{foreach},{foreachelse}{foreach} 用于像循环访问一个数字索引数组一样循环访问一个关联数组&#xff0c;与仅能访问数字索引数组的{section}不同&#xff0c;{foreach}的语法比 {section}的语法简单得多&#xff0c;但是作为一个折衷方案也仅能用于单个数组。每个{foreach}标…

自己动手,做一款抬头显示的「Todo Hud」

我用过好多款 TodoList 软件&#xff0c;但事情一多总还是丢三落四&#xff0c;原本计划好要做的事情总是安静地躺在某个角落&#xff0c;等我想起来要去扫一眼的时候&#xff0c;都已快「物是人非」。。。 要是能在桌面上实时显示 TodoList&#xff0c;那该多好&#xff01;但…

微软语音扩展全球语言支持,发布160个新声音

导语&#xff1a;全世界有数千种语言&#xff0c;最具语言天赋的人也只能说数十种&#xff0c;普通人能够学会两三种语言已属不易。然而&#xff0c;在科技日新月异的今天&#xff0c;具备自然语言对话能力的AI已经能够掌握上百种语言&#xff0c;扩展人类自身能力&#xff0c;…

P4269 [USACO18FEB]Snow Boots G

思维题。 以地板为序构造链表&#xff0c;再排序&#xff0c;然后删除走不过去的地面。 删除的时候顺便维护最大的跨度&#xff0c;以此判断可行性。 总的来说利用了答案的单调性。 #include <cstdio> #include <cstring> #include <iostream> #include <…