句柄到底是什么?TCB又是什么?C代码实例讲解
  rWLfpslpeeJP 2023年12月08日 24 0


前言

(1)讲良心话,如果是想真心了解句柄是什么的,就直接看代码。国内由太多关于句柄的一些专业术语了。反正互相抄,这些垃圾文,我也懒得喷,因为大家都在骂,我就不凑热闹了。
(2)不过,唯一让我感到诧异的是,这些垃圾文的收藏量居然惊人的高,以至于明明国内一大批讲解句柄是什么的。却在交流群中常常能够看到很多人问,句柄到底是什么。
(3)我也不想自夸,反正呢,我只能保证这篇博客和网上哪些动不动一堆术语无脑堆的不同。我直接上代码。
(4)事先叠甲,本文需要一定的C语言指针基础。零基础铁定看不懂

实例代码讲解

(1)国内网站已有大量这种堆术语的,我就不再赘述,直接上实际代码。
(2)以我最近移植的一款AHT20温湿度传感器为例子,AHT20I2C设备。当我每次和AHT20进行交互的时候,都需要告诉记得AHT20挂载在哪条I2C上,这无疑非常的麻烦。
(3)于是我们可以使用句柄。我们只有在初始化的时候,需要记得,AHT20挂载在哪条总线上,之后只需要操作aht20_create()函数返回的句柄即可
(4)我个人认为,学习编程术语最好的办法就是直接看代码,因为人的语言容易产生歧义。所以我也不想多废话了,直接上代码。建议不理解的人多看几遍如下代码。
<1>如下代码中的i2c_cmd_link_create()i2c_master_start()i2c_master_write_byte()i2c_master_cmd_begin()都是ESP32的库函数。其实我们不需要了解这四个库函数有什么用,只需要知道,这些函数里面,一旦使用到了一些特定的参数,例如sens->busAHT20挂载的I2C端口),或者sens->dev_addrAHT20的地址)的时候,我其他外部文件调用aht20_read_data()只需要传入一个句柄aht20_handle_t,就可以无脑操作了。
<2>但是,需要注意的一点是,aht20_create()创建句柄的时候,还是要记住AHT20的一些特定信息的,例如挂载的I2C端口和地址信息。

/*---------------------*/
/*------ aht20.h ------*/
/*---------------------*/
typedef void *aht20_handle_t;  //这个就是句柄,对外可见
/**
 * @brief   创建一个AHT20的句柄
 *
 * @param   port                挂载在哪个I2C上
 *         -ATH20_SLAVE_ADDRESS 传感器AHT20的地址
 *
 * @return  AHT20的句柄
 */
aht20_handle_t aht20_create(i2c_port_t port, uint8_t ATH20_SLAVE_ADDRESS);

/**
 * @brief   读取AHT20的温湿度数据
 *
 * @param   sensor      句柄
 *         -humidity    读取到的湿度值
 *         -temperature 读取到的温度值
 *
 * @return  无
 */
esp_err_t aht20_read_data(aht20_handle_t sensor, float* humidity, float* temperature);

/*---------------------*/
/*------ aht20.c ------*/
/*---------------------*/

typedef struct
{
	i2c_port_t bus;
	uint8_t dev_addr;
	// ...
}aht20_dev_t;  //这个就是TCB,只有aht20.c文件可以使用,对外不可见
	
aht20_handle_t aht20_create(i2c_port_t port, uint8_t ATH20_SLAVE_ADDRESS )
{
    aht20_dev_t *sens = (aht20_dev_t *) calloc(1, sizeof(aht20_dev_t));
	
	/*=== AHT20命令初始化 ===*/
    sens->bus = port;
    sens->dev_addr = ATH20_SLAVE_ADDRESS << 1;
	// ...
}
esp_err_t aht20_read_data(aht20_handle_t sensor, float* humidity, float* temperature)
{
    aht20_dev_t *sens = (aht20_dev_t *) sensor;
    esp_err_t  ret;
    // ...
    cmd = i2c_cmd_link_create();
    ret = i2c_master_start(cmd);
    ret = i2c_master_write_byte(cmd, sens->dev_addr | I2C_MASTER_READ, true);
    // ...    
    ret = i2c_master_cmd_begin(sens->bus, cmd, 10 / portTICK_PERIOD_MS);
	return ESP_OK;
}
/*---------------------*/
/*------ main.h ------*/
/*---------------------*/
void main()
{
	float humidity, temperature;
	aht20_handle_t aht20;
	//创建AHT20句柄
	aht20 = aht20_create(I2C_NUM_0);
	while(1)
	{
		//读取AHT20温湿度数据
        ret = aht20_read_data(aht20, &humidity, &temperature);
        //判断数据是否读取成功
        TEST_ASSERT_EQUAL(ESP_OK, ret);
        //打印温湿度数据
        ESP_LOGI(TAG,"humidity is %f , temperature is %f C",humidity,temperature);
	}
}

(5)看完上面的代码,可能就会有有同学会说了,这样做会不会需要多浪费一个4字节的空间存放void *类型的句柄(假设32位机器)?既然有了TCB了,我们不可以让TCB结构体全局可见吗(被extern修饰的结构体变量)?这样不就节省创建一个void *类型变量。这也很明显是开发经验缺乏的导致的疑惑,如果是大型项目,一定是要避免全局可见的变量的,因为很容易产生各种奇奇怪怪的问题。毕竟, 你要知道一个大型项目是几十人甚至上百人开发。开发周期有很长,你这种全局可见的变量,可能别人的变量名字冲突了最后他修改了你的值,产生bug调试很托开发周期,很容易出现一颗老鼠屎打烂一锅汤的情况。

结论

(1)所谓的句柄很好理解,说白了就是一个void *类型的指针。我们利用这个指针,通过强制类型转换,可很好的实现高内聚低耦合的特性。
(2)TCB就是句柄通过强制类型转换之后的结构类型指针,TCB和句柄指向的是同一个区域。他们两者的区别如下:
<1>TCB是不对外可见,只有当前文件可以使用。而句柄是对外可见的。
<2>句柄是一个void *类型的指针,不能直接操作。TCB是一个具象的结构体指针,可以直接操作。


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年12月08日 0

暂无评论

推荐阅读
rWLfpslpeeJP