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

  1. 系统支持: getmsg 主要在 System V Unix 系统中可用
  2. Linux 限制: 大多数 Linux 系统不完全支持 STREAMS
  3. 移植性: 代码可移植性较差
  4. 替代方案: Linux 中可以使用其他 IPC 机制
  5. 错误处理: 始终检查返回值和 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

  1. 设备驱动: 在某些 Unix 系统中用于设备通信
  2. 网络协议: 实现复杂的网络协议栈
  3. 系统管理: 系统管理工具的消息传递
  4. 模块化处理: 数据流的模块化处理

STREAMS 架构概念 Link to heading

应用程序
    ↓
STREAMS 接口 (putmsg/getmsg)
    ↓
流首部 (Stream Head)
    ↓
模块 1 → 模块 2 → 模块 3
    ↓
驱动程序 (Driver)
    ↓
硬件设备

虽然 getmsg 在现代 Linux 系统中使用较少,但了解这个概念有助于理解 Unix 系统的消息传递机制和 STREAMS 架构的设计思想。在实际开发中,建议使用 Linux 原生的 IPC 机制。