在 JNA 的联合内映射结构
Mapping a structure inside a union in JNA
我正在尝试使用 JNA 将 Solaris 11.3 中的 kstat library 映射到 Java。虽然我已经设法让大部分结构正常工作,但在过去的 24 小时里,我一直在与一个特别困难的联合结构内联合体作斗争。
我成功地检索了一个指向 kstat_named
结构的指针,我需要使用 kstat_data_lookup()。我的代码正确检索了此 C 结构中的大部分数据(名称、data_type 和联合的非结构成员):
typedef struct kstat_named {
char name[KSTAT_STRLEN]; /* name of counter */
uchar_t data_type; /* data type */
union {
charc[16]; /* enough for 128-bit ints */
struct {
union {
char *ptr; /* NULL-terminated string */
} addr;
uint32_t len; /* length of string */
} str;
int32_t i32;
uint32_t ui32;
int64_t i64;
uint64_t ui64;
/* These structure members are obsolete */
int32_t l;
uint32_t ul;
int64_t ll;
uint64_t ull;
} value; /* value of counter */
} kstat_named_t;
我已经在 JNA 中映射如下:
class KstatNamed extends Structure {
public static class UNION extends Union {
public byte[] charc = new byte[16]; // enough for 128-bit ints
public Pointer str; // KstatNamedString
public int i32;
public int ui32;
public long i64;
public long ui64;
}
public byte[] name = new byte[KSTAT_STRLEN]; // name of counter
public byte data_type; // data type
public UNION value; // value of counter
public KstatNamed() {
super();
}
public KstatNamed(Pointer p) {
super();
this.useMemory(p);
this.read();
}
@Override
public void read() {
super.read();
switch (data_type) {
case KSTAT_DATA_CHAR:
value.setType(byte[].class);
break;
case KSTAT_DATA_STRING:
value.setType(Pointer.class);
break;
case KSTAT_DATA_INT32:
case KSTAT_DATA_UINT32:
value.setType(int.class);
break;
case KSTAT_DATA_INT64:
case KSTAT_DATA_UINT64:
value.setType(long.class);
break;
default:
break;
}
value.read();
}
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "name", "data_type", "value" });
}
}
此代码适用于 int32 类型 (KSTAT_DATA_INT32)。但是,当数据类型为 KSTAT_DATA_STRING 时,对应于 union
中的 str
结构,我无法正确检索数据。
我已经映射了这样的嵌套结构:
class KstatNamedString extends Structure {
public static class UNION extends Union {
public Pointer ptr; // NULL-terminated string
}
public UNION addr;
public int len; // length of string
public KstatNamedString() {
super();
}
public KstatNamedString(Pointer p) {
super();
this.useMemory(p);
this.read();
}
@Override
public void read() {
super.read();
addr.setType(Pointer.class);
addr.read();
}
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "addr", "len" });
}
}
最终我试图复制这个 C 宏的行为:
#define KSTAT_NAMED_STR_PTR(knptr) ((knptr)->value.str.addr.ptr)
我尝试了多种不同的方法来尝试访问上述结构,但它似乎从未读取过正确的数据(len
值以百万计,并试图读取字符串 ptr
导致段错误)。我试过:
Pointer p = LibKstat.INSTANCE.kstat_data_lookup(ksp, name);
KstatNamed data = new KstatNamed(p);
KstatNamedString str = new KstatNamedString(data.value.str);
return str.addr.ptr.getString(0); // <--- Segfault on C side
我也试过:
- 指定
KstatNamedString
作为类型而不是 Pointer
类型
- 在结构和联合中使用
ByReference
的各种组合
我到处搜索,包括尝试我认为有希望的结果 here,但似乎没有任何效果。
我确定我遗漏了一些简单的东西。
使用 KstatNamedString
代替 Pointer
类型。
像这样更改基于指针的构造函数:
public KstatNamed(Pointer p) {
super(p);
this.read();
}
public KstatNamedString(Pointer p) {
super(p);
this.read();
}
并将 str
结构字段的 addr
字段更改为简单的 Pointer
(不需要它周围的联合位)。
public Pointer /*UNION*/ addr;
运行 带有 -Djna.dump_memory=true
的 JVM,并将新初始化的 Structure
打印为字符串。这将向您展示 JNA 如何解释结构的内存布局,以及如何初始化本机内存。这应该可以帮助您确定如何提取您正在寻找的字符串(假设它在那里)。
您还可以将联合 read()
方法调整为在设置联合类型之前仅读取类型字段(使用 Structure.readField("data_type")
)。
我正在尝试使用 JNA 将 Solaris 11.3 中的 kstat library 映射到 Java。虽然我已经设法让大部分结构正常工作,但在过去的 24 小时里,我一直在与一个特别困难的联合结构内联合体作斗争。
我成功地检索了一个指向 kstat_named
结构的指针,我需要使用 kstat_data_lookup()。我的代码正确检索了此 C 结构中的大部分数据(名称、data_type 和联合的非结构成员):
typedef struct kstat_named {
char name[KSTAT_STRLEN]; /* name of counter */
uchar_t data_type; /* data type */
union {
charc[16]; /* enough for 128-bit ints */
struct {
union {
char *ptr; /* NULL-terminated string */
} addr;
uint32_t len; /* length of string */
} str;
int32_t i32;
uint32_t ui32;
int64_t i64;
uint64_t ui64;
/* These structure members are obsolete */
int32_t l;
uint32_t ul;
int64_t ll;
uint64_t ull;
} value; /* value of counter */
} kstat_named_t;
我已经在 JNA 中映射如下:
class KstatNamed extends Structure {
public static class UNION extends Union {
public byte[] charc = new byte[16]; // enough for 128-bit ints
public Pointer str; // KstatNamedString
public int i32;
public int ui32;
public long i64;
public long ui64;
}
public byte[] name = new byte[KSTAT_STRLEN]; // name of counter
public byte data_type; // data type
public UNION value; // value of counter
public KstatNamed() {
super();
}
public KstatNamed(Pointer p) {
super();
this.useMemory(p);
this.read();
}
@Override
public void read() {
super.read();
switch (data_type) {
case KSTAT_DATA_CHAR:
value.setType(byte[].class);
break;
case KSTAT_DATA_STRING:
value.setType(Pointer.class);
break;
case KSTAT_DATA_INT32:
case KSTAT_DATA_UINT32:
value.setType(int.class);
break;
case KSTAT_DATA_INT64:
case KSTAT_DATA_UINT64:
value.setType(long.class);
break;
default:
break;
}
value.read();
}
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "name", "data_type", "value" });
}
}
此代码适用于 int32 类型 (KSTAT_DATA_INT32)。但是,当数据类型为 KSTAT_DATA_STRING 时,对应于 union
中的 str
结构,我无法正确检索数据。
我已经映射了这样的嵌套结构:
class KstatNamedString extends Structure {
public static class UNION extends Union {
public Pointer ptr; // NULL-terminated string
}
public UNION addr;
public int len; // length of string
public KstatNamedString() {
super();
}
public KstatNamedString(Pointer p) {
super();
this.useMemory(p);
this.read();
}
@Override
public void read() {
super.read();
addr.setType(Pointer.class);
addr.read();
}
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "addr", "len" });
}
}
最终我试图复制这个 C 宏的行为:
#define KSTAT_NAMED_STR_PTR(knptr) ((knptr)->value.str.addr.ptr)
我尝试了多种不同的方法来尝试访问上述结构,但它似乎从未读取过正确的数据(len
值以百万计,并试图读取字符串 ptr
导致段错误)。我试过:
Pointer p = LibKstat.INSTANCE.kstat_data_lookup(ksp, name);
KstatNamed data = new KstatNamed(p);
KstatNamedString str = new KstatNamedString(data.value.str);
return str.addr.ptr.getString(0); // <--- Segfault on C side
我也试过:
- 指定
KstatNamedString
作为类型而不是Pointer
类型 - 在结构和联合中使用
ByReference
的各种组合
我到处搜索,包括尝试我认为有希望的结果 here,但似乎没有任何效果。
我确定我遗漏了一些简单的东西。
使用 KstatNamedString
代替 Pointer
类型。
像这样更改基于指针的构造函数:
public KstatNamed(Pointer p) {
super(p);
this.read();
}
public KstatNamedString(Pointer p) {
super(p);
this.read();
}
并将 str
结构字段的 addr
字段更改为简单的 Pointer
(不需要它周围的联合位)。
public Pointer /*UNION*/ addr;
运行 带有 -Djna.dump_memory=true
的 JVM,并将新初始化的 Structure
打印为字符串。这将向您展示 JNA 如何解释结构的内存布局,以及如何初始化本机内存。这应该可以帮助您确定如何提取您正在寻找的字符串(假设它在那里)。
您还可以将联合 read()
方法调整为在设置联合类型之前仅读取类型字段(使用 Structure.readField("data_type")
)。