系统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 <linux/module.h>
#include <linux/skbuff.h>
#include <net/ip.h>

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, ehdr, 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 __init
panic_sendmsg_init(void)
{
    int ret = -1;
    dev = dev_get_by_name (&init_net, "eth2");
    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 __exit
panic_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 <[email protected]>");

需要指出的是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系统调用。

时间: 2024-10-19 13:37:38

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

远程触发SYSRQ获取最新的dmesg信息-一个几乎没有什么用的方案

本文在前半部分叙述一个听起来十分吸引人且合理的故事,然后紧接着告诉你这个美好的故事事实上几乎不会发生,最后来个总结.在接下来的一篇文章中,我提出一个比较自我的方案. 第一部分:美好的故事 在xtables-addons中,有一个特别有意思的小模块,那就是xt_SYSRQ,它作为一个iptables的target加载进内核,可以在远程为本机发送sysrq命令,这个功能可谓强大.在去年的项目中中,我已经将其部署到了实际的产品中,然而今日再看,发现还是有些美中不足,确实需要改进: 1.原版的xt_SY

C# Datetime 得到系统时间后, 增加或者减少几秒得到一个时间值

DateTime d = DateTime.Now.AddSeconds(100); // 添加100秒: DateTime d = DateTime.Now.AddSeconds(-100); // 减少100秒. https://www.cnblogs.com/dekevin/p/4551843.html https://blog.csdn.net/zj735539703/article/details/45845697 原文地址:https://www.cnblogs.com/wsq-bl

realarm Android系统编译后内核无法启动的解决方法

由于之前版本使用的内核并非uImage格式,而在编译时使用的是非uImage格式编译,所以照成无法启动. 解决方法是,在编译内核时使用make uImage方式编译. 修改根目录下的build_realv210.sh文件,如下图所示 另外注意上图中CPU_JOB_NUM这个参数,要根据自己的电脑配置来选择,该参数在该文件的起始处设置,可以设置成电脑CPU核心数的2倍,例如:如果核心数为2,那么设置成4即可. 完整脚本下载地址:http://download.csdn.net/detail/u01

tiny4412学习(一)之从零搭建linux系统(烧写uboot、内核进emmc+uboot启动内核)【转】

本文转载自:http://blog.csdn.net/fengyuwuzu0519/article/details/74080109 版权声明:本文为博主原创文章,转载请注明http://blog.csdn.net/fengyuwuzu0519. 目录(?)[+] 硬件平台:tiny4412 系统:linux-3.5-20151029 文件系统:busybox-1.22.1.tar.bz2 编译器: arm-linux-gcc-4.5.1 目的: 使用uboot引导Linux系统,并挂接根文件系

《Linux操作系统分析》之Linux系统的理解及学习Linux内核的心得

经过这一段时间的学习,自己对linux也有了一定的认识,今天这篇博客对以往的知识进行一个总结吧. 以往linux学习的博客,从上而下是学习深入的过程,我的博客链接如下: 第一篇:<Linux操作系统分析>之分析计算机如何启动以及如何工作运行 第二篇:<Linux操作系统分析>之分析精简的Linux的内核中断和时间片轮询 第三篇:<Linux操作系统分析>之跟踪分析Linux内核的启动过程 第四篇:<Linux操作系统分析>之使用库函数API和C代码中嵌入汇编

Linux系统的快速启动机制(内核切换) 【转】

转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4187846 原文地址:Linux系统的快速启动机制(内核切换) 作者:Renwen0524 快速启动机制:允许通过已经运行的Linux内核的上下文启动另一个Linux内核,不需要经过BIOS.BIOS可能会消耗很多时间,特别是带有众多数量的外设的大型服务器.这种办法可以为经常启动机器的开发者节省很多时间. 1.使用该机制要满足两个基本条件1)内核

Android3.1后Boot_COMPLETED广播不响应的问题

在做Android开发时,很多应用由于各种目的,希望在机器启动时被唤醒,一般的做法是写一个BroadcastReceiver,接收对应的boot action,当然别忘了在Manifest中添加permission "android.permission.RECEIVE_BOOT_COMPLETED".但是最近在做4.0开发时,有同事声称这个广播接收不到了,同时其他有人又说自己的能接收到,到底是怎么回事呢. 原来,在3.1之后,系统的package manager增加了对处于"

AIX系统中使用bsdlog函数输出内核信息

AIX系统中使用bsdlog函数输出内核信息 (1)修改syslogd进程的配置文件/etc/syslog.conf,使用echo命令在文件末尾添加内容“kern.info /tmp/kerninfo.log” echo “kern.info /tmp/kerninfo.log” >> /etc/syslog.conf (2)使用touch命令创建kerninfo.log文件,用此文件作为kernel info级别消息的输出文件 touch /tmp/kerninfo.log (3)重启sys

Android4.0系统接收不到广播的问题解析

在3.1之后,系统的package manager增加了对处于“stopped state”应用的管理,这个stopped和Activity生命周期中的stop状态是完全两码事,指的是安装后从来没有启动过和被用户手动强制停止的应用,与此同时系统增加了2个Flag:FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES ,来标识一个intent是否激活处于“stopped state”的应用.当2个Flag都不设置或者都进行设置的时候