"NewUserClient"应该如何实现
How should "NewUserClient" be implemented
我正在尝试与应用程序中的 dext 进行交互。我能够使用 IOServiceOpen
找到该服务,并且我收到对我的 dext 的 NewUserClient
的调用(我可以看到传递的 type
参数在日志中输出)。在此之后我有点迷路了。在此处阅读有关 NewUserClient I can see that one should use Create
创建新服务对象的信息。
讨论部分 here 说 propertiesKey
字典中的键描述了新服务。
这个字典应该作为顶级条目放在系统扩展的 plist 文件中,还是应该用 IOKitPersonalities
中的键放置字典?
我能否将 IOServiceDEXTEntitlements
键保留为空值,以免对连接到系统扩展的应用程序的权利施加任何限制?
我的 plist 看起来像这样(在两个地方有 MyUserClientProperties
键/字典)。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>MyUserClientProperties</key>
<dict>
<key>IOClass</key>
<string>MyUserClient</string>
<key>IOUserClass</key>
<string>MyUserUSBInterfaceDriver</string>
<key>IOServiceDEXTEntitlements</key>
<string></string>
</dict>
<key>IOKitPersonalities</key>
<dict>
<key>example_device</key>
<dict>
<key>MyUserClientProperties</key>
<dict>
<key>IOClass</key>
<string>MyUserClient</string>
<key>IOUserClass</key>
<string>MyUserUSBInterfaceDriver</string>
<key>IOServiceDEXTEntitlements</key>
<string></string>
</dict>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>IOClass</key>
<string>IOUserService</string>
<key>IOProviderClass</key>
<string>IOUSBHostInterface</string>
<key>IOUserClass</key>
<string>MyUserUSBInterfaceDriver</string>
<key>IOUserServerName</key>
<string>sc.example.MyUserUSBInterfaceDriver</string>
<key>bConfigurationValue</key>
<integer>0x1</integer>
<key>bInterfaceNumber</key>
<integer>0x0</integer>
<key>idVendor</key>
<integer>0x123</integer>
<key>idProduct</key>
<integer>0x08</integer>
</dict>
</dict>
<key>OSBundleUsageDescription</key>
<string>Example user space USB driver</string>
</dict>
</plist>
我需要将 SUPERDISPATCH
作为最后一个参数传递给 Create
吗?
来自 "OSX and iOS kernel programming" 第 5 章第 81 页:
I/O 套件设计的独创性在于用户客户端对象本身就是一个驱动程序对象:IOUserClient class 继承自 IOService 并且与任何其他 IOService 实例一样,每个用户客户端都有一个提供程序 class,对于用户客户端,它是应用程序控制的驱动程序的实例。
虽然以上可能只适用于 kext (?) 我假设 dext 的工作方式相同,
来自 Create 文档:使用 kIOUserClassKey 键指定您希望系统实例化的自定义 IOService subclass 的名称。
为什么还要实例化另一个IOService
class?这个class的目的是什么?是我的 class 的提供商继承自 IOUserClient
吗?如果是这样,我如何使我的驱动程序实例(实现 NewUserClient
的实例)成为提供者?
来自 Create 文档:使用 kIOClassKey
指定自定义名称 IOUserClient
subclass to return to您服务的客户。
class 的类型是否将被创建并分配给 Create
的第三个参数?如果是这样,那是我应该分配给 IOUserClient*
指针的那个,它被传递给 NewUserClient
吗?
kern_return_t IMPL(MyUserUSBInterfaceDriver, NewUserClient) {
os_log(OS_LOG_DEFAULT, "%{public}d:", type);
IOPropertyName propertiesKey = "MyUserClientProperties";
IOService* client;
auto ret = Create(this, propertiesKey, &client, SUPERDISPATCH);
// Need to do more things here...
return ret;
}
无论我尝试什么,我总是得到一个断言,但我看不出是什么导致了它。
3 com.apple.DriverKit 0x0000000102f2b24b __assert_rtn + 102
4 com.apple.DriverKit 0x0000000102f2c20a IOService::Create_Impl(IOService*, char const*, IOService**) (.cold.2) + 35
5 com.apple.DriverKit 0x0000000102f1766b IOService::Create_Impl(IOService*, char const*, IOService**) + 91
6 com.apple.DriverKit 0x0000000102f2668f IOService::Create_Invoke(IORPC, OSMetaClassBase*, int (*)(OSMetaClassBase*, IOService*, char const*, IOService**)) + 135
7 com.apple.DriverKit 0x0000000102f276d7 IOService::Create(IOService*, char const*, IOService**, int (*)(OSMetaClassBase*, IORPC)) + 267
8 sc.example.MyUserUSBInterfaceDriver 0x0000000102ee0c89 MyUserUSBInterfaceDriver::NewUserClient_Impl(unsigned int, IOUserClient**) + 313 (MyUserUSBInterfaceDriver.cpp:155)
尽管 DriverKit 上的 WWDC 演示试图假装不是这样,但 DriverKit 的世界观与内核的世界观非常不同,您需要了解一些实现细节,因为抽象非常漏洞。
您可能已经发现,DriverKit 驱动程序中看起来像 IOService
的对象实际上是内核(和用户 space 的)中的 IOUserService
对象I/O 注册表的视图。这个差距是通过 DriverKit 的 IPC 机制弥补的。
为了创建新的用户客户端,您需要一个(内核)IOUserClient
subclass 的实例,它由您的 (dext) IOUserClient
sub[=73] 支持=].内核 class 实际上是 IOUserUserClient
。 (是的,真的。)正如您所发现的,文档并没有完全清楚地说明您是如何做到这一点的。我发现查看源代码方面的可用内容很有帮助 - 调用 NewUserClient
is implemented in the IOUserServer::serviceNewUserClient()
function here.
的内核方面
您会立即注意到的一件事是,如果缺少 IOServiceDEXTEntitlements
属性,这不会阻止代码成功:
prop = userUC->copyProperty(gIOServiceDEXTEntitlementsKey);
ok = checkEntitlements(entitlements, prop, NULL, NULL);
并在 checkEntitlements
中:
if (!prop) {
return true;
}
这是个好消息,因为这意味着我们暂时不需要担心它,可以将其搁置。
接下来,事实证明 propertiesKey
引用了提供者 IOUserService
内核对象 上的 属性 。您无法从 dext 的代码中设置这些属性,因此提供它们的唯一方法是从 IOKit 匹配个性字典。
您可以随意命名 属性,但是:
- 它的值必须是一个字典。
- 它必须包含
"IOClass"
键值对,指定 kernel class 实例化为字符串 - 在您的例子中,"IOUserUserClient"
- 它必须包含
"IOUserClass"
键值对。这指定要实例化的 dext class,再次作为字符串。在你的情况下,这看起来像 MyUserClient
.
放在一起:
<key>IOKitPersonalities</key>
<dict>
<key>example_device</key>
<dict>
<key>MyUserClientProperties</key>
<dict>
<key>IOUserClass</key>
<string>MyUserClient</string>
<key>IOClass</key>
<string>IOUserUserClient</string>
</dict>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
…
</dict>
</dict>
…
然后,从您的 NewUserClient
函数调用:
IOService* client = nullptr;
kern_return_t ret = this->Create(this, "MyUserClient", &client);
我认为这里不需要 SUPERDISPATCH
,因为您大概不会在 class 中重写 Create
方法,所以您的超级实现无论如何都会被继承。
然后进行您可能需要的错误检查、任何其他初始化、准备等,最后:
*userClient = client;
return kIOReturnSuccess;
我正在尝试与应用程序中的 dext 进行交互。我能够使用 IOServiceOpen
找到该服务,并且我收到对我的 dext 的 NewUserClient
的调用(我可以看到传递的 type
参数在日志中输出)。在此之后我有点迷路了。在此处阅读有关 NewUserClient I can see that one should use Create
创建新服务对象的信息。
讨论部分 here 说 propertiesKey
字典中的键描述了新服务。
这个字典应该作为顶级条目放在系统扩展的 plist 文件中,还是应该用 IOKitPersonalities
中的键放置字典?
我能否将 IOServiceDEXTEntitlements
键保留为空值,以免对连接到系统扩展的应用程序的权利施加任何限制?
我的 plist 看起来像这样(在两个地方有 MyUserClientProperties
键/字典)。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>MyUserClientProperties</key>
<dict>
<key>IOClass</key>
<string>MyUserClient</string>
<key>IOUserClass</key>
<string>MyUserUSBInterfaceDriver</string>
<key>IOServiceDEXTEntitlements</key>
<string></string>
</dict>
<key>IOKitPersonalities</key>
<dict>
<key>example_device</key>
<dict>
<key>MyUserClientProperties</key>
<dict>
<key>IOClass</key>
<string>MyUserClient</string>
<key>IOUserClass</key>
<string>MyUserUSBInterfaceDriver</string>
<key>IOServiceDEXTEntitlements</key>
<string></string>
</dict>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>IOClass</key>
<string>IOUserService</string>
<key>IOProviderClass</key>
<string>IOUSBHostInterface</string>
<key>IOUserClass</key>
<string>MyUserUSBInterfaceDriver</string>
<key>IOUserServerName</key>
<string>sc.example.MyUserUSBInterfaceDriver</string>
<key>bConfigurationValue</key>
<integer>0x1</integer>
<key>bInterfaceNumber</key>
<integer>0x0</integer>
<key>idVendor</key>
<integer>0x123</integer>
<key>idProduct</key>
<integer>0x08</integer>
</dict>
</dict>
<key>OSBundleUsageDescription</key>
<string>Example user space USB driver</string>
</dict>
</plist>
我需要将 SUPERDISPATCH
作为最后一个参数传递给 Create
吗?
来自 "OSX and iOS kernel programming" 第 5 章第 81 页:
I/O 套件设计的独创性在于用户客户端对象本身就是一个驱动程序对象:IOUserClient class 继承自 IOService 并且与任何其他 IOService 实例一样,每个用户客户端都有一个提供程序 class,对于用户客户端,它是应用程序控制的驱动程序的实例。
虽然以上可能只适用于 kext (?) 我假设 dext 的工作方式相同,
来自 Create 文档:使用 kIOUserClassKey 键指定您希望系统实例化的自定义 IOService subclass 的名称。
为什么还要实例化另一个IOService
class?这个class的目的是什么?是我的 class 的提供商继承自 IOUserClient
吗?如果是这样,我如何使我的驱动程序实例(实现 NewUserClient
的实例)成为提供者?
来自 Create 文档:使用 kIOClassKey
指定自定义名称 IOUserClient
subclass to return to您服务的客户。
class 的类型是否将被创建并分配给 Create
的第三个参数?如果是这样,那是我应该分配给 IOUserClient*
指针的那个,它被传递给 NewUserClient
吗?
kern_return_t IMPL(MyUserUSBInterfaceDriver, NewUserClient) {
os_log(OS_LOG_DEFAULT, "%{public}d:", type);
IOPropertyName propertiesKey = "MyUserClientProperties";
IOService* client;
auto ret = Create(this, propertiesKey, &client, SUPERDISPATCH);
// Need to do more things here...
return ret;
}
无论我尝试什么,我总是得到一个断言,但我看不出是什么导致了它。
3 com.apple.DriverKit 0x0000000102f2b24b __assert_rtn + 102
4 com.apple.DriverKit 0x0000000102f2c20a IOService::Create_Impl(IOService*, char const*, IOService**) (.cold.2) + 35
5 com.apple.DriverKit 0x0000000102f1766b IOService::Create_Impl(IOService*, char const*, IOService**) + 91
6 com.apple.DriverKit 0x0000000102f2668f IOService::Create_Invoke(IORPC, OSMetaClassBase*, int (*)(OSMetaClassBase*, IOService*, char const*, IOService**)) + 135
7 com.apple.DriverKit 0x0000000102f276d7 IOService::Create(IOService*, char const*, IOService**, int (*)(OSMetaClassBase*, IORPC)) + 267
8 sc.example.MyUserUSBInterfaceDriver 0x0000000102ee0c89 MyUserUSBInterfaceDriver::NewUserClient_Impl(unsigned int, IOUserClient**) + 313 (MyUserUSBInterfaceDriver.cpp:155)
尽管 DriverKit 上的 WWDC 演示试图假装不是这样,但 DriverKit 的世界观与内核的世界观非常不同,您需要了解一些实现细节,因为抽象非常漏洞。
您可能已经发现,DriverKit 驱动程序中看起来像 IOService
的对象实际上是内核(和用户 space 的)中的 IOUserService
对象I/O 注册表的视图。这个差距是通过 DriverKit 的 IPC 机制弥补的。
为了创建新的用户客户端,您需要一个(内核)IOUserClient
subclass 的实例,它由您的 (dext) IOUserClient
sub[=73] 支持=].内核 class 实际上是 IOUserUserClient
。 (是的,真的。)正如您所发现的,文档并没有完全清楚地说明您是如何做到这一点的。我发现查看源代码方面的可用内容很有帮助 - 调用 NewUserClient
is implemented in the IOUserServer::serviceNewUserClient()
function here.
您会立即注意到的一件事是,如果缺少 IOServiceDEXTEntitlements
属性,这不会阻止代码成功:
prop = userUC->copyProperty(gIOServiceDEXTEntitlementsKey);
ok = checkEntitlements(entitlements, prop, NULL, NULL);
并在 checkEntitlements
中:
if (!prop) {
return true;
}
这是个好消息,因为这意味着我们暂时不需要担心它,可以将其搁置。
接下来,事实证明 propertiesKey
引用了提供者 IOUserService
内核对象 上的 属性 。您无法从 dext 的代码中设置这些属性,因此提供它们的唯一方法是从 IOKit 匹配个性字典。
您可以随意命名 属性,但是:
- 它的值必须是一个字典。
- 它必须包含
"IOClass"
键值对,指定 kernel class 实例化为字符串 - 在您的例子中,"IOUserUserClient"
- 它必须包含
"IOUserClass"
键值对。这指定要实例化的 dext class,再次作为字符串。在你的情况下,这看起来像MyUserClient
.
放在一起:
<key>IOKitPersonalities</key>
<dict>
<key>example_device</key>
<dict>
<key>MyUserClientProperties</key>
<dict>
<key>IOUserClass</key>
<string>MyUserClient</string>
<key>IOClass</key>
<string>IOUserUserClient</string>
</dict>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
…
</dict>
</dict>
…
然后,从您的 NewUserClient
函数调用:
IOService* client = nullptr;
kern_return_t ret = this->Create(this, "MyUserClient", &client);
我认为这里不需要 SUPERDISPATCH
,因为您大概不会在 class 中重写 Create
方法,所以您的超级实现无论如何都会被继承。
然后进行您可能需要的错误检查、任何其他初始化、准备等,最后:
*userClient = client;
return kIOReturnSuccess;