fileno()返回描述符号

每打开一个文件,操作系统都会在描述符表中新注册一项。假设你打开了某个文件:

  1. FILE *my_file = fopen("guitar.mp3", "r");

操作系统会打开guitar.mp3文件,然后返回一个指向它的指针,操作系统还会遍历描述符表寻找空项,把新文件注册在其中。

fileno()返回描述符号 - 图1

那么如何根据文件指针知道它是几号描述符呢?答案是调用fileno()函数。

fileno()返回描述符号 - 图2

在失败时不返回-1的函数很少,fileno()就是其中之一。只要你把打开文件的指针传给fileno(),它就一定会返回描述符编号。

dup2()复制数据流

每次打开文件都会使用描述符表中新的一项。但如果你想修改某个已经注册过的数据流,比如想让3号描述符重新指向其他数据流,该怎么做?可以用dup2()函数,dup2()可以复制数据流。假设你在4号描述符中注册了guitar.mp3文件指针,下面这行代码就能同时把它连接到3号描述符:

fileno()返回描述符号 - 图3

虽然guitar.mp3文件只有一个,与它相连的数据流也只有一条,但数据流(即FILE*)同时注册在了文件描述符3和4中。

既然你已经学会了如何在描述符表中查找文件和修改数据流,也就能把进程的标准输出重定向到某个文件。

还在为错误代码烦恼?

fileno()返回描述符号 - 图4

每次你在系统调用时都需要反反复复写那些错误处理代码。还犹豫什么!赶快使用我们的独家秘方,我们将向你展示如何重用错误代码,从此你将告别重复代码:

下面两段代码一看头就大:

fileno()返回描述符号 - 图5

有没有办法可以消除重复代码呢?当然有!只要创建一个error()函数,就可以一劳永逸。

error()函数是什么?这些return怎么处理?总不见得也移到error()函数里吧?

不需要!exit()系统调用是结束程序的最快方式。完全不用操心怎么返回主函数,直接调用exit(),你的程序就会灰飞烟灭!

首先,需要把处理代码放到一个单独的error()函数中,然后把return语句换成exit()系统调用。fileno()返回描述符号 - 图6

fileno()返回描述符号 - 图7

现在就可以把那些烦人的错误检查代码换成:

  1. pid_t pid = fork();
  2. if (pid == -1) {
  3. error("无法克隆进程");
  4. }
  5. if (execle(...) == -1) {
  6. error("无法运行脚本");
  7. }

这么做简单多了!

警告:每次程序执行只有一次调用exit()的机会,“程序突然结束恐惧症”患者慎用。

fileno()返回描述符号 - 图8磨笔上阵

程序把rssgossip.py脚本的输出保存到stories.txt文件中。程序只搜索一个RSS源,其他都和newshound一样。程序少了一行把子进程的标准输出重定向到stories.txt的代码,你能补出来吗?你可能用到描述符表的知识。

fileno()返回描述符号 - 图9

 

fileno()返回描述符号 - 图10磨笔上阵解答

程序把rssgossip.py脚本的输出保存到stories.txt文件中。程序只搜索一个RSS源,其他都和newshound一样。程序少了一行把子进程的标准输出重定向到stories.txt的代码,凭借对描述符表的了解,你把它补了出来。

fileno()返回描述符号 - 图11

你写对了吗?程序把子进程(脚本程序)的描述符表改成了这样:

也就是说当rssgossip.py把数据发往标准输出时,数据应该出现在stories.txt文件中。

# 数据流
0 键盘
1 stories.txt文件
2 屏幕
3 stories.txt文件

fileno()返回描述符号 - 图12试驾

编译运行程序,将看到:

fileno()返回描述符号 - 图13

发生了什么事?

当程序用fopen()打开stories.txt文件时,操作系统把文件f注册到了描述符表中,fileno(f)是文件f使用的描述符编号,而dup2()函数设置了标准输出描述符(1号),让它也指向了该文件。

fileno()返回描述符号 - 图14

fileno()返回描述符号 - 图15脑力风暴

假设RSS源中的确有你要找的新闻,可为什么程序结束以后stories.txt还是空的?