3. 使用资源管理器框架
任务说明
在QNX系统中,资源管理器(Resource Manager)是设备驱动与用户空间交互的标准框架。本案例将基于QNX资源管理器框架创建一个名为/dev/mydevice的设备节点,实现基本的打开、关闭、读取和写入消息处理,并验证客户端能够通过标准文件I/O与该设备通信。
环境依赖
- QNX SDP 7.0或更高版本
- 目标板或QNX虚拟机
- 编译工具链:
qcc
核心实现
以下代码实现了一个最小化的资源管理器,注册设备节点并处理客户端消息。核心逻辑包括:初始化资源管理器属性、创建设备节点、分发处理函数、处理打开/关闭/读/写消息。
#include <sys/iofunc.h>
#include <sys/dispatch.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
// 设备上下文结构
typedef struct {
iofunc_attr_t attr;
char buffer[256];
int buffer_len;
} my_device_t;
// 设备属性
static my_device_t my_device;
// 打开设备时的处理
static int my_open(resmgr_context_t *ctp, io_open_t *msg, void *extra) {
return iofunc_open_default(ctp, msg, &my_device.attr, 0, 0);
}
// 关闭设备时的处理
static int my_close(resmgr_context_t *ctp, void *reserved, void *extra) {
return iofunc_close_default(ctp, reserved, &my_device.attr);
}
// 读取设备数据
static int my_read(resmgr_context_t *ctp, io_read_t *msg, void *extra) {
int bytes_read = 0;
int nbytes = msg->nbytes;
// 检查是否有数据可读
if (my_device.buffer_len > 0) {
bytes_read = (nbytes < my_device.buffer_len) ? nbytes : my_device.buffer_len;
memcpy(msg->data, my_device.buffer, bytes_read);
// 清除已读取的数据
memmove(my_device.buffer, my_device.buffer + bytes_read, my_device.buffer_len - bytes_read);
my_device.buffer_len -= bytes_read;
}
if (bytes_read > 0) {
return _RESMGR_PTR(ctp, msg->data, bytes_read);
}
return _RESMGR_EOF;
}
// 写入设备数据
static int my_write(resmgr_context_t *ctp, io_write_t *msg, void *extra) {
int nbytes = msg->nbytes;
int bytes_written = 0;
// 检查缓冲区空间
if (my_device.buffer_len + nbytes < sizeof(my_device.buffer)) {
memcpy(my_device.buffer + my_device.buffer_len, msg->data, nbytes);
my_device.buffer_len += nbytes;
bytes_written = nbytes;
} else {
// 缓冲区满,写入能容纳的部分
int available = sizeof(my_device.buffer) - my_device.buffer_len;
if (available > 0) {
memcpy(my_device.buffer + my_device.buffer_len, msg->data, available);
my_device.buffer_len += available;
bytes_written = available;
}
}
return _RESMGR_PTR(ctp, msg->data, bytes_written);
}
// 消息分发表
static resmgr_io_funcs_t io_funcs = {
_RESMGR_IO_NFUNCS,
my_open, // 打开
my_close, // 关闭
my_read, // 读取
my_write // 写入
};
int main(int argc, char **argv) {
dispatch_t *dpp;
resmgr_attr_t resmgr_attr;
resmgr_context_t *ctp;
int id;
// 初始化设备属性
memset(&my_device, 0, sizeof(my_device));
my_device.buffer_len = 0;
iofunc_attr_init(&my_device.attr, S_IFCHR | 0666, 0, 0);
// 创建分发器
dpp = dispatch_create();
if (dpp == NULL) {
perror("dispatch_create");
return 1;
}
// 初始化资源管理器属性
memset(&resmgr_attr, 0, sizeof(resmgr_attr));
resmgr_attr.nparts_max = 1;
resmgr_attr.msg_max_size = 2048;
// 创建资源管理器
id = resmgr_attach(dpp, &resmgr_attr, "/dev/mydevice", _FTYPE_ANY, 0, &io_funcs, &my_device);
if (id == -1) {
perror("resmgr_attach");
return 1;
}
printf("资源管理器已启动,设备节点: /dev/mydevice\n");
// 消息循环
ctp = resmgr_context_alloc(dpp);
while (1) {
if (resmgr_block(ctp) == EOK) {
resmgr_handler(ctp);
}
}
return 0;
}
编译与运行
- 将上述代码保存为
my_resource_mgr.c。 - 编译:
qcc -o my_resource_mgr my_resource_mgr.c - 在目标板上运行:
./my_resource_mgr(需要root权限)
验证方法
在另一个终端中执行以下命令验证设备节点功能:
# 1. 确认设备节点存在
ls -l /dev/mydevice
# 2. 写入数据
echo "Hello QNX" > /dev/mydevice
# 3. 读取数据
cat /dev/mydevice
# 4. 预期输出
Hello QNX
通过条件:
ls -l /dev/mydevice能显示字符设备节点,权限为crw-rw-rw-。- 写入操作无错误返回。
cat命令能正确读出之前写入的字符串内容。- 程序运行期间无崩溃或错误日志输出。
关键说明
- 资源管理器使用
dispatch机制接收客户端消息,resmgr_attach将路径名/dev/mydevice绑定到消息处理函数。 - 设备属性通过
iofunc_attr_init初始化为字符设备,权限设为0666允许所有用户访问。 - 读写操作使用内部缓冲区存储数据,写入的数据可被后续读取操作获取。
- 若需支持多个客户端并发访问,可在此基础上增加互斥锁保护缓冲区。