> For the complete documentation index, see [llms.txt](https://book.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://book.bsdcn.org/man/man9/osd.9.md).

# osd.9

`osd` — 对象特定数据

## 名称

`osd`, `osd_register`, `osd_deregister`, `osd_set`, `osd_reserve`, `osd_set_reserved`, `osd_free_reserved`, `osd_get`, `osd_del`, `osd_call`, `osd_exit`

## 概要

```c
#include <sys/osd.h>
```

```c
typedef void *(osd_destructor_t)(void *value);

typedef int *(osd_method_t)(void *obj, void *data);

int
osd_register(u_int type, osd_destructor_t destructor,
    const osd_method_t *methods)

void
osd_deregister(u_int type, u_int slot)

int
osd_set(u_int type, struct osd *osd, u_int slot, void *value)

void **
osd_reserve(u_int slot)

int
osd_set_reserved(u_int type, struct osd *osd, u_int slot,
    void **rsv, void *value)

void
osd_free_reserved(void **rsv)

void *
osd_get(u_int type, struct osd *osd, u_int slot)

void
osd_del(u_int type, struct osd *osd, u_int slot)

int
osd_call(u_int type, u_int method, void *obj, void *data)

void
osd_exit(u_int type, struct osd *osd)
```

## 描述

`osd` 框架提供了一种机制，可以在运行时将任意数据动态关联到任何已适配为可使用 `osd` 的内核数据结构。所需的一次性修改涉及在内核数据结构中嵌入一个 `struct osd`。

一个额外的好处是，在对结构进行初始更改之后，所有后续对该结构使用 `osd` 都不涉及对结构布局的更改。推而广之，如果该数据结构是 ABI 的一部分，`osd` 提供了一种以保持 ABI 的方式扩展结构的方法。

嵌入的 `struct osd` 的细节与 `osd` 框架的使用者无关，不应直接操作。

与结构关联的数据由 `osd` 框架使用类型/槽标识符对来引用。类型在

```c
#include <sys/osd.h>
```

中静态定义，并为要注册的槽提供高级分组。槽标识符在使用 `osd_register` 注册数据类型时由框架动态分配，并在相应的 `osd_deregister` 调用之前保持有效。

### 函数

`osd_register` 函数向 `osd` 框架注册一个类型/槽标识符对，用于新的数据类型。该函数可能会休眠，因此不能从不可休眠的上下文中调用。`type` 参数指定从

```c
#include <sys/osd.h>
```

中定义的高级类型分组中分配槽标识符。`destructor` 参数指定一个可选的 `osd_destructor_t` 函数指针，该函数将在被注册类型的对象随后被 `osd_del` 函数销毁时调用。如果不需要析构函数，可以传递 NULL。`methods` 参数指定一个可选的 `osd_method_t` 函数指针数组，可随后由 `osd_call` 函数调用。如果不需要方法，可以传递 NULL。`methods` 参数目前仅在 OSD\_JAIL 类型标识符下有用。

`osd_deregister` 函数注销先前注册的类型/槽标识符对。该函数可能会休眠，因此不能从不可休眠的上下文中调用。`type` 参数指定从

```c
#include <sys/osd.h>
```

中定义的高级类型分组中分配槽标识符。`slot` 参数指定要注销的槽标识符，应为注册数据类型时 `osd_register` 返回的值。

`osd_set` 函数将数据对象指针与内核数据结构的 `struct osd` 成员关联。`type` 参数指定从

```c
#include <sys/osd.h>
```

中定义的高级类型分组中分配槽标识符。`osd` 参数是指向内核数据结构 `struct osd` 的指针，`value` 指针将与之关联。`slot` 参数指定要将 `value` 指针分配到的槽标识符。`value` 参数指向要与 `osd` 关联的数据对象。

`osd_set_reserved` 函数与 `osd_set` 功能相同，但多了一个 `rsv` 参数，这是先前通过 `osd_reserve` 分配的内部使用内存。

`osd_get` 函数从指定的类型/槽标识符对返回与内核数据结构 `struct osd` 成员关联的数据指针。`type` 参数指定从

```c
#include <sys/osd.h>
```

中定义的高级类型分组中分配槽标识符。`osd` 参数是指向内核数据结构 `struct osd` 的指针，从中检索数据指针。`slot` 参数指定从中检索数据指针的槽标识符。

`osd_del` 函数从指定的类型/槽标识符对中移除与内核数据结构 `struct osd` 成员关联的数据指针。`type` 参数指定从

```c
#include <sys/osd.h>
```

中定义的高级类型分组中分配槽标识符。`osd` 参数是指向内核数据结构 `struct osd` 的指针，从中移除数据指针。`slot` 参数指定从中移除数据指针的槽标识符。如果在注册时指定了 `osd_destructor_t` 函数指针，则将调用析构函数并传递正在删除的类型/槽标识符对的数据指针。

`osd_call` 函数为指定 `obj` 和 `data` 指针上给定类型的所有当前已注册槽调用指定的 `osd_method_t` 函数指针。该函数可能会休眠，因此不能从不可休眠的上下文中调用。`type` 参数指定从

```c
#include <sys/osd.h>
```

中定义的高级类型分组中调用方法。`method` 参数指定传递给 `osd_register` 的 `osd_method_t` 数组的索引。`obj` 和 `data` 参数传递给每个槽的方法函数指针。

`osd_exit` 函数从指定内核数据结构 `struct osd` 成员的所有当前已注册槽中移除给定类型的所有数据对象指针。`type` 参数指定从

```c
#include <sys/osd.h>
```

中定义的高级类型分组中移除数据指针。`osd` 参数是指向内核数据结构 `struct osd` 的指针，从中移除所有当前已注册槽的所有数据对象指针。

## 实现说明

`osd` 使用二维矩阵（数组的数组）作为数据结构来管理与内核数据结构 `struct osd` 成员关联的外部数据。类型标识符用作外层数组的索引，槽标识符用作内层数组的索引。要设置或检索给定类型/槽标识符对的数据指针，`osd_set` 和 `osd_get` 执行相当于 array\[type]\[slot] 的操作，这既是常数时间又很快。

如果首次对 `struct osd` 调用 `osd_set`，存储数据指针的数组将使用 [malloc(9)](/man/man9/malloc.9.md) 以 M\_NOWAIT 动态分配到适合正在设置的槽标识符的大小。如果随后调用 `osd_set` 尝试设置数值上大于先前 `osd_set` 调用中使用的槽的槽标识符，则使用 realloc(9) 将数组增长到适当的大小以便可以使用该槽标识符。为最大化任何按顺序在多个不同槽标识符上调用 `osd_set` 的代码（例如在初始化阶段）的效率，应按从高到低的降序遍历槽标识符。这将导致仅一次 [malloc(9)](/man/man9/malloc.9.md) 调用来创建最大槽大小的数组，所有后续对 `osd_set` 的调用将在没有任何 realloc(9) 调用的情况下进行。

`osd_set` 有可能无法分配此数组。为确保此类分配成功，可以（在非阻塞上下文中）调用 `osd_reserve`，它将通过 [malloc(9)](/man/man9/malloc.9.md) 以 M\_WAITOK 预分配内存。然后此预分配的内存传递给 `osd_set_reserved`，后者在必要时使用它，否则丢弃它。也可以通过调用 `osd_free_reserved` 显式丢弃内存。由于此方法总是分配内存（无论最终是否需要），应仅很少使用，例如在 `osd_set` 失败的不太可能发生的情况下。

`osd` API 面向存储指向给定 `osd` 类型标识符的相同底层数据结构类型指针的槽标识符。这不是必需的，例如 [khelp(9)](/man/man9/khelp.9.md) 在 OSD\_KHELP 类型标识符下的槽中存储完全不同的数据类型。

### 锁

`osd` 内部使用 [mutex(9)](/man/man9/mutex.9.md)、[rmlock(9)](/man/man9/rmlock.9.md) 和 [sx(9)](/man/man9/sx.9.md) 锁的混合来保护其内部数据结构和状态。

同步对内核数据结构 `struct osd` 成员访问的责任留给使用该数据结构并调用 `osd` API 的子系统。

`osd_get` 仅在读取模式下获取 [rmlock(9)](/man/man9/rmlock.9.md)，因此在内核内大多数上下文（包括大多数快速路径）中使用都是安全的。

## 返回值

`osd_register` 返回新注册数据类型的槽标识符。

`osd_set` 和 `osd_set_reserved` 成功时返回零，如果指定的类型/槽标识符对触发了失败的内部 realloc(9)，则返回 ENOMEM（当 `rsv` 为非 NULL 时，`osd_set_reserved` 将始终成功）。

`osd_get` 返回指定类型/槽标识符对的数据指针，如果槽尚未初始化则返回 NULL。

`osd_reserve` 返回适合传递给 `osd_set_reserved` 或 `osd_free_reserved` 的指针。

`osd_call` 如果没有方法运行或每个槽的方法都成功运行则返回零。如果某个槽的方法返回非零值，`osd_call` 提前终止并将方法的错误返回给调用者。

## 参见

[khelp(9)](/man/man9/khelp.9.md)

## 历史

对象特定数据（OSD）设施首次出现在 FreeBSD 8.0 中。

## 作者

`osd` 设施由 Pawel Jakub Dawidek <pjd@FreeBSD.org> 编写。

本手册页由 Lawrence Stewart <lstewart@FreeBSD.org> 编写。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://book.bsdcn.org/man/man9/osd.9.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
