免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 9776 | 回复: 13
打印 上一主题 下一主题

Netconsole实例源代码分析 [复制链接]

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-11-26 20:56 |只看该作者 |倒序浏览
Netconsole实例源代码分析
By dremice
dremice.jiang@gmail.com
目录

1.前言        - 2 -
2.实例配置        - 2 -
2.1 监控主机的配置        - 2 -
2.2 被监控主机的配置        - 2 -
2.3 实例测试        - 3 -
3.Netconsole内核源码分析        - 4 -
3.1 几个重要的数据结构        - 4 -
3.1.1 struct console        - 4 -
3.1.2 struct netpoll        - 5 -
3.1.3 struct net_device        - 5 -
3.1.4 struct netpoll_info        - 6 -
3.2 实现分析        - 6 -
3.2.1 Netconsole初始化        - 7 -
3.2.2 具体运行实现        - 11 -










1.前言
Netconsole是Linux2.6版内核的一个新的特性。它允许将本机的dmesg系统信息,通过网络的方式传送到另一台主机上。这样,就可以实现远程监控某台机子的kernel panic信息了。使用起来非常方便,也给开发人员调试内核提供了更加便捷的途径。

2.实例配置
由于2.6版本的内核本身已经支持Netconsole,并以模块形式编译进了内核。所以,下面的介绍都是基于2.6版内核,且本身在内核编译的时候,已经选择了将Netconsole以模块进行编译的方式。如果内核本身并没有编译Netconsole这个模块,那就需要重新编译内核了(笔者暂时没有找到其他的解决方案)。
2.1 监控主机的配置
监控主机有两种配置方式:使用本身的syslogd和利用netcat这个工具。Syslogd使用了514这个特定的UDP端口,而netcat工具可以指定任意未被使用的UDP端口进行监视。我这里使用的是netcat的方式进行监视的。安装好netcat后,执行shell命令:
# netcat -l -p 30000 –u
这里使用了端口30000来进行接收,这个端口是任意制定的,只要不发生冲突即可。
2.2 被监控主机的配置
在需要被监控的主机上加载运行netconsole模块,由于netconsole模块需要许多其他模块的依赖,以及在加载时必须配置相关的端口,IP地址等信息,所以shell执行命令如下:
#modprobe netconsole netconsole=6666@10.14.0.225/eth7,30000@10.14.0.220/00:0C:29:E2:87:E8
我们来分析一下这段加载命令。
Modprobe 这个模块加载命令是指在加载该模块时,同时加载其依赖的其它模块;
第一个@前的6666是本地端口,这个我们可以在源码中看到,是一个源代码中默认的本地端口;
10.14.0.225/eth7:这里指配本机的IP地址及网卡名称(通常我们可以用ifconfig –a来查看到);
30000@10.14.0.220/00:0C:29:E2:87:E8:三个位段分别是,远端监控端口,远端IP地址及远端机子的MAC地址。也就是我们在2.1节中指定的监控端口30000,以及其IP地址和MAC地址了。
2.3 实例测试
以上配置就算完成了,让我来看一个实例吧。在被监控的主机上运行一个简单的hello world模块,看看我们的printk信息是怎样被发送到监控主机上去的。
模块helloworld源码:
hello.c
  1 /*hello.c*/
  2 #include <linux/kernel.h>
  3 #include <linux/init.h>
  4 #include <linux/module.h>
  5 MODULE_LICENSE("Dual BSD/GPL");
  6
  7 static int hello_init(void)
  8 {
  9         printk(KERN_ALERT "Hello World\n" );
10         return 0;
11 }
12 static void hello_exit(void)
13 {
14         printk(KERN_ALERT "Goodbye World\n" );
15 }
16
17 module_init(hello_init);
18 module_exit(hello_exit);

Makefile:
  1 obj-m:=hello.o
  2 KDIR:=/lib/modules/$(shell uname -r)/build
  3 PWD:=$(shell pwd)
  4
  5 default:
  6         $(MAKE) -C $(KDIR) M=$(PWD) modules
  7
  8 clean:
  9         $(RM) *.o *.mod.c *.ko *.symvers

执行:
# make
# insmod hello.ko
在监控机的终端上,我们看到以下信息:
netconsole: network logging started
Hello World
第一条是我们先前加载netconsole模块时收到的,第二条是我们加载helloworld模块时收到的。
再运行rmmod hello
监控机终端上将看到:
netconsole: network logging started
Hello World
Goodbye World

