心渐渐失空

导航

tgt源码阅读

读懂一个开源项目源码之前,需要先了解该项目的背景知识。背景知识熟悉了,代码只是具体实现手段而已。

源码地址:https://github.com/fujita/tgt

对于tgt来说,背景知识是块设备、scsi、iscsi协议。

众所周知,一条协议一般指的是一个包头,然后把要收发的数据放在包头后面。scsi协议和iscsi协议也如此。

对于tcp/ip协议、http协议,包头能在书中或网上找到,每个字段的含义都非常明确。

 

 

1.iscsi协议头一般是跟在tcp包头后面,而iscsi协议的包体就是scsi协议。

2.iscsi协议头和scsi协议头和tcp头部一样,都是二进制格式。但是scsi协议并不是对称的。就是request和response的内容不一样。

3.scsi request报文通常被叫做CDB(主要包含一个opcode表示是读操作还是写操作或者其他)。response叫做status code,如果status code == 0,表示request成功。如果status code == 2,表示后面还要回复一个叫做sense data的response给客户端,指示客户端下一步做什么。

 scsi协议只需要看opcode,头部内其他字段都是根据不同的opcode,而代表不同的含义,例如scsi opcode是读操作时:

 这只是scsi请求,而scsi响应则是:

 若status为2,则还会发一个sense response:

 

4.iscsi报文比较简单,一个报文叫做一个PDU,一个PDU由iscsi头部和包体组成,头部包含一个字段opcode字段,外加一些可选的key-value,包体就是scsi协议。正真读写的数据则是在scsi协议包体内。

注意,scsi协议头也有opcode,iscsi协议头也有opcode,但是他们不是同一个字段。

scsi的op code,表示本次请求是读/写/获取设备信息等等

iscsi的op code,表示的是本次操作是登录认证/协商会话信息/传输scsi指令等等

 

总之,关于scsi和iscsi具体协议,版本比较多,opcode也很多,不需要了解所有opcode,因为大部分都用不到。只需要了解几个主要的opcode就行了。

 

 了解完了协议,再来看一下iscsi的架构:

这是一个网络块设备协议,其中有一些架构概念需要了解:

1.scsi和iscsi都是C/S架构

2.scsi设备是插在电脑IO总线上的,内核会给scsi设备分配一个总线ID。

3.这个总线ID下面最多能插16个设备,其中第一个是控制器(虚拟的),所以真实可用的是15个,每一个都会分配一个target ID。

4.每个target就是一个硬盘,一个target又可以做分区,最多分为32个分区,其中第一个是控制器(虚拟的),所以真实可以用的是31个分区,分区被叫做lun(逻辑单元)。

5.客户端叫做initiator,也会有一个initiator ID,用来区分C/S架构里的唯一Client。

这些是scsi协议的背景知识,当然,有scsi协议的时候还没有iscsi协议。所以scsi协议是本机内核与硬盘通信的协议。所以scsi协议是没有认证登录相关协议的,

把scsi协议套上一个iscsi头和tcp/ip头后,这套协议就可以在网络中运行。也不再是内核和硬盘的C/S架构,而是一台客户机和另一台服务器的C/S架构。target、lun这些概念仍然保留,但不再是表示硬盘和分区了。target仅仅代表一个网络地址,lun可以代表一个块设备,这个块设备的后端存储可以是硬盘,也可以是ceph rbd 等。而客户端作为initiator,一般就是请求挂载一个lun。

另外,由于是网络协议,所以iscsi有登录认证相关的协议。

tgtd作为iscsi协议的服务端,首先,它的主线程采用了epoll_wait,来监听3260端口,并把所有连接过来的tcp链接都放到这个epoll内收发数据。tgtd启动后,main函数会堵塞在event_loop函数内。

在进入event_loop之前,各种类型的后端存储都会注册到后端存储表里。当用户使用tgtadm来创建一个lun时,就会根据用户指定的bstype参数来选择该类型的后端存储:aio,rdwr,rbd等,例如rbd引擎的注册:

有一个叫做global_target的全局变量,

 当用户使用tgtadm创建target时,就会创建一个struct target对象,并把它插入到下面链表中:

 

 当用户创建lun时,也会在target对象中的device_list中插入一个struct scsi_lu对象。

 

这个树状结构的数据结构(target list),就是tgtd要维护在内存里的数据主体,每个target是一个网络地址,它包含很多东西:

多个lun,每个lun对应一个后端块设备,包含对应的后端设备处理函数集,scsi指令最终会调用这个指令来完成

多个session(每个session是一个initiator)

每个session可以有多个connect(tcp link)

 

在connect和session里面,还包含了本次通信的请求和回复报文,收发数据缓冲区,scsi请求队列,还有当前会话的登录状态、scsi指令执行的状态等等。

总之,顺着全局target list往下看,能看到服务端的整个全景图。

 

posted on 2023-09-20 16:13  心渐渐失空  阅读(70)  评论(0编辑  收藏  举报