getmsg 函数详解 Link to heading
1. 函数介绍 Link to heading
getmsg
是 System V STREAMS 接口中的一个函数,用于从 STREAMS 设备或管道中接收消息。可以把 STREAMS 想象成一个"消息传送带系统"——数据以消息的形式在系统中流动,getmsg
就是从这个传送带上取下消息的工具。
STREAMS 是 Unix System V 中的一种模块化 I/O 框架,它允许在数据流中插入处理模块,实现复杂的数据处理。虽然在现代 Linux 系统中 STREAMS 使用较少,但在一些 Unix 系统(如 Solaris)中仍然重要。
getmsg
允许你接收包含控制信息和数据信息的消息,提供了比普通 read
更精细的控制。
2. 函数原型 Link to heading
#include <stropts.h>
int getmsg(int fildes, struct strbuf *ctlptr, struct strbuf *dataptr, int *flagsp);
3. 功能 Link to heading
getmsg
函数用于从 STREAMS 文件描述符中接收消息。它可以分别接收消息的控制部分和数据部分,提供了对消息结构的精细控制。
4. 参数 Link to heading
- fildes: STREAMS 设备或管道的文件描述符
- ctlptr: 指向
strbuf
结构体的指针,用于接收控制信息 - dataptr: 指向
strbuf
结构体的指针,用于接收数据信息 - flagsp: 指向标志的指针,用于指定接收模式和返回消息类型
5. strbuf 结构体 Link to heading
struct strbuf {
int maxlen; /* 缓冲区最大长度 */
int len; /* 实际数据长度 */
char *buf; /* 指向缓冲区的指针 */
};
6. flags 参数说明 Link to heading
输入标志(指定要接收的消息类型):
- 0: 接收下一条消息(按优先级顺序)
- RS_HIPRI: 接收下一条高优先级消息
输出标志(返回实际接收到的消息类型):
- 0: 普通优先级消息
- RS_HIPRI: 高优先级消息
7. 返回值 Link to heading
- 成功: 返回 0 或非负值
- 失败: 返回 -1,并设置相应的 errno 错误码
特殊返回值:
- MORECTL: 控制部分还有更多数据
- MOREDATA: 数据部分还有更多数据
常见错误码:
EBADF
: fildes 不是有效的文件描述符EINVAL
: 参数无效EIO
: I/O 错误ENOSTR
: fildes 不是 STREAMS 设备ENOSR
: 没有足够的 STREAMS 资源EAGAIN
: 非阻塞模式下无数据可读
8. 相似函数或关联函数 Link to heading
- putmsg: 发送消息到 STREAMS 设备
- getpmsg: 获取带优先级的消息(更高级的版本)
- putpmsg: 发送带优先级的消息
- ioctl: 控制 STREAMS 设备
- read/write: 普通的文件读写操作
9. 示例代码 Link to heading
示例1:基础用法 - 简单的消息接收 Link to heading
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stropts.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
// 注意:这个示例在大多数 Linux 系统上可能无法运行
// 因为 Linux 不完全支持 STREAMS
int main() {
int fd;
struct strbuf ctlbuf, databuf;
char ctl_data[256], data_buf[1024];
int flags;
printf("=== getmsg 基础示例 ===\n\n");
// 初始化缓冲区结构
ctlbuf.maxlen = sizeof(ctl_data);
ctlbuf.buf = ctl_data;
ctlbuf.len = 0;
databuf.maxlen = sizeof(data_buf);
databuf.buf = data_buf;
databuf.len = 0;
flags = 0; // 接收普通消息
printf("注意: getmsg 主要用于 STREAMS 系统\n");
printf("在大多数 Linux 系统上可能不可用\n\n");
// 尝试打开一个 STREAMS 设备(示例)
// fd = open("/dev/stream_device", O_RDONLY);
// 由于大多数系统没有 STREAMS 设备,这里只演示结构
printf("控制缓冲区设置:\n");
printf(" 最大长度: %d\n", ctlbuf.maxlen);
printf(" 缓冲区地址: %p\n", (void*)ctlbuf.buf);
printf("数据缓冲区设置:\n");
printf(" 最大长度: %d\n", databuf.maxlen);
printf(" 缓冲区地址: %p\n", (void*)databuf.buf);
printf("标志设置: %d\n", flags);
printf("\n如果在支持 STREAMS 的系统上,可以这样调用:\n");
printf("result = getmsg(fd, &ctlbuf, &databuf, &flags);\n");
return 0;
}
示例2:模拟 STREAMS 消息处理 Link to heading
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 模拟 STREAMS 消息结构
struct simulated_msg {
int priority; // 消息优先级
int control_len; // 控制数据长度
char control_data[256]; // 控制数据
int data_len; // 数据长度
char data[1024]; // 实际数据
};
// 模拟的 strbuf 结构
struct simulated_strbuf {
int maxlen;
int len;
char *buf;
};
// 模拟的 getmsg 函数
int simulated_getmsg(struct simulated_msg *msg,
struct simulated_strbuf *ctlptr,
struct simulated_strbuf *dataptr,
int *flagsp) {
printf("=== 模拟 getmsg 操作 ===\n");
// 复制控制数据
if (ctlptr && ctlptr->buf) {
int copy_len = (msg->control_len < ctlptr->maxlen) ?
msg->control_len : ctlptr->maxlen;
memcpy(ctlptr->buf, msg->control_data, copy_len);
ctlptr->len = copy_len;
printf("复制控制数据: %d 字节\n", copy_len);
}
// 复制数据
if (dataptr && dataptr->buf) {
int copy_len = (msg->data_len < dataptr->maxlen) ?
msg->data_len : dataptr->maxlen;
memcpy(dataptr->buf, msg->data, copy_len);
dataptr->len = copy_len;
printf("复制数据: %d 字节\n", copy_len);
}
// 设置标志
if (flagsp) {
*flagsp = (msg->priority > 0) ? 1 : 0; // 模拟高优先级标志
}
printf("消息优先级: %s\n",
(msg->priority > 0) ? "高" : "普通");
printf("控制数据长度: %d\n", msg->control_len);
printf("数据长度: %d\n", msg->data_len);
return 0; // 成功
}
// 创建测试消息
struct simulated_msg* create_test_message() {
static struct simulated_msg msg;
msg.priority = 1; // 高优先级
msg.control_len = strlen("CONTROL_INFO") + 1;
strcpy(msg.control_data, "CONTROL_INFO");
msg.data_len = strlen("Hello, STREAMS World!") + 1;
strcpy(msg.data, "Hello, STREAMS World!");
return &msg;
}
int main() {
struct simulated_msg *test_msg;
struct simulated_strbuf ctlbuf, databuf;
char ctl_buffer[256], data_buffer[1024];
int flags;
int result;
printf("=== STREAMS 消息处理模拟 ===\n\n");
// 创建测试消息
test_msg = create_test_message();
printf("创建测试消息完成\n\n");
// 初始化缓冲区
ctlbuf.maxlen = sizeof(ctl_buffer);
ctlbuf.buf = ctl_buffer;
ctlbuf.len = 0;
databuf.maxlen = sizeof(data_buffer);
databuf.buf = data_buffer;
databuf.len = 0;
flags = 0;
// 调用模拟的 getmsg
result = simulated_getmsg(test_msg, &ctlbuf, &databuf, &flags);
if (result == 0) {
printf("\n=== 接收结果 ===\n");
printf("控制数据 (%d 字节): %s\n", ctlbuf.len, ctlbuf.buf);
printf("数据 (%d 字节): %s\n", databuf.len, databuf.buf);
printf("消息标志: %s\n", (flags > 0) ? "高优先级" : "普通优先级");
} else {
printf("接收消息失败\n");
}
return 0;
}
示例3:完整的 STREAMS 消息系统模拟 Link to heading
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
// 消息类型定义
#define MSG_NORMAL 0
#define MSG_HIGH_PRIORITY 1
#define MSG_CONTROL 2
#define MSG_DATA 3
// 模拟的 STREAMS 消息队列
struct message_queue {
int count;
struct {
int priority;
int type;
time_t timestamp;
int control_len;
char control_data[128];
int data_len;
char data[512];
} messages[10];
};
// 模拟的 strbuf 结构
struct my_strbuf {
int maxlen;
int len;
char *buf;
};
// 全局消息队列
struct message_queue global_queue = {0};
// 向队列添加消息
int add_message(int priority, int type,
const char *control, const char *data) {
if (global_queue.count >= 10) {
printf("消息队列已满\n");
return -1;
}
int index = global_queue.count++;
global_queue.messages[index].priority = priority;
global_queue.messages[index].type = type;
global_queue.messages[index].timestamp = time(NULL);
if (control) {
global_queue.messages[index].control_len = strlen(control) + 1;
strncpy(global_queue.messages[index].control_data, control, 127);
global_queue.messages[index].control_data[127] = '\0';
} else {
global_queue.messages[index].control_len = 0;
global_queue.messages[index].control_data[0] = '\0';
}
if (data) {
global_queue.messages[index].data_len = strlen(data) + 1;
strncpy(global_queue.messages[index].data, data, 511);
global_queue.messages[index].data[511] = '\0';
} else {
global_queue.messages[index].data_len = 0;
global_queue.messages[index].data[0] = '\0';
}
printf("添加消息: 优先级=%d, 类型=%d, 控制=%s, 数据=%s\n",
priority, type, control ? control : "无", data ? data : "无");
return 0;
}
// 模拟的 getmsg 实现
int my_getmsg(struct my_strbuf *ctlptr,
struct my_strbuf *dataptr,
int *flagsp) {
if (global_queue.count == 0) {
printf("消息队列为空\n");
return -1;
}
// 查找高优先级消息(如果请求)
int msg_index = 0;
if (flagsp && (*flagsp & 1)) { // 模拟 RS_HIPRI
for (int i = 0; i < global_queue.count; i++) {
if (global_queue.messages[i].priority > 0) {
msg_index = i;
break;
}
}
}
// 获取消息
struct message_queue *msg = &global_queue.messages[msg_index];
// 复制控制数据
if (ctlptr && ctlptr->buf) {
int copy_len = (msg->control_len < ctlptr->maxlen) ?
msg->control_len : ctlptr->maxlen;
memcpy(ctlptr->buf, msg->control_data, copy_len);
ctlptr->len = copy_len;
}
// 复制数据
if (dataptr && dataptr->buf) {
int copy_len = (msg->data_len < dataptr->maxlen) ?
msg->data_len : dataptr->maxlen;
memcpy(dataptr->buf, msg->data, copy_len);
dataptr->len = copy_len;
}
// 设置返回标志
if (flagsp) {
*flagsp = (msg->priority > 0) ? 1 : 0; // 高优先级标志
}
// 从队列中移除消息
for (int i = msg_index; i < global_queue.count - 1; i++) {
global_queue.messages[i] = global_queue.messages[i + 1];
}
global_queue.count--;
return 0;
}
// 显示队列状态
void show_queue_status() {
printf("\n=== 消息队列状态 ===\n");
printf("队列中消息数量: %d\n", global_queue.count);
for (int i = 0; i < global_queue.count; i++) {
printf("消息 %d: 优先级=%d, 类型=%d, 时间=%s",
i, global_queue.messages[i].priority,
global_queue.messages[i].type,
ctime(&global_queue.messages[i].timestamp));
printf(" 控制: %s\n", global_queue.messages[i].control_data);
printf(" 数据: %s\n", global_queue.messages[i].data);
}
}
int main() {
struct my_strbuf ctlbuf, databuf;
char ctl_buffer[256], data_buffer[1024];
int flags;
int result;
printf("=== 完整的 STREAMS 消息系统模拟 ===\n\n");
// 初始化缓冲区
ctlbuf.maxlen = sizeof(ctl_buffer);
ctlbuf.buf = ctl_buffer;
ctlbuf.len = 0;
databuf.maxlen = sizeof(data_buffer);
databuf.buf = data_buffer;
databuf.len = 0;
// 添加测试消息
printf("添加测试消息...\n");
add_message(0, MSG_NORMAL, "NORMAL_CTL", "普通消息数据");
add_message(1, MSG_HIGH_PRIORITY, "HIGH_CTL", "高优先级消息");
add_message(0, MSG_DATA, "DATA_CTL", "另一个普通消息");
add_message(1, MSG_CONTROL, "CTRL_CTL", "高优先级控制消息");
show_queue_status();
// 测试接收普通消息
printf("\n--- 测试1: 接收普通消息 ---\n");
flags = 0; // 普通消息
result = my_getmsg(&ctlbuf, &databuf, &flags);
if (result == 0) {
printf("成功接收消息:\n");
printf(" 控制数据: %.*s\n", ctlbuf.len, ctlbuf.buf);
printf(" 数据: %.*s\n", databuf.len, databuf.buf);
printf(" 优先级: %s\n", (flags > 0) ? "高" : "普通");
}
// 测试接收高优先级消息
printf("\n--- 测试2: 接收高优先级消息 ---\n");
flags = 1; // 高优先级消息
result = my_getmsg(&ctlbuf, &databuf, &flags);
if (result == 0) {
printf("成功接收高优先级消息:\n");
printf(" 控制数据: %.*s\n", ctlbuf.len, ctlbuf.buf);
printf(" 数据: %.*s\n", databuf.len, databuf.buf);
printf(" 优先级: %s\n", (flags > 0) ? "高" : "普通");
}
show_queue_status();
printf("\n=== STREAMS 概念说明 ===\n");
printf("STREAMS 是 System V 中的消息传递机制\n");
printf("特点:\n");
printf("1. 消息包含控制部分和数据部分\n");
printf("2. 支持消息优先级\n");
printf("3. 模块化处理架构\n");
printf("4. 主要在 Solaris 等系统中使用\n");
printf("\n在 Linux 中,类似功能可通过以下方式实现:\n");
printf("- Unix 域套接字\n");
printf("- 管道和 FIFO\n");
printf("- netlink 套接字\n");
printf("- D-Bus 消息系统\n");
return 0;
}
编译和运行说明 Link to heading
# 编译示例程序
gcc -o getmsg_example1 example1.c
gcc -o getmsg_example2 example2.c
gcc -o getmsg_example3 example3.c
# 运行示例
./getmsg_example1
./getmsg_example2
./getmsg_example3
STREAMS 系统检查 Link to heading
# 检查系统是否支持 STREAMS
ls /usr/include/stropts.h
# 在支持 STREAMS 的系统上编译
gcc -DSTREAMS_AVAILABLE -o getmsg_real example_real.c -lstrmi
# 查看 STREAMS 相关设备
ls /dev/* | grep stream
重要注意事项 Link to heading
- 系统支持:
getmsg
主要在 System V Unix 系统中可用 - Linux 限制: 大多数 Linux 系统不完全支持 STREAMS
- 移植性: 代码可移植性较差
- 替代方案: Linux 中可以使用其他 IPC 机制
- 错误处理: 始终检查返回值和 errno
现代 Linux 替代方案 Link to heading
使用 Unix 域套接字 Link to heading
#include <sys/socket.h>
#include <sys/un.h>
// 创建 Unix 域套接字进行消息传递
int create_unix_socket(const char *path) {
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
// ... 配置和使用套接字
return sock;
}
使用管道 Link to heading
#include <unistd.h>
// 创建管道进行进程间通信
int pipe_fd[2];
if (pipe(pipe_fd) == 0) {
// 使用 pipe_fd[0] 读取,pipe_fd[1] 写入
}
实际应用场景 Link to heading
- 设备驱动: 在某些 Unix 系统中用于设备通信
- 网络协议: 实现复杂的网络协议栈
- 系统管理: 系统管理工具的消息传递
- 模块化处理: 数据流的模块化处理
STREAMS 架构概念 Link to heading
应用程序
↓
STREAMS 接口 (putmsg/getmsg)
↓
流首部 (Stream Head)
↓
模块 1 → 模块 2 → 模块 3
↓
驱动程序 (Driver)
↓
硬件设备
虽然 getmsg
在现代 Linux 系统中使用较少,但了解这个概念有助于理解 Unix 系统的消息传递机制和 STREAMS 架构的设计思想。在实际开发中,建议使用 Linux 原生的 IPC 机制。