OK,以上测试顺利完成,当然,我们也可以测试kernel panic的情况,例如操作一个内核的非法地址等。下面,我们开始来分析内核源码的netconsole的实现。

[ 本帖最后由 dreamice 于 2008-11-26 21:00 编辑 ]

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
2 [报告]
发表于 2008-11-26 20:57 |只看该作者
3.Netconsole内核源码分析
3.1 几个重要的数据结构
3.1.1 struct console
Include/linux/console.h
/*
* The interface for a console, or any other device that wants to capture
* console messages (printer driver?)
*
* If a console driver is marked CON_BOOT then it will be auto-unregistered
* when the first real console is registered.  This is for early-printk drivers.
*/

#define CON_PRINTBUFFER        (1)
#define CON_CONSDEV        (2) /* Last on the command line */
#define CON_ENABLED        (4)
#define CON_BOOT        (8)
#define CON_ANYTIME        (16) /* Safe to call when cpu is offline */

struct console
{
        char        name[8];
        void        (*write)(struct console *, const char *, unsigned);
        int        (*read)(struct console *, char *, unsigned);
        struct tty_driver *(*device)(struct console *, int *);
        void        (*unblank)(void);
        int        (*setup)(struct console *, char *);
        short        flags;
        short        index;
        int        cflag;
        void        *data;
        struct         console *next;
};
从上面的注释,我们看到,这个结构,是提供给那些需要捕捉console终端信息的设备的。我们捕获的dmesg的信息,实际上是用prink输出的,因此,我们必须构造这么一个结构,并实现其信息捕捉函数,即write函数。
3.1.2 struct netpoll
Include/linux/netpoll.h
struct netpoll {
        struct net_device *dev; //指向实际的网卡
        char dev_name[16], *name; //名字
        void (*rx_hook)(struct netpoll *, int, char *, int); //钩子函数,这里并没有用到
        void (*drop)(struct sk_buff *skb); //信息发送函数
        u32 local_ip, remote_ip; //本地ip地址和远端ip地址
        u16 local_port, remote_port;//本地端口和远端端口
        unsigned char local_mac[6], remote_mac[6];//本地mac地址和远端mac地址
};
这是netconsole实现的一个关键数据结构。net_device成员指向了实际的网卡,我们正是通过网络进行监视获取dmesg信息,因此就必须通过实际的网卡来发送数据。实际上,这个数据结构包含了一个网络包发送的所有关键字段(除了有效数据)。
3.1.3 struct net_device
Include/linux/netdevice.h
struct net_device
{

        /*
         * This is the first field of the "visible" part of this structure
         * (i.e. as seen by users in the "Space.c" file).  It is the name
         * the interface.
         */
        char                        name[IFNAMSIZ];
        /* device name hash chain */
        struct hlist_node        name_hlist;
……
#ifdef CONFIG_NETPOLL
        struct netpoll_info        *npinfo;
#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
        void                    (*poll_controller)(struct net_device *dev);
#endif

        /* bridge stuff */
        struct net_bridge_port        *br_port;

#ifdef CONFIG_NET_DIVERT
        /* this will get initialized at each interface type init routine */
        struct divert_blk        *divert;
#endif /* CONFIG_NET_DIVERT */

        /* class/net/name entry */
        struct class_device        class_dev;
        /* space for optional statistics and wireless sysfs groups */
        struct attribute_group  *sysfs_groups[3];
};
net_device结构包含了一个网络设备相关的所有信息,这里并没有全部列出其成员,其中用粉红色标记出来的,正是和netpoll相关的结构。从这里我们可以看到,如果要支持netconsole,那么就必须编译netpoll,而编译netpoll,就必须配置CONFIG_NETPOLL和CONFIG_NET_POLL_CONTROLLER这两个选项。
结构中,npinfo包含了与netpoll相关的一些重要信息。
3.1.4 struct netpoll_info
Include/linux/netpoll.h
struct netpoll_info {
        spinlock_t poll_lock; //spinlock防止并发访问
        int poll_owner; //所有者
        int tries;//如果发送失败,指定了发送信息的次数
        int rx_flags;
        spinlock_t rx_lock;
        struct netpoll *rx_np; /* netpoll that registered an rx_hook */
        struct sk_buff_head arp_tx; /* list of arp requests to reply to */
};
在后面的实现函数分析中,将看到这个结构的详细用处。
3.2 实现分析
Drivers/net/netconsole.h

