瓦尼什如何与切里6/N相遇¶
Varnish套接字地址¶
套接字地址有点乱,特别是因为在引入IPv6时,没有人敢更改所有的IPv4遗留代码。
在Varnish中,我们将所有的丑陋封装在一个 struct suckaddr
,之所以这样命名,是因为我们不得不在这上面花费时间和代码,这太糟糕了。
在这样的情况下,将内部设置为严格只读是有意义的,以确保没有人会有偷偷摸摸的想法:
struct suckaddr *
VSA_Build(void *d, const void *s, unsigned sal)
{
struct suckaddr *sua;
[… lots of ugly stuff …]
return (RO(sua));
}
那么使用C语言似乎是合乎逻辑的 const
来表示这一事实,但由于当前的VSA API是这样定义的,因此用户调用 free(3)
当他们完成后,这是不起作用的,因为原型 free(3)
是:
void free(void *ptr);
因此您不能使用 const
指针。
向ISO-C标准委员会致敬!
这让我想到了Cheri的一个软点:分配器。
如何与切丽一起免费购物¶
封装类的一个非常常见的设计模式如下所示:
struct real_foo {
struct foo foo;
[some metadata about foo]
};
const struct foo *
Make_Foo([arguments])
{
struct real_foo *rf;
rf = calloc(1, sizeof *rf);
if (rf == NULL)
return (rf);
[fill in rf]
return (&rf->foo);
}
void
Free_Foo(const struct foo **ptr)
{
const struct foo *fp;
struct real_foo *rfp;
assert(ptr != NULL);
fp = *ptr;
assert(fp != NULL);
*ptr = NULL;
rfp = (struct real_foo*)((uintptr_t)fp);
[clean stuff up]
}
我们通过了一个 **ptr
至 Free_Foo()
,另一种Varnish风格模式,因此我们可以清空调用函数中的保持变量,以避免指向现在被销毁的对象的悬挂指针在以后造成任何类型的麻烦。
在调用函数中,如下所示:
const struct foo *foo_ptr;
[…]
Free_Foo(&foo_ptr);
如果我们利用Cheri让Foo成为真正的 const
对于API的用户,我们不能如上所述,清洗 const
带着一次旅行离开 uintptr_t
然后写入元数据。
Cheri C/C++手册是一本简明的学术大部头,它简洁地提到:
»Therefore, some additional work may be required to derive a pointer to the allocation’s metadata via another global capability, rather than the one that has been passed to free().«
尽管轻描淡写,但我非常赞成这一点,因为这是 precisely 为什么是我自己的 phkmalloc 在20年前大受欢迎:通过坚定地将元数据与分配的空间分开,使用 malloc(3)
API可以检测到,也确实检测到了。
但这件事 is 对于Cheri的用户来说,这将是一个麻烦,因为Cheri从一个指针指向不同的指针是一件实际的工作。
唯一“合适”的解决方案是构建某种数据结构:列表、树、散列、DB2数据库,选择您喜欢的任何毒药,并使用无能指针作为键搜索出元数据指针。考虑到Cheri指针很大,在对象中嵌入一个数字索引并将其用作键可能是一个更好的想法。
这种额外工作的一个重要好处是,如果您的自由函数被传递了一个指向其他对象的指针,您将会发现,因为它不在您的数据结构中。
如果Cheri提供了一个“找到我的另一个指针”的高质量实现,那将是一个好主意,这样就没有人被迫为此打开计算机编程的艺术之门。
当API像VSA一样是“即火即忘”时,从没有元数据需要清理的意义上讲,我们可以将艰苦的工作留给 malloc(3)
实施。
从此以后 phkmalloc
没有相关的实现 malloc(3)
已取消引用已释放的指针,以便找到分配的元数据。尽管它的非常量C原型 free(3)
,因此将很高兴地处理 const
甚至是CHERILED只读指针。
但是你 will 必须擦洗 const
带着一个 uintptr_t
要满足C编译器的要求,请执行以下操作:
void
VSA_free(const struct suckaddr **vsap)
{
const struct suckaddr *vsa;
AN(vsap);
vsa = *vsap;
*vsap = NULL;
free((char *)(uintptr_t)vsa);
}
或Varnish风格的:
void
VSA_free(const struct suckaddr **vsap)
{
const struct suckaddr *vsa;
TAKE_OBJ_NOTNULL(vsa, vsap, SUCKADDR_MAGIC);
free(TRUST_ME(vsa));
}
现在我已经看完了这段代码,我决定将 struct suckaddr
在Varnish中,即使没有切丽,这样也更健康。
这不是错误,但Cheri让我更简单、更快速地探索这一更改的后果,所以在这一点上,我将放弃“半个错误”的分数给Cheri。
/phk