网站开发电子书,网站建设需要哪些软件,cms管理手机网站模板下载,全国公路建设市场信用信息管理系统网站话接上篇#xff1a; 1.文件描述符fd 磁盘文件 VS 内存文件#xff1f; 当文件存储在磁盘当中时#xff0c;我们将其称之为磁盘文件#xff0c;而当磁盘文件被加载到内存当中后#xff0c;我们将加载到内存当中的文件称之为内存文件。磁盘文件和内存文件之间的关系就像程…话接上篇 1.文件描述符fd 磁盘文件 VS 内存文件 当文件存储在磁盘当中时我们将其称之为磁盘文件而当磁盘文件被加载到内存当中后我们将加载到内存当中的文件称之为内存文件。磁盘文件和内存文件之间的关系就像程序和进程的关系一样当程序运行起来后便成了进程而当磁盘文件加载到内存后便成了内存文件。 进程想要访问文件必须先打开文件一个进程可以打开多个文件而系统当中又存在大量进程也就是说在系统中任何时刻都可能存在大量已经打开的文件已经打开的文件会被加载到了内存中这些文件也叫内存文件反之没有打开的文件就叫做磁盘文件。那么操作系统就要管理这些打开的文件。 如何管理就是先描述再组织。操作系统为每个已经打开的文件创建各自的struct file结构体然后将这些结构体以双链表的形式连接起来那么操作系统对文件的管理也就变成了对这张双链表的增删改查等操作在每个节点中不仅有链表的指针还应该存在着文件的内容属性这些信息大部分在磁盘中就保留在文件内部了加载的时候就从磁盘中把数据加载到内存。 而为了区分已经打开的文件哪些属于特定的某一个进程我们就还需要建立进程和文件之间的对应关系。 进程和文件之间的对应关系是如何建立的 当进程运行的时候操作系统会将该程序的代码和数据加载到内存然后创建对应的task_struct, mm_struct, 页表等… task_struct 里面有一个指针指向files_struct结构体结构体里面有名为fd_array的指针数组该数组的下标就是文件描述符fd。 使用read和write的时候要传入文件描述符通过文件描述符找到这个数组中的指针进而对文件访问。 当进程打开log.txt文件时我们需要先将该文件从磁盘当中加载到内存形成对应的struct file将该struct file连入文件双链表并将该结构体的首地址填入到fd_array数组当中下标为3的位置使得fd_array数组中下标为3的指针指向该struct file最后返回该文件的文件描述符给调用进程即可。 因此我们只要有某一文件的文件描述符就可以找到与该文件相关的文件信息进而对文件进行一系列输入输出操作。 注意 向文件写入数据时是先将数据写入到对应文件的缓冲区当中然后定期将缓冲区数据刷新到磁盘当中。 1.1.文件描述符的分配规则
我们之前连续打开了6个文件我们发现文件描述符是从3开始的并且是连续地址的。那真的是一直从3开始吗下面我们看一段代码 #includestdio.h #includestring.h #includeunistd.h#includesys/types.h#includesys/stat.h#includefcntl.hint main(){ close(0);int fd1open(./log1.txt,O_WRONLY|O_CREAT,0644);int fd2open(./log2.txt,O_WRONLY|O_CREAT,0644);int fd3open(./log3.txt,O_WRONLY|O_CREAT,0644);int fd4open(./log4.txt,O_WRONLY|O_CREAT,0644);printf(%d\n,fd1);printf(%d\n,fd2);printf(%d\n,fd3);printf(%d\n,fd4);close(fd1);close(fd2);close(fd3);close(fd4);return 0;} 我们发现怎么fd从0开始了而之后的又是从3开始了。现在我们在将2也关了我们再来看结果会是如何。 #includestdio.h #includestring.h #includeunistd.h#includesys/types.h#includesys/stat.h#includefcntl.hint main(){ close(0);close(2);int fd1open(./log1.txt,O_WRONLY|O_CREAT,0644);int fd2open(./log2.txt,O_WRONLY|O_CREAT,0644);int fd3open(./log3.txt,O_WRONLY|O_CREAT,0644);int fd4open(./log4.txt,O_WRONLY|O_CREAT,0644);printf(%d\n,fd1);printf(%d\n,fd2);printf(%d\n,fd3);printf(%d\n,fd4);close(fd1);close(fd2);close(fd3);close(fd4);return 0;} 我们发现0和2也被用起来了。现在我们就明白了文件描述符的分配规则是从最小的未被使用的下标开始的 事实上 Linux下进程默认会打开三个文件描述符0标准输入、1标准输出、2标准错误。 012对应的物理设备一般是键盘、显示器、显示器。 我们之前验证了文件描述符默认是从3开始的也就是说012是默认被打开的。 0代表的是标准输入流对应硬件设备为键盘1代表标准输出流对应硬件设备是显示器2代表标准错误流对应硬件设备为显示器。 当一个进程被创建时OS就会根据键盘、显示器、显示器形成各自的struct file将这3个struct file链接到文件的双链表当中并将这3个struct file的地址分别填入fd_array数组下标为0、1、2的位置至此就默认打开了标准输入流、标准输出流和标准错误流。 文件描述符的分配规则分配最小的没有被占用的。如果我把0号关闭那么为新文件分配的时候就从最小的0分配。 2.重定向
2.1.输出重定向
1.输入重定项。
我们之前学习过的输出重定向就是将我们本应该输出到显示器上的数据重定向输出到另一个文件中。那他的原理是什么了 例如 如果我们想让本应该输出到“显示器文件”的数据输出到log.txt文件当中那么我们可以在打开log.txt文件之前将文件描述符为1的文件关闭也就是将“显示器文件”关闭这样一来当我们后续打开log.txt文件时所分配到的文件描述符就是1。
#include stdio.h
#include unistd.h
#include string.h
#include stdlib.h#include sys/types.h
#include sys/stat.h
#include fcntl.hint main()
{close(1);// 打开文件int fd open(log.txt, O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd 0){perror(open);exit(1);}// 打开成功printf(fd: %d\n, fd);printf(fd: %d\n, fd);printf(fd: %d\n, fd);printf(fd: %d\n, fd);fprintf(stdout, hello fprintf\n);const char* s hello fwrite\n;fwrite(s, strlen(s), 1, stdout);fflush(stdout);// 关闭文件close(fd); return 0;
} 通过上面的现象也可以看出打印的数据没有到显示器上而是到了磁盘的文件中这是为什么呢 上面就说过0、1、2默认是被打开的对应的就要打开显示器所以stdout的文件描述符就是1所以C语言的接口fprintf认识的就是stdout或者说就是1我们一开始就关闭了1号文件描述符把数组下标为1的位置设置为NULL然后打开了log.txt文件此时1没有被占用所以就把下标为1的位置填入log.txt的结构体的地址log.txt的文件描述符就是1了但是上层的C语言函数认识的还是1他们还是继续往1中写入这样就不能打印到屏幕而是重定向到了文件中。 重定向的本质是在操作系统中更改fd对应的内容上面演示的这就就叫做输出重定向。 2.2.输入重定向
输入重定向就是将我们本应该从一个键盘上读取数据现在重定向为从另一个文件读取数据。 比如说我们的fget函数是从标准输入读取数据现在我们让它从log1.txt当中读取数据我们在scanf读取数据之前close(0).这样键盘文件就被关闭这样一样log1.txt的文件描述符就是0.
int main()
{close(0);// 打开文件int fd open(log.txt, O_RDONLY);if (fd 0){perror(open);exit(1);}printf(fd: %d\n, fd);char buffer[64];fgets(buffer, sizeof(buffer), stdin);printf(%s\n, buffer);// 关闭文件close(fd);return 0;
} 关闭了0号文件描述符所以打卡的新文件的文件描述符就变成了0然后读取了文件中的第一行数据。
2.3.追加重定向
还有一种就是追加重定向更改一下选项就行了。
int main()
{close(1);// 打开文件int fd open(log.txt, O_WRONLY | O_APPEND | O_CREAT);if (fd 0){perror(open);exit(1);}printf(%d\n, fd);fprintf(stdout, append success\n);fflush(stdout);// 关闭文件close(fd);return 0;
} 【注意】“”输出重定向修改的只是1号也就是stdout标准输出所以尽管程序中有两行代码一行向1号文件描述符中打印另一行向2号文件描述符中打印那么使用输出重定向只会使1号文件描述符重定向2号还是打印到显示器上。 2.4.dup2
我们发现我们上面只能通过close关闭对应的文件描述符实习对应的输出重定向和输出重定向那我们能不能不关闭呢
要完成重定向我们只需对fd_array数组当中元素进行拷贝即可。 例如我们若是将fd_array[3]当中的内容拷贝到fd_array[1]当中因为C语言当中的stdout就是向文件描述符为1文件输出数据那么此时我们就将输出重定向到了文件log.txt。而在linux当中就给我们提供了这个系统调用 函数功能 dup2会将fd_array[oldfd]的内容拷贝到fd_array[newfd]当中。函数返回值调用成功返回0失败返回-1 使用的过程中需要注意 如果oldfd不是有效的文件描述符则dup2调用失败并且此时文件描述符为newfd的文件没有被关闭。如果oldfd是一个有效的文件描述符但是newfd和oldfd具有相同的值则dup2不做任何操作并返回newfd。 只需要把想要重定向的文件在数组中拷贝过去比如我想要输出重定向重定向到某个文件那么1就代表标准输出所以就要改变1的指向就把3的地址拷贝过去这样1就指向了重定向的文件。 输入重定向也是一样的0是标准输入就要从其他文件输入就把其他文件的地址拷贝到0的位置。
下面通过dup2演示一下前面的输出重定向 1 #includestdio.h2 #includesys/types.h3 #includesys/stat.h4 #includeunistd.h5 #includefcntl.h 6 int main()7 {8 int fdopen(./log.txt,O_WRONLY|O_CREAT,0644);9 dup2(fd,1);10 printf(hello world\n);11 printf(hello world\n);12 13 }