static char config[256]; //定义模块加载时指定参数选项的buf
module_param_string(netconsole, config, 256, 0); //定义模块参数

//模块的描述,说明了模块加载时,指定参数及选项的格式
MODULE_PARM_DESC(netconsole, " netconsole=[src-port]@[src-ip]/[dev],[tgt-port]@<tgt-ip>/[tgt-macaddr]\n");

3.2.1 Netconsole初始化
前面提到,如果要对console的信息进行捕获,必须要实现一个struct console。因此,我们首先来看一看如何注册一个console结构。(看源码的时候,很多地方我使用的是英文注释)
/* netconsole module initialization */
static int init_netconsole(void)
{
        /* The config is the module parameter, which contains the capture options */
    //这个config就是上面的模块参数,在加载模块的时候它已经保存了必要的配置信息
        if(strlen(config))
                option_setup(config); //解析模块参数

        if(!configured) { //用户加载时,参数不对,配置失败
                printk("netconsole: not configured, aborting\n");
                return 0;
        }

        /* Setup the netpoll capture operations */
        if(netpoll_setup(&np)) //初始化建立netpoll的配置,np是一个struct netpoll结构全局变量,//指定了netpoll的相关信息
                return -EINVAL;

        /* register netconsole that can get the printk info */
        register_console(&netconsole); //注册netconsole
        printk(KERN_INFO "netconsole: network logging started\n");
        return 0;
}

我们首先看一看结构netconsole的定义,去解析对终端信息捕捉的具体操作:
/* Initialize the console capture... */
static struct console netconsole = {
        .name = "netcon",
        .flags = CON_ENABLED | CON_PRINTBUFFER,
        .write = write_msg //这就是对终端信息的捕捉函数
};
来看一下option_setup:
static int configured = 0;
static int option_setup(char *opt)
{
        /* parse the console capture options */
        configured = !netpoll_parse_options(&np, opt);
        return 1;
}
netpoll_parse_options()实现对模块参数的解析,并提取信息,保存到np结构中。这里不再详细解析该函数的实现,有兴趣可以阅读源代码。

下面分析netpoll_setup()函数的实现,在分析之前,先看一下np结构这个全局变量的定义:
static struct netpoll np = {
        .name = "netconsole", //名字定义
        .dev_name = "eth0", //默认的网络接口卡名称
        .local_port = 6665, //默认的本地端口定义
        .remote_port = 6666, //默认的远程监控端口定义
        .remote_mac = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, //默认的远程监控机MAC地址,实际上是一个广播地址
        .drop = netpoll_queue, //定义了netpoll在处理console信息的发送函数
};

