在Linux编程时,无论是在操作文件还是网络操作时都能够通过文件描述符来read或者write。之前在没有接触C++的时候,不懂面向对象的那套东西。自从学习了C++以及面向对象的一些知识以后突然对Linux这套文件描述符有了稍微深刻的认识。

怎么说呢,Linux这一套文件机制就相当于面向对象里面的多态,拿到一个文件描述符都可以进行read或者write。但是具体的read和write却跟对应文件描述符的具体实现不同。比如socket的就是走网络,普通文件的就是走磁盘IO。

下面一张UML类图大概表现出了Linux文件描述符的大概意思:

当然,为了将不同的类型的I/O与对应的文件描述符绑定,则是需要不同的初始化函数的。在C++中有构造函数跟编译器帮助搞定,在C函数里只能自己动手丰衣足食了。
普通文件就通过open函数,指定对应的文件路径,操作系统通过路径能够找到对应的文件系统类型,如ext4啊,fat啊等等。
如果是网络呢,就通过socket函数来初始化,socket函数就通过(domain, type, protocol)来找到对应的网络协议栈,比如TCP/IP,UNIX等等。

整个Linux 文件系统的结构差不多就这个意思,socket跟他绑定也是为了统一接口。
所以网络相关的调用,如connect, bind等等,第一步基本上就是通过文件描述符找到对应的内核socket结构,然后在进行对应的操作。
  1. SYSCALL_DEFINE2(listen, int, fd, int, backlog)
  2. {
  3.     struct socket *sock;
  4.     int err, fput_needed;
  5.     int somaxconn;

  6.     /* 通过文件描述符获得 kernel socket结构, 并且增加此结构的引用计数 */
  7.     sock = sockfd_lookup_light(fd, &err, &fput_needed);
  8.     if (sock) {
  9.         /* 进行检测,看看是否满足系统设计的需求,功能上不重要 */
  10.         somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;
  11.         if ((unsigned)backlog > somaxconn)
  12.             backlog = somaxconn;
  13.         /* 检测此调用是否安全 */
  14.         err = security_socket_listen(sock, backlog);
  15.         /* 执行具体的listen操作,TCP啊,或者是其他网络协议等等,这个ops是在socket时候绑定的 */
  16.         if (!err)
  17.             err = sock->ops->listen(sock, backlog);
  18.         /* 减少kernel socket的引用计数 */
  19.         fput_light(sock->file, fput_needed);
  20.     }
  21.     return err;
  22. }
上面就是一个典型的调用listen的内核操作。

在socket层内核完成的就是一个interface功能,或许也可以叫做桥接模式(bridge pattern)。

这就是我对socket及Linux文件描述符的理解。
Logo

更多推荐