做电商网站微信号是多少,tp框架做响应式网站,外贸网站建设不可缺少的灵活性,云岭建设集团的网站[Linux]文件描述符 文章目录 [Linux]文件描述符文件系统接口open函数close函数write函数read函数系统接口与编程语言库函数的关系 文件描述符文件描述符的概念文件数据交换的原理理解“一切皆文件”进程默认文件描述符文件描述符和编程语言的关系 重定向输出重定向输入重定向追…[Linux]文件描述符 文章目录 [Linux]文件描述符文件系统接口open函数close函数write函数read函数系统接口与编程语言库函数的关系 文件描述符文件描述符的概念文件数据交换的原理理解“一切皆文件”进程默认文件描述符文件描述符和编程语言的关系 重定向输出重定向输入重定向追加重定向使用指令完成重定向重定向系统调用 C语言文件缓冲区 文件系统接口
在学习文件描述符前首先要了解一下Linux系统常用的文件系统接口。
open函数
//open函数所在的头文件和函数声明
#include sys/types.h
#include sys/stat.h
#include fcntl.hint open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);open函数有两个接口三个参数的接口是在两个参数的接口的基础上添加了控制创建文件的权限功能更适合写文件时使用pathname参数 – 要打开的文件所在的路径flags参数 – 打开文件的方式mode参数 – 如果要创建文件文件的权限函数成功返回一个新的文件描述符函数失败返回-1,错误码被设置。
其中flags参数采用位图结构来接收要操作文件的打开模式位图结构在这里的作用是只使用一个参数就可以传入多个标志信息位图结构的具体形式是将flags参数的这样一个32位的整形变量中的每一个比特位都看作为一个个体每个比特位都代表一个标志信息位图结构的示意图如下: 假设flags参数的倒数第一比特位表示是否清空文件如果该比特位为1表示需要清空文件如果为0则表示不需要清空文件。
为了测试open函数编写如下代码:
#include stdio.h
#include errno.h
#include string.h
#include stdlib.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h#define LOG log.txtint main()
{int fd open(LOG, O_WRONLY);if (fd -1){printf(fd: %d, errno: %d, errstring: %s\n, fd, errno, strerror(errno));exit(1);}return 0;
}编译代码运行并查看结果 由于当前路径下不存在log.txt文件O_WRONLY打开模式是以读的方式打开文件因此open函数使用失败从错误信息中也可以看到失败原因是文件不存在。
再编写如下代码测试open函数:
#include stdio.h
#include errno.h
#include string.h
#include stdlib.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h#define LOG log.txtint main()
{int fd open(LOG, O_WRONLY | O_CREAT);if (fd -1){printf(fd: %d, errno: %d, errstring: %s\n, fd, errno, strerror(errno));exit(1);}return 0;
编译代码运行并查看结果 执行程序后我们能够发现由于文件权限是存在问题的因此文件名被高亮显式了由于本次使用的是O_WRONLY | O_CREAT 模式打开 O_CREAT模式是如果文件不存在就创建文件但是由于没有设置权限因此文件权限存在问题。
再编写如下代码测试open函数:
#include stdio.h
#include errno.h
#include string.h
#include stdlib.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h#define LOG log.txtint main()
{umask(0);int fd open(LOG, O_WRONLY | O_CREAT, 0666);if (fd -1){printf(fd: %d, errno: %d, errstring: %s\n, fd, errno, strerror(errno));exit(1);}else printf(fd: %d, errno: %d, errstring: %s\n, fd, errno, strerror(errno));return 0;
}编译代码运行并查看结果 三参数的open函数接口第三个接口提供了创建文件是设置文件权限的功能另外本段代码中使用了umask函数设置了该进程所使用的umask值保证所创建的文件权限是想获得的。
//umask函数所在的头文件和函数声明
#include sys/types.h
#include sys/stat.hmode_t umask(mode_t mask);close函数
//close函数所在的头文件和函数声明
#include unistd.hint close(int fd);close函数关闭文件能够避免文件内容出现问题close函数要关闭对应文件要传入使用open函数打开该文件时的返回值文件描述符成功返回0失败返回-1并且设置错误码
编写如下代码测试close函数:
#include stdio.h
#include errno.h
#include string.h
#include stdlib.h
#include unistd.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h#define LOG log.txtint main()
{umask(0);int fd open(LOG, O_WRONLY | O_CREAT, 0666);if (fd -1){printf(fd: %d, errno: %d, errstring: %s\n, fd, errno, strerror(errno));exit(1);}else printf(fd: %d, errno: %d, errstring: %s\n, fd, errno, strerror(errno));close(fd);return 0;
}编译代码运行并查看结果 write函数
//write函数所在的头文件和函数声明
#include unistd.hssize_t write(int fd, const void *buf, size_t count);write函数能够将数据写入相应的文件中fd参数 – 用open函数打开该文件时的返回值文件描述符buf参数 – 要写入文件的数据count参数 – 要写入到文件中的数据字节数成功返回写入的数据字节数失败返回-1并且设置错误码
编写如下代码测试write函数:
#include stdio.h
#include errno.h
#include string.h
#include stdlib.h
#include unistd.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h#define LOG log.txtint main()
{umask(0);int fd open(LOG, O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd -1){printf(fd: %d, errno: %d, errstring: %s\n, fd, errno, strerror(errno));exit(1);}else printf(fd: %d, errno: %d, errstring: %s\n, fd, errno, strerror(errno));const char* msg hello world\n;write(fd, msg, strlen(msg)); close(fd);return 0;
}编译代码运行并查看结果 说明
O_TRUNC模式是在打开文件时先清空文件向文件写入数据时不需要将\0写入其作用是在语言上表示字符串的结尾在文件中不需要。
再编写如下代码测试write函数:
#include stdio.h
#include errno.h
#include string.h
#include stdlib.h
#include unistd.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h#define LOG log.txtint main()
{umask(0);int fd open(LOG, O_WRONLY | O_CREAT | O_APPEND, 0666);if (fd -1){printf(fd: %d, errno: %d, errstring: %s\n, fd, errno, strerror(errno));exit(1);}else printf(fd: %d, errno: %d, errstring: %s\n, fd, errno, strerror(errno));const char* msg hello world i love you\n;write(fd, msg, strlen(msg));close(fd);return 0;
}编译代码运行并查看结果 说明 O_APPEND模式是采用追加的方式需要和O_WRONLY模式配合使用才能实现追加写入。
read函数
//read函数所在的头文件和函数声明
#include unistd.hssize_t read(int fd, void *buf, size_t count);read函数能够读取文件中的数据fd参数 – 要读取的文件的文件描述符buf参数 – 接收文件中数据的字符串变量地址count参数 – buf能接收的最大数据量单位为字节
#include stdio.h
#include errno.h
#include string.h
#include stdlib.h
#include unistd.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h#define LOG log.txtint main()
{int fd open(LOG, O_RDONLY);if (fd -1){printf(fd: %d, errno: %d, errstring: %s\n, fd, errno, strerror(errno));exit(1);}else printf(fd: %d, errno: %d, errstring: %s\n, fd, errno, strerror(errno));char buffer[256];ssize_t n read(fd, buffer, sizeof(buffer)-1);if (n 0){buffer[n] \0;}printf(buffer: %s, buffer);close(fd);return 0;
}编译代码运行并查看结果 说明
O_RDONLY模式是以读取的方式打开文件用于接收read函数读取到的数据的字符串要在末尾预留一个元素存储\0
系统接口与编程语言库函数的关系
由于计算机的体系结构中操作系统是对上向用户提供服务向下管理硬件的部分并且文件的操作是会涉及到硬件的使用导致编程语言语言库函数中的文件操作一定是对系统接口的封装无论任何编程语言都是如此因此无论何种编程语言其文件操作的本质都是相同的。计算机体系结构示意图如下 文件描述符
文件描述符的概念
在学习文件描述符前要知道观察Linux操作系统的文件系统接口能够看出文件操作和文件描述符强相关open函数的返回值close函数、write函数、read函数的参数都是文件描述符。
首先进行文件操作时操作系统不会将磁盘中的文件加载到内存中而是在内存中创建一个描述文件的结构如下图struct file该结构会管理一个缓冲区内存和磁盘文件的数据交换都是通过这个缓冲区。示意图如下 其次由于文件操作是由进程调用了系统接口完成的因此进程控制块如下图struct task_struct需要记录这些描述文件的结构因此进程控制块会申请一块创建一个结构如下图struct files_struct描述进程操作的文件然后在进程控制块中使用一个变量如下图struct files_struct*记录这个进程操作的文件的地址。示意图如下 最后由于一个进程可以操作的文件数量众多因此描述进程操作的文件的结构如下图struct files_struct中会用一个数组如下图struct file* fd_array[]记录所有该进程操作的文件的描述结构如下图struct file的地址而这个数组的下标就是文件描述符。示意图如下 说明 文件描述符的存在使得进程管理和文件管理处于轻耦合的状态二者之间只是使用了文件描述结构的指针联系在了一起。 文件数据交换的原理 进程调用系统接口write之后操作系统会将要传入到磁盘文件的数据拷贝至该文件描述结构中管理的缓冲区然后操作系统会根据自身的刷新策略在合适的时候将数据刷新到磁盘文件中。 进程调用系统接口read之后,操作系统会将磁盘文件中的数据拷贝至该文件描述结构中管理的缓冲区然后将该缓冲区中的内容拷贝至进程指定的位置中。
理解“一切皆文件”
在Linux操作系统中一切的外设都被看作是文件因此说“Linux下一切皆文件”。关于“一切皆文件”的理解如下
首先操作系统在管理硬件时并不是直接管理硬件设备的二者之间要通过驱动程序这一中间软件来完成交互外设与计算机的交互方式就是数据写入和读取因此在各个硬件对应的驱动程序中会存在该硬件的读写方法。示意图如下: 虽然驱动程序都设计了对应硬件的读写方法声明但并不是都有具体实现的比如键盘只有读取方法无法向键盘写入。
其次Linux操作系统会使用描述文件的结构如下图struct file来描述每一个外设并且该结构中会使用函数指针的形式来记录驱动程序所提供的读写方法然后操作系统要进行外设数据的写入时只需要将数据写入至文件对应的缓冲区然后使用函数指针调用对应的函数方法将数据写入写入外设操作系统读取外设数据时只需要调用函数指针调用对应的方法将数据写入文件对应的缓冲然后从缓冲区中读取数据。示意图如下 由于所有的外设都被统一的使用了描述文件的结构描述并且进程都是利用缓冲区交换数据调用对应的读写方法进行读写其操作方式和文件的操作方式相同因此从进程的角度看外设和磁盘文件是一样的因此才说“Linux下一切皆文件”。
进程默认文件描述符
进程在运行时会默认打开三个文件分别是标准输入、标准输出、标准错误。为了验证这编写如下代码:
#include stdio.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h#define LOG log.txtint main()
{int fd open(LOG, O_WRONLY | O_CREAT | O_TRUNC, 0666);printf(fd:%d\n, fd);close(fd);return 0;
}编译代码运行并查看结果 文件描述符代表的是记录进程操作文件的结构的数组的下标文件描述符的使用规则是从数组起始位置开始找到第一个没被使用的位置进程运行时默认打开了标准输入对应文件描述符为0、标准输出对应文件描述符为1、标准错误对应文件描述符为2因此再打开的文件的文件描述符是从3开始。所以能够看到上面的代码打开文件获取的文件描述符为3。
文件描述符和编程语言的关系
在C语言标准库中使用了struct FILE描述文件信息将标准输入命名为stdin,标准输出命名为stdout,标准错误命名为stderr,由于Linux操作系统的提供的文件操作接口都需要使用文件描述符因此struct FILE需要包含文件描述符字段。C语言进行进程文件操作和struct FILE强相关C语言库函数中提供的文件操作函数都会利用struct FILE中记录的文件描述符来调用Linux系统接口来完成。在Linux系统下C语言的struct FILE会有一个变量 _fileno记录文件描述符。为了验证这编写如下代码:
#include stdio.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h#define LOG log.txtint main()
{printf(stdin:%d\n, stdin-_fileno);printf(stdout:%d\n, stdout-_fileno);printf(stderr:%d\n, stderr-_fileno);FILE* fp fopen(LOG, w);printf(fp:%d\n, fp-_fileno);fclose(fp);return 0;
}编译代码运行并查看结果 在Linux操作系统中无论何种编程语言只要使用文件操作必然需要对文件描述符进行一定的封装来使用才能完成Linux操作系统下的文件操作。
重定向
输出重定向
输出重定向是将进程原本应该打印到显示器上的数据输出到文件中。
实现输出重定向的原理 输出函数是将文件描述符1作为参数调用系统接口函数实现的修改文件描述符表中1号位置指向的文件即可完成输出重定向。输出函数的封装中只是使用文件描述符1无法知晓输出位置的改变。落实到C语言中就是printf是向stdout输出数据但是stdout也只是封装了文件描述符1修改文件描述符表中的指向的文件printf也无法知晓。
为了模式实现输出重定向编写如下代码:
#include stdio.h
#include unistd.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h#define LOG log.txtint main()
{int fd close(1);open(LOG, O_WRONLY | O_CREAT | O_TRUNC, 0666);printf(hello world\n);printf(hello world\n);printf(hello world\n);close(fd);return 0;
}编译代码运行查看结果 输入重定向
输入重定向是将进程原本应该从键盘中获取数据变为从文件中获取数据。
实现输入重定向的原理 输入函数是将文件描述符0作为参数调用系统接口函数实现的修改文件描述符表中0号位置指向的文件即可完成输入重定向。输入函数的封装中只是使用文件描述符0无法知晓输入位置的改变。落实到C语言中就是scanf是从stdin获取数据但是stdin也只是封装了文件描述符0修改文件描述符表中的指向的文件scanf也无法知晓。
为了模式实现输入重定向编写如下代码:
#include stdio.h
#include unistd.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h#define LOG log.txtint main()
{close(0);int fd open(LOG, O_RDONLY);int a 0;int b 0;scanf(%d %d, a, b);printf(%d %d\n, a, b);close(fd);return 0;
}编译代码运行查看结果 追加重定向
追加重定向是将进程原本应该追加打印到显示器上的数据变为追加打印到文件中。
实现追加重定向的原理 将输出函数使用的文件描述符1指向改为指定文件并且在打开文件时使用追加的方式。
为了模式实现追加重定向编写如下代码:
#include stdio.h
#include unistd.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h#define LOG log.txtint main()
{close(1);int fd open(LOG, O_WRONLY | O_CREAT | O_APPEND, 0666);printf(hello world\n);printf(hello world\n);printf(hello world\n);close(fd);return 0;
}编译代码运行查看结果 说明 在实现重定向时也可以选择如下的输入或输出方式
fscanf(stdin, ...); //等效于scanf
fprintf(stdout, ...); //等效于printf使用指令完成重定向
Linux操作系统中可以使用指令完成重定向为了验证编写如下代码
#include stdio.hint main()
{printf(hello-printf\n);fprintf(stdout, hello-fprintf stdout\n);fprintf(stderr, hello-fprintf stderr\n);return 0;
}编译代码运行查看结果 ./myfile log.txt将./myfile的输出重定向到 log.txt文件中也就是将 log.txt的文件信息写入到该进程的文件描述符1中21是将该进程的文件描述符1的文件信息写入到文件描述符2中。
重定向系统调用
Linux操作系统中提供了能够重定向的系统调用dup2
//dup2函数所在的头文件和声明
#include unistd.hint dup2(int oldfd, int newfd);dup2函数会将输出到文件描述符newfd的数据改为输出到文件描述符oldfd对应的文件中。dup2函数会将文件描述符oldfd的文件信息写入到文件描述符newfd的文件信息中。
为了验证dup2编写如下代码
#include stdio.h
#include unistd.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h#define LOG log.txtint main()
{int fd open(LOG, O_WRONLY | O_CREAT | O_TRUNC);dup2(fd, 1);printf(this is printf\n);fprintf(stdout, this is fprintf-stdout\n);close(fd);return 0;
}编译代码运行查看结果 C语言文件缓冲区
在C语言标准库中提供的struct FILE中包含了缓冲区字段使用C语言库函数中的文件操作向文件写入数据C语言标准库只会将数据写入到对应文件的struct FILE中的缓冲区然后结合一定的刷新策略将缓冲区中的内容刷新到Linux操作系统中的文件缓冲区中。示意图如下
缓冲区刷新策略 无缓冲 – 数据不经过缓冲区直接写入操作系统 行缓冲 – 数据写入缓冲区后遇到\n,将\n及以前的数据写入操作系统 全缓冲 – 缓冲区写满后将数据写入操作系统 显示器采用的刷新策略行缓冲 普通文件采用的刷新策略全缓冲
将缓冲区中的数据刷新到操作系统需要使用系统调用而系统调用的使用要花费大量时间因此采用缓冲区刷新策略可以提高效率。
为了验证C语言struct FILE中的缓冲区编写如下代码
#include stdio.h
#include unistd.h
#include string.h#define LOG log.txtint main()
{fprintf(stdout, hello fprint-stdout\n);const char* msg hello write\n;write(1, msg, strlen(msg));fork();return 0;
}编译代码运行查看结果 由于不进行重定向时打印数据的位置是显示器采用的是行缓冲的策略因此数据会在创建子进程前被刷新到操作系统进行重定向后打印数据的位置是普通文件采用的是全缓冲的策略但数据不够写满缓冲区因此数据会在进程解说也就是创建子进程之后刷新而无论父子进程哪一个先刷新都会发生写时拷贝导致最终数据被刷新到操作系统两次。调用系统调用wrire是直接写入操作系统因此不受影响。 文章转载自: http://www.morning.pmbcr.cn.gov.cn.pmbcr.cn http://www.morning.mkpqr.cn.gov.cn.mkpqr.cn http://www.morning.gywfp.cn.gov.cn.gywfp.cn http://www.morning.mzkn.cn.gov.cn.mzkn.cn http://www.morning.krbjb.cn.gov.cn.krbjb.cn http://www.morning.rnzjc.cn.gov.cn.rnzjc.cn http://www.morning.ybgt.cn.gov.cn.ybgt.cn http://www.morning.bfmq.cn.gov.cn.bfmq.cn http://www.morning.qxlyf.cn.gov.cn.qxlyf.cn http://www.morning.twgzq.cn.gov.cn.twgzq.cn http://www.morning.pljdy.cn.gov.cn.pljdy.cn http://www.morning.jrhmh.cn.gov.cn.jrhmh.cn http://www.morning.tqjwx.cn.gov.cn.tqjwx.cn http://www.morning.nlgnk.cn.gov.cn.nlgnk.cn http://www.morning.rxhn.cn.gov.cn.rxhn.cn http://www.morning.tbstj.cn.gov.cn.tbstj.cn http://www.morning.krlsz.cn.gov.cn.krlsz.cn http://www.morning.lwqst.cn.gov.cn.lwqst.cn http://www.morning.fgqbx.cn.gov.cn.fgqbx.cn http://www.morning.mdpcz.cn.gov.cn.mdpcz.cn http://www.morning.mzwfw.cn.gov.cn.mzwfw.cn http://www.morning.sjpht.cn.gov.cn.sjpht.cn http://www.morning.jkwwm.cn.gov.cn.jkwwm.cn http://www.morning.ymhjb.cn.gov.cn.ymhjb.cn http://www.morning.fbzyc.cn.gov.cn.fbzyc.cn http://www.morning.djbhz.cn.gov.cn.djbhz.cn http://www.morning.wnbqy.cn.gov.cn.wnbqy.cn http://www.morning.tcylt.cn.gov.cn.tcylt.cn http://www.morning.ynbyk.cn.gov.cn.ynbyk.cn http://www.morning.rycd.cn.gov.cn.rycd.cn http://www.morning.wcgfy.cn.gov.cn.wcgfy.cn http://www.morning.kyflr.cn.gov.cn.kyflr.cn http://www.morning.qszyd.cn.gov.cn.qszyd.cn http://www.morning.smcfk.cn.gov.cn.smcfk.cn http://www.morning.mqmmc.cn.gov.cn.mqmmc.cn http://www.morning.ngcth.cn.gov.cn.ngcth.cn http://www.morning.kaakyy.com.gov.cn.kaakyy.com http://www.morning.rytps.cn.gov.cn.rytps.cn http://www.morning.nlqmp.cn.gov.cn.nlqmp.cn http://www.morning.dbhnx.cn.gov.cn.dbhnx.cn http://www.morning.rnzjc.cn.gov.cn.rnzjc.cn http://www.morning.ngkgy.cn.gov.cn.ngkgy.cn http://www.morning.zwfgh.cn.gov.cn.zwfgh.cn http://www.morning.qqbjt.cn.gov.cn.qqbjt.cn http://www.morning.qyjqj.cn.gov.cn.qyjqj.cn http://www.morning.lmjkn.cn.gov.cn.lmjkn.cn http://www.morning.owenzhi.com.gov.cn.owenzhi.com http://www.morning.plydc.cn.gov.cn.plydc.cn http://www.morning.snmsq.cn.gov.cn.snmsq.cn http://www.morning.bgxgq.cn.gov.cn.bgxgq.cn http://www.morning.xyhql.cn.gov.cn.xyhql.cn http://www.morning.nwczt.cn.gov.cn.nwczt.cn http://www.morning.rtspr.cn.gov.cn.rtspr.cn http://www.morning.fpkpz.cn.gov.cn.fpkpz.cn http://www.morning.kwrzg.cn.gov.cn.kwrzg.cn http://www.morning.bdzps.cn.gov.cn.bdzps.cn http://www.morning.qnbzs.cn.gov.cn.qnbzs.cn http://www.morning.rykx.cn.gov.cn.rykx.cn http://www.morning.clxpp.cn.gov.cn.clxpp.cn http://www.morning.ctqlq.cn.gov.cn.ctqlq.cn http://www.morning.ltbwq.cn.gov.cn.ltbwq.cn http://www.morning.lhxrn.cn.gov.cn.lhxrn.cn http://www.morning.xpmwt.cn.gov.cn.xpmwt.cn http://www.morning.srbbh.cn.gov.cn.srbbh.cn http://www.morning.wrbnh.cn.gov.cn.wrbnh.cn http://www.morning.xkqjw.cn.gov.cn.xkqjw.cn http://www.morning.rwbx.cn.gov.cn.rwbx.cn http://www.morning.rcrfz.cn.gov.cn.rcrfz.cn http://www.morning.xxhc.cn.gov.cn.xxhc.cn http://www.morning.kwyq.cn.gov.cn.kwyq.cn http://www.morning.lwmxk.cn.gov.cn.lwmxk.cn http://www.morning.pzrrq.cn.gov.cn.pzrrq.cn http://www.morning.dgsr.cn.gov.cn.dgsr.cn http://www.morning.rzcbk.cn.gov.cn.rzcbk.cn http://www.morning.ssqrd.cn.gov.cn.ssqrd.cn http://www.morning.fpxsd.cn.gov.cn.fpxsd.cn http://www.morning.cxryx.cn.gov.cn.cxryx.cn http://www.morning.mjats.com.gov.cn.mjats.com http://www.morning.kpzbf.cn.gov.cn.kpzbf.cn http://www.morning.zzbwjy.cn.gov.cn.zzbwjy.cn