int netpoll_setup(struct netpoll *np)
{
        struct net_device *ndev = NULL;
        struct in_device *in_dev;
        struct netpoll_info *npinfo;
        unsigned long flags;
   
    //检查是否指定了网络接口卡,如果指定了,并查找系统中是否存在这样一块网卡
        if (np->dev_name)
                /* find the network interface card */
                ndev = dev_get_by_name(np->dev_name);
        if (!ndev) {
                printk(KERN_ERR "%s: %s doesn't exist, aborting.\n",
                       np->name, np->dev_name);
                return -1;
        }

        np->dev = ndev; //为np结构指定网络接口卡
        if (!ndev->npinfo) { //这里就是准备初始化前面说到的net_device结构这个重要的npinfo成员了
                npinfo = kmalloc(sizeof(*npinfo), GFP_KERNEL);
                if (!npinfo)
                        goto release;

                npinfo->rx_flags = 0;
                npinfo->rx_np = NULL; //不指定hook函数
                spin_lock_init(&npinfo->poll_lock); //初始化自旋锁
                npinfo->poll_owner = -1; //暂时无引用
                npinfo->tries = MAX_RETRIES; //设定如果发送失败的话,重复的最大次数
                spin_lock_init(&npinfo->rx_lock);
                skb_queue_head_init(&npinfo->arp_tx); //和arp相关的
        } else
                npinfo = ndev->npinfo;

        if (!ndev->poll_controller) { //检查netdev是否设定了poll_controller,否则将不能支持netconsole。它保证了在不使能中断的情况下,就可以发送skb,而且它并不是在执行终端例程的情况下被执行的。
                printk(KERN_ERR "%s: %s doesn't support polling, aborting.\n",
                       np->name, np->dev_name);
                goto release;
        }

        if (!netif_running(ndev)) { //检查网卡是否启动
                unsigned long atmost, atleast;

                printk(KERN_INFO "%s: device %s not up yet, forcing it\n",
                       np->name, np->dev_name);

                rtnl_lock();
                if (dev_change_flags(ndev, ndev->flags | IFF_UP) < 0) {
                        printk(KERN_ERR "%s: failed to open %s\n",
                               np->name, np->dev_name);
                        rtnl_unlock();
                        goto release;
                }
                rtnl_unlock();

                atleast = jiffies + HZ/10;
                atmost = jiffies + 4*HZ;
                while (!netif_carrier_ok(ndev)) {
                        if (time_after(jiffies, atmost)) {
                                printk(KERN_NOTICE
                                       "%s: timeout waiting for carrier\n",
                                       np->name);
                                break;
                        }
                        cond_resched();
                }

                /* If carrier appears to come up instantly, we don't
                 * trust it and pause so that we don't pump all our
                 * queued console messages into the bitbucket.
                 */

                if (time_before(jiffies, atleast)) {
                        printk(KERN_NOTICE "%s: carrier detect appears"
                               " untrustworthy, waiting 4 seconds\n",
                               np->name);
                        msleep(4000);
                }
        }

        /* initialize the local mac, that why we need not set the local mac for the options */
   //初始化本地mac地址,在加载模块的时候,我们只是指定了监控机的mac地址,所以这里要进行本地mac地址的初始化。
        if (is_zero_ether_addr(np->local_mac) && ndev->dev_addr)
                memcpy(np->local_mac, ndev->dev_addr, 6);

        if (!np->local_ip) {//检查是否指定了本地ip,如果没有的话,主动去获取
                rcu_read_lock();
                in_dev = __in_dev_get_rcu(ndev);

                if (!in_dev || !in_dev->ifa_list) {
                        rcu_read_unlock();
                        printk(KERN_ERR "%s: no IP address for %s, aborting\n",
                               np->name, np->dev_name);
                        goto release;
                }

                np->local_ip = ntohl(in_dev->ifa_list->ifa_local);
                rcu_read_unlock();
                printk(KERN_INFO "%s: local IP %d.%d.%d.%d\n",
                       np->name, HIPQUAD(np->local_ip));
        }

        if (np->rx_hook) {
                spin_lock_irqsave(&npinfo->rx_lock, flags);
                npinfo->rx_flags |= NETPOLL_RX_ENABLED;
                npinfo->rx_np = np;
                spin_unlock_irqrestore(&npinfo->rx_lock, flags);
        }

        /* fill up the skb queue */
        refill_skbs();

        /* last thing to do is link it to the net device structure */
        ndev->npinfo = npinfo;

        /* avoid racing with NAPI reading npinfo */
        synchronize_rcu();

        return 0;

release:
        if (!ndev->npinfo)
                kfree(npinfo);
        np->dev = NULL;
        dev_put(ndev);
        return -1;
}
以上,初始化完成。
3.2.2 具体运行实现
在上一节最开始就提到了netconsole这个结构的定义,它里面的一个重要的write函数的初始化,正是这个函数实现了对终端信息的捕捉。下面,我们就来分析一下write_msg()的具体实现:
#define MAX_PRINT_CHUNK 1000

static void write_msg(struct console *con, const char *msg, unsigned int len)
{
        int frag, left;
        unsigned long flags;

        if (!np.dev)
                return;

        local_irq_save(flags);
   
    //处理msg的发送,由于发送的包大小不能操作长度MAX_PRINT_CHUNK,所以可能分多次发送这些信息
        for(left = len; left; ) {
                /* the transport  msg should NOT larger than MAX_PRINT_CHUNK*/
                frag = min(left, MAX_PRINT_CHUNK);
                netpoll_send_udp(&np, msg, frag); /* send the msg */
                msg += frag;
                left -= frag;
        }

        local_irq_restore(flags);
}

