linux中的文件描述符与套接字socket

2022/01/03 1042点热度 0人点赞 0条评论

基本概念

文件描述符fd

Linux 系统中,把一切都看做是文件,当进程打开现有文件或创建新文件时,内核向进程返回一个文件描述符,文件描述符就是内核为了高效管理已被打开的文件所创建的索引,用来指向被打开的文件,所有执行I/O操作的系统调用都会通过文件描述符。

常见文件类型

  • 普通文件:包含任意数据

  • 目录:包含一组链接(link)的文件

  • 套接字(socket):用来与另一个进程进行跨网络通信的文件

  • 命名通道

  • 符号链接

  • 支付和块设备

linux抽象了一组标准接口,叫unix I/O,使得所有的输入和输出都能以一种统一的方式来执行;

  • 打开文件,应用程序通过内核创建,以宣告想要访问的I/O设备;返回一个小的非负整数,内核记录有关这个打开文件的所有信息,应用程序只需记录这个描述符。

    • 每个进程都有三个打开的文件

    • 标注输入(描述符为0)

    • 标准输出 (描述符为1)

    • 标准错误(描述符为2)

    • 这也是为什么nohup command 2>&1 &

  • 每个打开的文件内核保持一个文件位置k,初始为0

  • 读写文件:从文件复制n个字节到内存,从当前的文件位置k开始增加n,当k>=文件的大小时,会触发一个end-of-file(EOF)的条件;

  • 关闭文件:内核释放文件打开时创建的数据结构,并恢复到可用的描述符池中;

    • 从这里可以看出文件描述符是从小到大创建;

    • 文件描述符是可以重复利用的

套接字socket

套接字允许用于链接到网络,套接字与邮筒和墙壁上的电话插座是类似的,看下示意图(现代操作系统里的)

  • 套接字可以被动态的创建和销毁

  • 创建一个套接字成功后会返回一个文件描述符

  • 创建连接、读数据、写数据、解除连接需要用到文件描述符

  • 套接字必须有一个地址与它绑定

  • 源计算机和目标计算机都建立成功后,则两个计算机之间可以建立一个链接;

  • 网路是水管,套接字就是水管的口子,连接就是那两个口子相通;

基于套接字接口的额网络应用

  • connect 客户端通过此函数和服务器建立连接;

  • bind 服务端绑定一个ip和端口

  • listen 用于监听客户端的请求(将套接字转成监听套接字)

  • accept 等待客户端的连接请求

在redis的源码中,

# 通过acceptTcpHandleranet.c文件里的anetGenericAccept获取socket监听的fd
fd = accept(s,sa,len);

然后通过createClient将新创建的fd绑定到了client

并在这个fd上绑定了一个回调函数readQueryFromClient用于处理AE_READABLE事件

epollkqueue通过aeApiAddEvent 将这个fd注册到事件处理器的fd上,并且会标注事件类型

readQueryFromClient中会通过
read(fd, c->querybuf+qblen, readlen);
从网络缓冲区读取数据

writeToClient中通过
nwritten = write(fd,c->buf+c->sentlen,c->bufpos-c->sentlen);
写入网络缓冲区

文件的三张表

内核用三个相关的数据结构来表示打开的文件:

  • 描述符表(descriptor table)

    • 每个进程独立

    • 用进程打开的文件描述符来做索引

  • 文件表(file table)

    • 所有进程共享这张表

    • 打开文件在在内存中的表示

    • 通过open调用时创建,支持read、write、sendfile、lock等系统调用

    • 包括当前文件位置、引用计数(打开引用计数会+1)、指向i-node的指针

  • inode表(index-node table)

    • 所有进程共享这张表

    • 表示某个确切的文件(目录和块也是文件)

系统调用 描述
fd=creat(name,mode) 创建文件的一种方法
fd=open(file,how,...) 打开文件读、写或者读写
s=close(fd) 关闭一个已打开的文件
n=read(fd,buffer,nbytes) 从文件读取一些数据到缓冲区
n=write(fd,buffer,nbytes) 把数据从缓冲区写入到文件
position=lseek(fd,offset,whence) 移动文件指针
s=stat(name,&buf) 获取一个文件的状态信息
s=fstat(fd,&buf) 获取一个文件的状态信息
s=pipe(&fd[0]) 创建一个管道
s=fcntl(fd,cmd,...) 文件加锁及其他操作

当然这里还会做很多的缓存优化,这里不做详解。

参考:《现代操作系统》10.6 linux文件系统 《深入理解计算机系统》 第10章 系统级I/O

yxkong

这个人很懒,什么都没留下