我正在ioctl从C++调用一个我不拥有/维护的驱动程序,并且我正在尝试理清是否有一个干净的"安全"机制来处理所需的一些丑陋的结构分配.
涉及一些结构的细长版本
// IOCTL expects an instance of this structure "first"
typedef struct {
int param1;
int param2;
} s_ioctl_request;
//... followed by an instance of this. If attr_length
// is > sizeof(s_attr_header), more data is allowed to follow.
typedef struct {
uint32_t attr_length;
uint32_t attr_type;
} s_attr_header;
// Example that uses more data than just the header.
typedef struct {
s_attr_header hdr;
uint32_t attr_param;
} s_attr_type1;
// Another example.
typedef struct {
s_attr_header hdr;
uint32_t attr_param1;
uint32_t attr_param2;
} s_attr_type2;
Run Code Online (Sandbox Code Playgroud)
ioctl要求s_ioctl_request紧跟一个s_attr_header或包含它的其他结构,其中attr_length设置为外部结构的大小(以字节为单位).
在C,为它写一个包装器ioctl将通过这些方面的东西来完成:
int do_ugly_ioctl(int fd, int p1, int p2, s_attr_header * attr)
{
int res;
// Allocate enough memory for both structures.
s_ioctl_request *req = malloc( sizeof(*req) + attr->hdr.attr_length );
// Copy the 2nd, (variable length) structure after the first.
memcpy( ((char*)req) + sizeof(*req), attr, attr->hdr.attr_length);
// Modify params as necessary
req->param1 = p1;
req->param2 = p2;
// Make the driver call, free mem, and return result.
res = ioctl(fd, SOME_IOCTL_ID, req);
free(req);
return res;
}
// Example invocation.
s_attr_type1 a1;
a1.hdr.attr_length = sizeof(a1);
a1.hdr.attr_type = 1;
do_ugly_ioctl(fd, 10, 20, &a1);
Run Code Online (Sandbox Code Playgroud)
我想到的几个选项是:
抛出现代C++ - 窗外的主义,并完全按照我上面所示的那样做.
使用std :: vector分配存储,然后使用生成的std :: vector :: data()指针进行丑陋的转换,这样至少我没有做new[]/ delete[]或malloc/ free.
为每个s_attr_type*使用自己的"特殊"结构的人创建一个独特的包装器方法.这似乎是"最安全的",即包装方法的用户最不可能将其搞砸.和奖励积分,允许pass-by-ref.
方法#3示例:
int do_ugly_ioctl(fd, int param1, int param2, s_attr_type2& attr){
struct RequestData {
s_ioctl_request ioreq;
s_attr_type2 attr;
};
RequestData r;
r.ioreq.param1 = param1;
r.ioreq.param2 = param2;
r.attr = attr;
r.attr.hdr.attr_length = sizeof(attr); // Might as well enforce this here.
ioctl(fd, SOME_IOCTL_ID, (void*) &r);
}
Run Code Online (Sandbox Code Playgroud)
所以我想这里的一些问题是:
它对C++来说是否"值得" - 这个问题的解决方案是什么?(而不是依赖于更容易出错的C impl).
如果我使用方法#3或类似方法,是否有任何我可以<type_traits>用来制作此函数的模板并且只接受带有s_attr_header第一个成员的结构?
还有其他好主意吗?
完全值得,而且你的解决方案非常好。您可能希望将结构声明为packed(有编译器扩展可以实现此目的),以避免在组合多个结构时出现额外的填充。
您还可以在构造函数中设置结构的大小。
struct RequestData
{
RequestData() : ioreq{}, attr{}
{
attr.hdr.attr_length = sizeof(attr);
}
s_ioctl_request ioreq;
s_attr_type2 attr;
};
Run Code Online (Sandbox Code Playgroud)
关于你的第二个问题,你可以将作业分成两部分,这不太好,但很容易,如果你传递的内容没有正确的标头,则会导致编译器错误:
template<typename Attr>
int do_ugly_ioctl(fd, int param1, int param2, Attr& attr){
struct RequestData {
s_ioctl_request ioreq;
Attr attr;
};
RequestData r;
r.ioreq.param1 = param1;
r.ioreq.param2 = param2;
s_attr_header hdr = Attr.hdr; //this will lead to compilation error if the type is not what we expect
(void) hdr;
r.attr = attr;
r.attr.hdr.attr_length = sizeof(attr); // Might as well enforce this here.
ioctl(fd, SOME_IOCTL_ID, (void*) &r);
}
Run Code Online (Sandbox Code Playgroud)