我们看到,真正发送信息,实际上调用的是netpoll_send_udp()函数:
void netpoll_send_udp(struct netpoll *np, const char *msg, int len)
{
        int total_len, eth_len, ip_len, udp_len;
        struct sk_buff *skb;
        struct udphdr *udph;
        struct iphdr *iph;
        struct ethhdr *eth;

        udp_len = len + sizeof(*udph);
        ip_len = eth_len = udp_len + sizeof(*iph);
        total_len = eth_len + ETH_HLEN + NET_IP_ALIGN;

        skb = find_skb(np, total_len, total_len - len);
        if (!skb)
                return;

        memcpy(skb->data, msg, len);
        skb->len += len;

        udph = (struct udphdr *) skb_push(skb, sizeof(*udph));
        udph->source = htons(np->local_port);
        udph->dest = htons(np->remote_port);
        udph->len = htons(udp_len);
        udph->check = 0;

        iph = (struct iphdr *)skb_push(skb, sizeof(*iph));

        /* iph->version = 4; iph->ihl = 5; */
        put_unaligned(0x45, (unsigned char *)iph);
        iph->tos      = 0;
        put_unaligned(htons(ip_len), &(iph->tot_len));
        iph->id       = 0;
        iph->frag_off = 0;
        iph->ttl      = 64;
        iph->protocol = IPPROTO_UDP;
        iph->check    = 0;
        put_unaligned(htonl(np->local_ip), &(iph->saddr));
        put_unaligned(htonl(np->remote_ip), &(iph->daddr));
        iph->check    = ip_fast_csum((unsigned char *)iph, iph->ihl);

        eth = (struct ethhdr *) skb_push(skb, ETH_HLEN);

        eth->h_proto = htons(ETH_P_IP);
        memcpy(eth->h_source, np->local_mac, 6);
        memcpy(eth->h_dest, np->remote_mac, 6);

        skb->dev = np->dev;

        netpoll_send_skb(np, skb);
}
这个函数看起来并不复杂,主要是对一些控制信息的设置,最后调用了netpoll_send_skp()来发送数据。接下来看看netpoll_send_skp()是怎样实现的:
static void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb)
{
        int status;
        struct netpoll_info *npinfo;

    //相关检查
        if (!np || !np->dev || !netif_running(np->dev)) {
                __kfree_skb(skb);
                return;
        }

        npinfo = np->dev->npinfo;

        /* avoid recursion */
    //检查是不是本地cpu在处理发送信息
        if (npinfo->poll_owner == smp_processor_id() ||
            np->dev->xmit_lock_owner == smp_processor_id()) {
            /* for our own cpu */
        //本地cpu的情况,检查netpoll结构是否指定了drop函数,回到3.2.1节的初始化,我们为该结构指定的drop函数是netpoll_queue(在后面分析该函数的实现)
                if (np->drop) /* invoke the netpoll_queue of np to send the msg, the work_queue */
                        np->drop(skb);
                else
                        __kfree_skb(skb);
                return;
        }

    //非本地cpu以及drop函数并没有实现的情况
        do {
                /* the times of try to send the msg if it fails */
                npinfo->tries--;
                netif_tx_lock(np->dev);

                /*
                 * network drivers do not expect to be called if the queue is
                 * stopped.
                 */
                status = NETDEV_TX_BUSY;
                if (!netif_queue_stopped(np->dev))
                        /* send the package directly */
            //直接调用了底层的skb数据包发送函数
                        status = np->dev->hard_start_xmit(skb, np->dev);

                netif_tx_unlock(np->dev);

                /* success */
                if(!status) {
                        npinfo->tries = MAX_RETRIES; /* reset */
                        return;
                }

                /* transmit busy */
        //发送繁忙的情况,进行poll操作,并尝试重新发包
                netpoll_poll(np);
                udelay(50);
        } while (npinfo->tries > 0);
}

