千家信息网

系统panic后主动广播最后内核dmesg信息-一个几乎可以使用的方案

发表于:2026-10-31 作者:千家信息网编辑
千家信息网最后更新 2026年10月31日,在《远程触发SYSRQ获取最新的dmesg信息-一个几乎没有什么用的方案》中,我认为远程触发SYSRQ并没有什么实际的用处,系统没有挂起时,用SSH等标准方式会好很多,系统挂起时,远程触发在多数情况下
千家信息网最后更新 2026年10月31日系统panic后主动广播最后内核dmesg信息-一个几乎可以使用的方案

在《远程触发SYSRQ获取最新的dmesg信息-一个几乎没有什么用的方案》中,我认为远程触发SYSRQ并没有什么实际的用处,系统没有挂起时,用SSH等标准方式会好很多,系统挂起时,远程触发在多数情况下均无法得到响应。那么有什么方法在系统panic的时候通知外部呢?
当然采用crash kexec kernel的方式会是一个好方法,但是那毕竟是为了debug而生的,有的时候只是想知道dmesg的最后几行就好,要知道此时系统已经完全没有机会记 录日志了,用户态的任何进程均已经全军覆没。但是别忘了还有一个口子可以将最后的信息送出去,那就是网络。为了使得信息更加可能被接收,我觉得采用广播会 比较好。具体的方式我在《远程触发SYSRQ获取最新的dmesg信息-一个几乎没有什么用的方案》的最后已经给出了。本文主要给出一个切实可以运行的代码。这个代码和平时的代码有所不同,那就是这个代码是在系统已经panic了以后被触发的,此时的系统很可能全然混乱,因此此时最好的做法就是:
1.减少内存操作;
2.不处理异常流;
3.尽可能简化,不再计算校验值;
4.避免ARP交互,直接广播。
代码的逻辑如下:
1.加载模块:预先分配skb和get device,为了不在panic以后处理内存;注册通知链。
2.panic之后:调用通知链的通知操作,用最简化的方式广播没有校验码的数据包。

代码如下:


#include #include #include struct sk_buff * skb = NULL;struct net_device * dev = NULL;u8 ethhdr [ETH_HLEN] = {        0xff, 0xff, 0xff, 0xff, 0xff, 0xff,        0x00, 0x0c, 0x29, 0xdd, 0xaa, 0xfd,        0x00, 0x00};u8 iphdr [20] = {        0x45,        0x00,        0x00, 0x00,  //total length        0x00, 0x00,  //ID        0x40, 0x00,  //Don't fragment        0x40,        //TTL        0xff,        // UDP?        0x00, 0x00,  //checksum        0x02, 0x02, 0x02, 0x02,        0xff, 0xff, 0xff, 0xff};u8 data[1024] = {0};void do_nothing (struct sk_buff *skb){    /* 不再进行任何内存操作 */    //dev_put(skb->dev);}static int send_last_msg(struct notifier_block *self,            unsigned long event, void *unused){    int ret;    struct ethhdr *eth;    struct iphdr *ip;    char *p;    /* 获取内核缓冲区中的数据 */    kernel_log_buffer(data, sizeof(data));     skb->dev = dev;    skb->pkt_type = PACKET_OTHERHOST;    skb->protocol = htons(ETH_P_IP);    skb->ip_summed = CHECKSUM_NONE;    skb->destructor = do_nothing;    skb->priority = 0;    /* 保留skb区域 */    skb_reserve (skb, 2 + ETH_HLEN + sizeof(struct iphdr) + sizeof(data));    /* 构造数据区(使用UDP会比较好,但是懒的封装了) */    p = skb_push(skb, sizeof(data));    memcpy(p, &data[0], sizeof(data));    skb_reset_transport_header(skb);    /* 构造IP头 */    p = skb_push(skb, sizeof(struct iphdr));    memcpy(p, &iphdr, sizeof(struct iphdr));    ip = (struct iphdr*)p;    ip->tot_len = htons(sizeof(data) + sizeof(struct iphdr));    skb_reset_network_header(skb);    /* 构造以太头 */    p = skb_push(skb, sizeof(struct ethhdr));    eth = (struct ethhdr*)p;    eth->h_proto = htons(ETH_P_IP);    memcpy(p, hdr, sizeof(struct ethhdr));    skb_reset_mac_header(skb);    /* 发射 */    ret = dev_queue_xmit(skb);    if (ret < 0) {        /* 由于panic了,不再处理内存,不处理异常流 */        //kfree_skb (skb);        //dev_put(dev);        goto out;    }out:    return ret;}static struct notifier_block on_panic_send = {        .notifier_call = send_last_msg,};static int __initpanic_sendmsg_init(void){    int ret = -1;    dev = dev_get_by_name (&init_net, "eth3");    if (!dev) {        printk("Can't get device\n");        goto out;    }    /* 不在通知链中分配,因为那时已经panic,故预先分配 */    skb = alloc_skb(1500, GFP_ATOMIC);    if (!skb) {        dev_put(dev);        printk("Alloc skb failed\n");        goto out;    }    /* 注册 */    ret = atomic_notifier_chain_register(&panic_notifier_list, &on_panic_send);    if (ret) {        dev_put(dev);        kfree_skb (skb);        printk("Register notifier chain failed\n");        goto out;    }    return 0;out:    return ret;}static void __exitpanic_sendmsg_exit(void){    atomic_notifier_chain_unregister(&panic_notifier_list, &on_panic_send);    if (dev) {        dev_put(dev);    }    if (skb) {        kfree_skb (skb);    }}module_init(panic_sendmsg_init);module_exit(panic_sendmsg_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("marywangran ");


