一、文件描述符
文件描述符(File descriptor)是计算机科学中的一个术语,一个用于表述指向文件的引用的抽象化概念。
文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每个进程所维护的该进程打开文件目录表。当程序打开一个现有文件或者创建一个新文件时,内核就向进程返回一个文件描述符。
在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。
每一个Linux进程(除了可能的守护进程)应均有三个标准的POSIX(可移植操作系统接口)文件描述符,对应与三个标准的流。
随便创建一个文件,并用vim打开
touch iotest vim iotest #换一个窗口 ps -ef|grep vim ll /proc/${pid}/fd
可以看出文件描述符的实质是一个软链接,对软链接的操作都会作用于软链接指向的文件。一个进程的输入输出实际是对一个文件的操作。
标准输入输出操作的是操作系统在/dev/pts下创建的一个与进程对应的临时设备文件,进程关闭时删除(每个进程都会创建一个临时设备文件)
vim进程操作的是/home/wq/io/.iotest.swp文件,是一个vim进程创建的临时文件,杀死进程时保留,vim退出指令会替代原文件iotest
在Linux系列的操作系统上,由于Linux的设计思想便是把一切设备都视作为文件。因此,文件描述符为在此系列平台上进行设备相关的编程实际提供了一个统一的方法。
文件描述符的优点
- 基于文件描述符的I/O操作兼容POSIX标准
- 在UNIX、Linux的系统调用中,大量的系统调用都是依赖于文件描述符
文件描述符的缺点:
- 在非UNIX、Linux操作系统上(如windows),无法基于这一概念进行编程。
- 由于文件描述符在形式上不过是个魔数,当代码量增大时,会使编程者难以分清哪些整数表示数据,哪些表示文件描述符。代码可读性差。
操作系统中与文件描述符相关的操作
文件描述符的生成操作:
open()/open64/create()/create64()//创建本地文件的文件描述符 socket()//创建socket套接字的文件描述符 socketpair()//创建一对socket套接字的文件描述符 pipe()//创建管道的文件描述符
与单一文件描述符相关的操作
read(),wirte()//从文件描述符对应的文件读写 recv(),send()//从文件描述符套接字socket recvmsg,sendmsg()// sendfile()//两个文件描述符传递数据,在内核中进行,不需要用户态,内核态切换 lseek(),lseek64()// fstat(),fstat64()// fchmod()// fchown()//
与多个文件描述符相关的操作
select(),pselect()poll(),epoll()
与文件描述符表相关的操作
close()// dup()// dup2()// fcntl (F_DUPFD)// fcntl (F_GETFD and F_SETFD)//
改变进程状态的操作
fchdir()// mmap()//
与文件加锁的操作
flock()// fcntl (F_GETLK, F_SETLK and F_SETLKW)// lockf()//
与套接字socket相关的操作
connect()// bind()// listen()// accept()// getsockname()// getpeername()// getsockopt(), setsockopt()// shutdown()//
IO多路复用的操作:select、poll、epoll,
文件内存映射操作:mmap
零拷贝技术:sendfile
二、IO多路复用
综上:进程对文件或接口的IO操作,是通过操作文件描述符的方式实现的,创建一个指向文件或者接口的软链接。进程操作软链接来操作文件。
以kafka进程为例:
IO多路复用就是在内核中操作多个代表select、poll、epoll
man 2 select //打开select的说明页 //如果报错 No manual entry for select in section 2,说明man-pages不全 yum install -y man-pages
select与pselect允许一个程序监控多个文件描述符,调用select时当前程序阻塞,直到一个或者多个文件描述符 “准备好” 某类I/O操作时当前程序被唤醒。
“准备好”例如:单个文件操作符的I/O操作,
根据里面有一个C语言的例子:
#include <stdio.h> #include <stdlib.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> int main(void) { fd_set rfds; struct timeval tv; int retval; /* Watch stdin (fd 0) to see when it has input. */ FD_ZERO(&rfds); FD_SET(0, &rfds); /* Wait up to five seconds. */ tv.tv_sec = 5; tv.tv_usec = 0; retval = select(1, &rfds, NULL, NULL, &tv); /* Don't rely on the value of tv now! */ if (retval == -1) perror("select()"); else if (retval) printf("Data is available now. "); /* FD_ISSET(0, &rfds) will be true. */ else printf("No data within five seconds. "); exit(EXIT_SUCCESS); }
三、文件内存映射
四、零拷贝
1