最后,剩下最重要的netpoll_queue()函数的实现分析了。它的实现具有一定的神奇之处,慢慢来剖析吧。
void netpoll_queue(struct sk_buff *skb)
{
        unsigned long flags;

        if (queue_depth == MAX_QUEUE_DEPTH) {
                __kfree_skb(skb);
                return;
        }

        spin_lock_irqsave(&queue_lock, flags);
        if (!queue_head)
                queue_head = skb;
        else
                queue_tail->next = skb;
        queue_tail = skb;
        queue_depth++;
        spin_unlock_irqrestore(&queue_lock, flags);

        schedule_work(&send_queue);
}
从代码已开始,就看到了工作队列,先不说这个函数的实现,补习一下工作队列的相关知识。工作队列是和tasklet一类的东西,但本质上存在极大的差异。表现在:
1、        工作队列可以睡眠;
2、        工作队列可以运行在多cpu上(默认是同一处理器上);
3、        工作队类不必以原子化执行,它还可以延迟执行;
相比于tasklet,工作队列在实时性上就显得不足了。Tasklet可以在很短的时间内很快执行,并且以原子模式执行。关于工作队列更详细的描述,请参考LDD3(p204—206)。
回到这个程序,我们在看一下在这个函数之前的一些声明和定义:
static void queue_process(void *p)
{
        unsigned long flags;
        struct sk_buff *skb;

        while (queue_head) {
                spin_lock_irqsave(&queue_lock, flags);

                skb = queue_head;
                queue_head = skb->next;
                if (skb == queue_tail)
                        queue_head = NULL;

                queue_depth--;

                spin_unlock_irqrestore(&queue_lock, flags);

                dev_queue_xmit(skb);
        }
}

static DECLARE_WORK(send_queue, queue_process, NULL);
我们看到,在这里定义了work queue(send_queue),并指定了调用函数queue_process,没有传递参数。
可以看到,queue_process()函数,最终调用函数dev_queue_xmit实现了数据包的发送。
重新回到函数netpoll_queue(),它实际上只是做了一些wrok queue深度的检查,并最终调用函数schedule_work(&send_queue);实现对工作队列的调度,也就是最终能够调度到queue_process()来完成数据包的发送任务。
4.总结
Netconsole提供了一种通过网络监控调试信息的便捷的方法,配置也十分简单。其源代码实现主要也精炼,由于使用了工作队列的机制,它可以安全的工作在中断上下文中。
但是,netconsole的使用仍有一些限制,正如kernel中netconsole.txt文档说的:“only IP networks, UDP packets and ethernet devices are supported”,它只能工作在IP网络中,并且使用不可靠的UDP连接,目前只支持以太网络设备。
期待在新的内核版本中,netconsole会有新的发展。

评分

参与人数 1可用积分 +30 收起 理由
T-Bagwell + 30 精品文章

查看全部评分

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
3 [报告]
发表于 2008-11-26 21:02 |只看该作者
第一次分析源码,拿出来分享一下,请各位多多指点,呵呵。
ps:本来想发到驱动版,感觉这个更适合发到内核版
下面是本文的pdf:

netconsole 源码分析.pdf

209.59 KB, 下载次数: 311

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
4 [报告]
发表于 2008-11-26 21:27 |只看该作者
好东西,顶一下啊。随后详细学习啊

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
5 [报告]
发表于 2008-11-26 21:29 |只看该作者
原帖由 Godbach 于 2008-11-26 21:27 发表
好东西,顶一下啊。随后详细学习啊


这东西,用起来很方便。尤其是对那种远程调试的。好东西,与大家一起分享

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
6 [报告]
发表于 2008-12-01 13:01 |只看该作者
有时间仔细拜读一下。

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
7 [报告]
发表于 2010-08-30 16:23 |只看该作者
今天要用一下netconsole。再次拜读一下dreamice兄的大作。

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
8 [报告]
发表于 2010-08-30 16:24 |只看该作者
补充一下:
需要监控主机端的syslog作如下配置
Red Hat / CentOS / Fedora Linux Configuration
If you are using Red Hat / CentOS / Fedora Linux open /etc/sysconfig/syslog file and set SYSLOGD_OPTIONS option for udp logging.
# vi /etc/sysconfig/syslog
Configure syslogd option as follows:
SYSLOGD_OPTIONS="-m 0 -r -x"
Save and close the file. Restart syslogd, enter:
# service syslog restart


就是要开启监控主机的syslog可以接收远程的消息

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
9 [报告]
发表于 2010-08-30 21:23 |只看该作者
请教一下dreamice兄,如果被监控主机和监控主机中间有路由设备的话,那么加载模块是的MAC地址用的还是监控主机的MAC吗,还是被路由修改后的

论坛徽章:
0
10 [报告]
发表于 2010-08-31 06:45 |只看该作者
本帖最后由 lmarsin 于 2010-08-31 17:23 编辑

借人气
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP