getmsg系统调用及示例

CalcGuide ≈ 37 分钟阅读 · 12.8k 字

getmsg 函数详解

  1. 函数介绍

getmsg 是 System V STREAMS 接口中的一个函数,用于从 STREAMS 设备或管道中接收消息。可以把 STREAMS 想象成一个”消息传送带系统”——数据以消息的形式在系统中流动,getmsg 就是从这个传送带上取下消息的工具。

STREAMS 是 Unix System V 中的一种模块化 I/O 框架,它允许在数据流中插入处理模块,实现复杂的数据处理。虽然在现代 Linux 系统中 STREAMS 使用较少,但在一些 Unix 系统(如 Solaris)中仍然重要。

getmsg 允许你接收包含控制信息和数据信息的消息,提供了比普通 read 更精细的控制。

getmsg系统调用及示例-CSDN博客

  1. 函数原型
#include <stropts.h>

int getmsg(int fildes, struct strbuf *ctlptr, struct strbuf *dataptr, int *flagsp);
  1. 功能

getmsg 函数用于从 STREAMS 文件描述符中接收消息。它可以分别接收消息的控制部分和数据部分,提供了对消息结构的精细控制。

  1. 参数
  • fildes: STREAMS 设备或管道的文件描述符

  • ctlptr: 指向 strbuf 结构体的指针,用于接收控制信息

  • dataptr: 指向 strbuf 结构体的指针,用于接收数据信息

  • flagsp: 指向标志的指针,用于指定接收模式和返回消息类型

  1. strbuf 结构体
struct strbuf {
    int     maxlen;    /* 缓冲区最大长度 */
    int     len;       /* 实际数据长度 */
    char    *buf;      /* 指向缓冲区的指针 */
};
  1. flags 参数说明

输入标志(指定要接收的消息类型):

  • 0: 接收下一条消息(按优先级顺序)

  • RS_HIPRI: 接收下一条高优先级消息

输出标志(返回实际接收到的消息类型):

  • 0: 普通优先级消息

  • RS_HIPRI: 高优先级消息

  1. 返回值
  • 成功: 返回 0 或非负值

  • 失败: 返回 -1,并设置相应的 errno 错误码

特殊返回值:

  • MORECTL: 控制部分还有更多数据

  • MOREDATA: 数据部分还有更多数据

常见错误码:

  • EBADF: fildes 不是有效的文件描述符

  • EINVAL: 参数无效

  • EIO: I/O 错误

  • ENOSTR: fildes 不是 STREAMS 设备

  • ENOSR: 没有足够的 STREAMS 资源

  • EAGAIN: 非阻塞模式下无数据可读

  1. 相似函数或关联函数
  • putmsg: 发送消息到 STREAMS 设备

  • getpmsg: 获取带优先级的消息(更高级的版本)

  • putpmsg: 发送带优先级的消息

  • ioctl: 控制 STREAMS 设备

  • read/write: 普通的文件读写操作

  1. 示例代码

示例1:基础用法 - 简单的消息接收

#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&#91;256], data_buf&#91;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 消息处理

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 模拟 STREAMS 消息结构
struct simulated_msg {
    int priority;           // 消息优先级
    int control_len;        // 控制数据长度
    char control_data&#91;256]; // 控制数据
    int data_len;           // 数据长度
    char data&#91;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&#91;256], data_buffer&#91;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 消息系统模拟

#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&#91;128];
        int data_len;
        char data&#91;512];
    } messages&#91;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&#91;index].priority = priority;
    global_queue.messages&#91;index].type = type;
    global_queue.messages&#91;index].timestamp = time(NULL);
    
    if (control) {
        global_queue.messages&#91;index].control_len = strlen(control) + 1;
        strncpy(global_queue.messages&#91;index].control_data, control, 127);
        global_queue.messages&#91;index].control_data&#91;127] = '\0';
    } else {
        global_queue.messages&#91;index].control_len = 0;
        global_queue.messages&#91;index].control_data&#91;0] = '\0';
    }
    
    if (data) {
        global_queue.messages&#91;index].data_len = strlen(data) + 1;
        strncpy(global_queue.messages&#91;index].data, data, 511);
        global_queue.messages&#91;index].data&#91;511] = '\0';
    } else {
        global_queue.messages&#91;index].data_len = 0;
        global_queue.messages&#91;index].data&#91;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&#91;i].priority > 0) {
                msg_index = i;
                break;
            }
        }
    }
    
    // 获取消息
    struct message_queue *msg = &global_queue.messages&#91;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&#91;i] = global_queue.messages&#91;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&#91;i].priority,
               global_queue.messages&#91;i].type,
               ctime(&global_queue.messages&#91;i].timestamp));
        printf("  控制: %s\n", global_queue.messages&#91;i].control_data);
        printf("  数据: %s\n", global_queue.messages&#91;i].data);
    }
}

int main() {
    struct my_strbuf ctlbuf, databuf;
    char ctl_buffer&#91;256], data_buffer&#91;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;
}

编译和运行说明

# 编译示例程序
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 系统检查

# 检查系统是否支持 STREAMS
ls /usr/include/stropts.h

# 在支持 STREAMS 的系统上编译
gcc -DSTREAMS_AVAILABLE -o getmsg_real example_real.c -lstrmi

# 查看 STREAMS 相关设备
ls /dev/* | grep stream

重要注意事项

系统支持: getmsg 主要在 System V Unix 系统中可用

Linux 限制: 大多数 Linux 系统不完全支持 STREAMS

移植性: 代码可移植性较差

替代方案: Linux 中可以使用其他 IPC 机制

错误处理: 始终检查返回值和 errno

现代 Linux 替代方案

使用 Unix 域套接字

#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;
}

使用管道

#include <unistd.h>

// 创建管道进行进程间通信
int pipe_fd&#91;2];
if (pipe(pipe_fd) == 0) {
    // 使用 pipe_fd&#91;0] 读取,pipe_fd&#91;1] 写入
}

实际应用场景

设备驱动: 在某些 Unix 系统中用于设备通信

网络协议: 实现复杂的网络协议栈

系统管理: 系统管理工具的消息传递

模块化处理: 数据流的模块化处理

STREAMS 架构概念

应用程序

STREAMS 接口 (putmsg/getmsg)

流首部 (Stream Head)

模块 1 → 模块 2 → 模块 3

驱动程序 (Driver)

硬件设备

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

相关阅读