需要指出的 是kernel_log_buffer接口,这个接口是我自己添加进内核并EXPORT出来的。我认为这是必要的,printk将信息放在了内核的一个缓 冲区中,这个缓冲区可以被用户态进程通过syslog系统调用读取。我们知道,常规的办法是内核信息被syslog守护进程读取,然后发送到网络或者保存 成文件。但是panic之后呢?既然用户态已经完全不再运行,那么就不存在通过什么守护进程存日志的希望了,当然在内核panic通知链钩子中直接操作磁 盘日志文件是可以的,但是那太不可靠,因为磁盘的IO逻辑同样存在于内核态代码或者驱动代码中,此时内核已经panic了...虽然从网络发送数据也同样 要用到已经panic的内核执行流,但是毕竟协议栈操作要比磁盘操作的调用深度要浅很多(数据的最终解释,排序,保存操作都在对端进行,磁盘IO则不 然)。

不管怎么样,不管是磁盘操作还是网络操作,都需要将内核log缓冲区中的内存dump下来才好,然而内核log缓冲区本身并没有被EXPORT,它只能通过不多的几个系统调用接口来获取,而这些接口很难在内核里面调用,因此我自己添加了一个:


int kernel_log_buffer(char *buf, int max_len);


其中,buf为内核log保存到的缓冲区,max_len为buf的长度,返回值为实际返回信息的长度。这个接口完全类似于read系统调用。


内核 系统 代码 信息 内存 接口 数据 缓冲区 缓冲 方式 磁盘 网络 进程 处理 广播 就是 日志 用户 方案 实际 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网中国科技 互联网科技未来 喔噻互联网科技 奇酷互联网络科技 腾讯科技 互联网女皇 新浪科技互联网 互联网科技股午盘股票涨跌 玖二柒互联网科技公司待遇 重庆威客帮互联网科技 互联网科技和法律行业 互联网数据科技专业 互联网科技公司营业执照怎么办理 互联网科技实习生岗位 广州百诚互联网科技有限公司官网 互联网风格科技公司名称 中国互联网公司的科技投入 互联网科技职业技能培训学校 数字互联网科技有限公司 盐城市攀科互联网科技有限公司 互联网科技创业有什么好项目 期待互联网科技 字节跳动和互联网科技 厦门千工互联网科技有限公司 苏州铭阳互联网科技有限公司 米聚互联网科技南京有限公司排面 伍佰万互联网科技怎么赚钱 山东大美朱楼互联网科技有限公司 昆明中奇联合互联网科技 互联网科技手抄报图片 互联网高科技绘画
0