linux netlink通信机制

一、什么是Netlink通信机制

   Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口。

Netlink 是一种特殊的 socket,它是 Linux 所特有的,类似于 BSD 中的AF_ROUTE 但又远比它的功能强大,目前在Linux 内核中
    使用netlink 进行应用与内核通信的应用很多; 包括:路由 daemon(NETLINK_ROUTE),用户态 socket 协议(NETLINK_USERSOCK),
    防火墙(NETLINK_FIREWALL),netfilter 子系统(NETLINK_NETFILTER),内核事件向用户态通知(NETLINK_KOBJECT_UEVENT),
    通用 netlink(NETLINK_GENERIC)等。
    
    Netlink 是一种在内核与用户应用间进行双向数据传输的非常好的方式,用户态应用使用标准的 socket API 就可以使用 netlink 提供的强大功能,
    内核态需要使用专门的内核 API 来使用 netlink。
    Netlink 相对于系统调用ioctl 以及 /proc文件系统而言具有以下优点:
    1,netlink使用简单,只需要在include/linux/netlink.h中增加一个新类型的 netlink 协议定义即可,(如 #define NETLINK_TEST 20 然后,内核和用户态应用就可以立即通过 socket API 使用该 netlink 协议类型进行数据交换);
    2. netlink是一种异步通信机制,在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接收者的socket的接收队列,而不需要等待接收者收到消息;
    3.使用 netlink 的内核部分可以采用模块的方式实现,使用 netlink 的应用部分和内核部分没有编译时依赖;
    4.netlink 支持多播,内核模块或应用可以把消息多播给一个netlink组,属于该neilink 组的任何内核模块或应用都能接收到该消息,内核事件向用户态的通知机制就使用了这一特性;
    5.内核可以使用 netlink 首先发起会话;

二、Netlink常用数据结构及函数

 用户态应用使用标准的 socket API有(sendto()),recvfrom(); sendmsg(), recvmsg())

 下面简单介绍几种NETLINK用户态通信的常用数据结构

 1、用户态数据结构

Netlink通信跟常用UDP Socket通信类似:
 struct sockaddr_nl 是netlink通信地址跟普通socket struct sockaddr_in类似
  struct sockaddr_nl结构: 

1 struct sockaddr_nl {
2     __kernel_sa_family_t    nl_family;  /* AF_NETLINK (跟AF_INET对应)*/
3     unsigned short  nl_pad;     /* zero */
4     __u32       nl_pid;     /* port ID  (通信端口号)*/
5     __u32       nl_groups;  /* multicast groups mask */
6 };

struct nlmsghd 结构:

1 /* struct nlmsghd 是netlink消息头*/
2 struct nlmsghdr {
3     __u32       nlmsg_len;  /* Length of message including header */
4     __u16       nlmsg_type; /* Message content */
5     __u16       nlmsg_flags;    /* Additional flags */
6     __u32       nlmsg_seq;  /* Sequence number */
7     __u32       nlmsg_pid;  /* Sending process port ID */
8 };

(1)nlmsg_len:整个netlink消息的长度(包含消息头);
(2)nlmsg_type:消息状态,内核在include/uapi/linux/netlink.h中定义了以下4种通用的消息类型,它们分别是:

 1 #define NLMSG_NOOP      0x1 /* Nothing.     */
 2 #define NLMSG_ERROR     0x2 /* Error        */
 3 #define NLMSG_DONE      0x3 /* End of a dump    */
 4 #define NLMSG_OVERRUN       0x4 /* Data lost        */
 5
 6 #define NLMSG_MIN_TYPE      0x10    /* < 0x10: reserved control messages */
 7
 8 /*NLMSG_NOOP:不执行任何动作,必须将该消息丢弃;
 9 NLMSG_ERROR:消息发生错误;
10 NLMSG_DONE:标识分组消息的末尾;
11 NLMSG_OVERRUN:缓冲区溢出,表示某些消息已经丢失。
12 NLMSG_MIN_TYPEK:预留 */

(3)nlmsg_flags:消息标记,它们用以表示消息的类型,如下

 1 /* Flags values */
 2
 3 #define NLM_F_REQUEST       1   /* It is request message.   */
 4 #define NLM_F_MULTI     2   /* Multipart message, terminated by NLMSG_DONE */
 5 #define NLM_F_ACK       4   /* Reply with ack, with zero or error code */
 6 #define NLM_F_ECHO      8   /* Echo this request        */
 7 #define NLM_F_DUMP_INTR     16  /* Dump was inconsistent due to sequence change */
 8
 9 /* Modifiers to GET request */
10 #define NLM_F_ROOT  0x100   /* specify tree root    */
11 #define NLM_F_MATCH 0x200   /* return all matching  */
12 #define NLM_F_ATOMIC    0x400   /* atomic GET       */
13 #define NLM_F_DUMP  (NLM_F_ROOT|NLM_F_MATCH)
14
15 /* Modifiers to NEW request */
16 #define NLM_F_REPLACE   0x100   /* Override existing        */
17 #define NLM_F_EXCL  0x200   /* Do not touch, if it exists   */
18 #define NLM_F_CREATE    0x400   /* Create, if it does not exist */
19 #define NLM_F_APPEND    0x800   /* Add to end of list       */

(4)nlmsg_seq:消息序列号,用以将消息排队,有些类似TCP协议中的序号(不完全一样),但是netlink的这个字段是可选的,不强制使用;
(5)nlmsg_pid:发送端口的ID号,对于内核来说该值就是0,对于用户进程来说就是其socket所绑定的ID号。

struct msghdr 结构体

 1 struct iovec {                    /* Scatter/gather array items */
 2      void  *iov_base;              /* Starting address */
 3      size_t iov_len;               /* Number of bytes to transfer */
 4  };
 5   /* iov_base: iov_base指向数据包缓冲区,即参数buff,iov_len是buff的长度。msghdr中允许一次传递多个buff,
 6     以数组的形式组织在 msg_iov中,msg_iovlen就记录数组的长度 (即有多少个buff)
 7   */
 8  struct msghdr {
 9      void         *msg_name;       /* optional address */
10      socklen_t     msg_namelen;    /* size of address */
11      struct iovec *msg_iov;        /* scatter/gather array */
12      size_t        msg_iovlen;     /* # elements in msg_iov */
13      void         *msg_control;    /* ancillary data, see below */
14      size_t        msg_controllen; /* ancillary data buffer len */
15      int           msg_flags;      /* flags on received message */
16  };
17  /* msg_name: 数据的目的地址,网络包指向sockaddr_in, netlink则指向sockaddr_nl;
18     msg_namelen: msg_name 所代表的地址长度
19     msg_iov: 指向的是缓冲区数组
20     msg_iovlen: 缓冲区数组长度
21     msg_control: 辅助数据,控制信息(发送任何的控制信息)
22     msg_controllen: 辅助信息长度
23     msg_flags: 消息标识
24  */

 2. netlink 内核数据结构、常用宏及函数:

netlink消息类型:

 1 #define NETLINK_ROUTE       0   /* Routing/device hook              */
 2 #define NETLINK_UNUSED      1   /* Unused number                */
 3 #define NETLINK_USERSOCK    2   /* Reserved for user mode socket protocols  */
 4 #define NETLINK_FIREWALL    3   /* Unused number, formerly ip_queue     */
 5 #define NETLINK_SOCK_DIAG   4   /* socket monitoring                */
 6 #define NETLINK_NFLOG       5   /* netfilter/iptables ULOG */
 7 #define NETLINK_XFRM        6   /* ipsec */
 8 #define NETLINK_SELINUX     7   /* SELinux event notifications */
 9 #define NETLINK_ISCSI       8   /* Open-iSCSI */
10 #define NETLINK_AUDIT       9   /* auditing */
11 #define NETLINK_FIB_LOOKUP  10
12 #define NETLINK_CONNECTOR   11
13 #define NETLINK_NETFILTER   12  /* netfilter subsystem */
14 #define NETLINK_IP6_FW      13
15 #define NETLINK_DNRTMSG     14  /* DECnet routing messages */
16 #define NETLINK_KOBJECT_UEVENT  15  /* Kernel messages to userspace */
17 #define NETLINK_GENERIC     16
18 /* leave room for NETLINK_DM (DM Events) */
19 #define NETLINK_SCSITRANSPORT   18  /* SCSI Transports */
20 #define NETLINK_ECRYPTFS    19
21 #define NETLINK_RDMA        20
22 #define NETLINK_CRYPTO      21  /* Crypto layer */
23
24 #define NETLINK_INET_DIAG   NETLINK_SOCK_DIAG
25
26 #define MAX_LINKS 32 

netlink常用宏

 1 #define NLMSG_ALIGNTO   4U
 2 /* 宏NLMSG_ALIGN(len)用于得到不小于len且字节对齐的最小数值 */
 3 #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
 4
 5 /* Netlink 头部长度 */
 6 #define NLMSG_HDRLEN     ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
 7
 8 /* 计算消息数据len的真实消息长度(消息体 + 消息头)*/
 9 #define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)
10
11 /* 宏NLMSG_SPACE(len)返回不小于NLMSG_LENGTH(len)且字节对齐的最小数值 */
12 #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
13
14 /* 宏NLMSG_DATA(nlh)用于取得消息的数据部分的首地址,设置和读取消息数据部分时需要使用该宏 */
15 #define NLMSG_DATA(nlh)  ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
16
17 /* 宏NLMSG_NEXT(nlh,len)用于得到下一个消息的首地址, 同时len 变为剩余消息的长度 */
18 #define NLMSG_NEXT(nlh,len)  ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), 19                   (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
20
21 /* 判断消息是否 >len */
22 #define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && 23                (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && 24                (nlh)->nlmsg_len <= (len))
25
26 /* NLMSG_PAYLOAD(nlh,len) 用于返回payload的长度*/
27 #define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))

netlink 内核常用函数:

netlink_kernel_create内核函数用于创建 内核socket用用户态通信

 1 static inline struct sock *
 2 netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)
 3 /* net: net指向所在的网络命名空间, 一般默认传入的是&init_net(不需要定义);  定义在net_namespace.c(extern struct net init_net);
 4    unit:netlink协议类型
 5    cfg: cfg存放的是netlink内核配置参数(如下)
 6 */
 7
 8 /* optional Netlink kernel configuration parameters */
 9 struct netlink_kernel_cfg {
10     unsigned int    groups;
11     unsigned int    flags;
12     void        (*input)(struct sk_buff *skb); /* input 回调函数 */
13     struct mutex    *cb_mutex;
14     void        (*bind)(int group);
15     bool        (*compare)(struct net *net, struct sock *sk);
16 };

单播netlink_unicast() 和 多播netlink_broadcast()

 1 /* 来发送单播消息 */
 2 extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);
 3 /* ssk: netlink socket
 4    skb: skb buff 指针
 5    portid: 通信的端口号
 6    nonblock:表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回,而如果为0,该函数在没有接收缓存可利用 定时睡眠
 7 */
 8
 9 /* 用来发送多播消息 */
10 extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 portid,
11                  __u32 group, gfp_t allocation);
12 /* ssk: 同上(对应netlink_kernel_create 返回值)、
13    skb: 内核skb buff
14    portid: 端口id
15    group: 是所有目标多播组对应掩码的"OR"操作的合值。
16    allocation: 指定内核内存分配方式,通常GFP_ATOMIC用于中断上下文,而GFP_KERNEL用于其他场合。
17                 这个参数的存在是因为该API可能需要分配一个或多个缓冲区来对多播消息进行clone
18 */

 三、netlink实例
(1)用户态程序 (sendto(), recvfrom())

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <sys/socket.h>
 4 #include <string.h>
 5 #include <linux/netlink.h>
 6 #include <stdint.h>
 7 #include <unistd.h>
 8 #include <errno.h>
 9
10 #define NETLINK_TEST    30
11 #define MSG_LEN            125
12 #define MAX_PLOAD        125
13
14 typedef struct _user_msg_info
15 {
16     struct nlmsghdr hdr;
17     char  msg[MSG_LEN];
18 } user_msg_info;
19
20 int main(int argc, char **argv)
21 {
22     int skfd;
23     int ret;
24     user_msg_info u_info;
25     socklen_t len;
26     struct nlmsghdr *nlh = NULL;
27     struct sockaddr_nl saddr, daddr;
28     char *umsg = "hello netlink!!";
29
30     /* 创建NETLINK socket */
31     skfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);
32     if(skfd == -1)
33     {
34         perror("create socket error\n");
35         return -1;
36     }
37
38     memset(&saddr, 0, sizeof(saddr));
39     saddr.nl_family = AF_NETLINK; //AF_NETLINK
40     saddr.nl_pid = 100;  //端口号(port ID)
41     saddr.nl_groups = 0;
42     if(bind(skfd, (struct sockaddr *)&saddr, sizeof(saddr)) != 0)
43     {
44         perror("bind() error\n");
45         close(skfd);
46         return -1;
47     }
48
49     memset(&daddr, 0, sizeof(daddr));
50     daddr.nl_family = AF_NETLINK;
51     daddr.nl_pid = 0; // to kernel
52     daddr.nl_groups = 0;
53
54     nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PLOAD));
55     memset(nlh, 0, sizeof(struct nlmsghdr));
56     nlh->nlmsg_len = NLMSG_SPACE(MAX_PLOAD);
57     nlh->nlmsg_flags = 0;
58     nlh->nlmsg_type = 0;
59     nlh->nlmsg_seq = 0;
60     nlh->nlmsg_pid = saddr.nl_pid; //self port
61
62     memcpy(NLMSG_DATA(nlh), umsg, strlen(umsg));
63     ret = sendto(skfd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&daddr, sizeof(struct sockaddr_nl));
64     if(!ret)
65     {
66         perror("sendto error\n");
67         close(skfd);
68         exit(-1);
69     }
70     printf("send kernel:%s\n", umsg);
71
72     memset(&u_info, 0, sizeof(u_info));
73     len = sizeof(struct sockaddr_nl);
74     ret = recvfrom(skfd, &u_info, sizeof(user_msg_info), 0, (struct sockaddr *)&daddr, &len);
75     if(!ret)
76     {
77         perror("recv form kernel error\n");
78         close(skfd);
79         exit(-1);
80     }
81
82     printf("from kernel:%s\n", u_info.msg);
83     close(skfd);
84
85     free((void *)nlh);
86     return 0;
87 }

Netlink 内核模块代码:

  1 /****************************************
  2 * Author: zhangwj
  3 * Date: 2017-01-19
  4 * Filename: netlink_test.c
  5 * Descript: netlink of kernel
  6 * Kernel: 3.10.0-327.22.2.el7.x86_64
  7 * Warning:
  8 ******************************************/
  9
 10 #include <linux/init.h>
 11 #include <linux/module.h>
 12 #include <linux/types.h>
 13 #include <net/sock.h>
 14 #include <linux/netlink.h>
 15
 16 #define NETLINK_TEST     30
 17 #define MSG_LEN            125
 18 #define USER_PORT        100
 19
 20 MODULE_LICENSE("GPL");
 21 MODULE_AUTHOR("zhangwj");
 22 MODULE_DESCRIPTION("netlink example");
 23
 24 struct sock *nlsk = NULL;
 25 extern struct net init_net;
 26
 27 int send_usrmsg(char *pbuf, uint16_t len)
 28 {
 29     struct sk_buff *nl_skb;
 30     struct nlmsghdr *nlh;
 31
 32     int ret;
 33
 34     /* 创建sk_buff 空间 */
 35     nl_skb = nlmsg_new(len, GFP_ATOMIC);
 36     if(!nl_skb)
 37     {
 38         printk("netlink alloc failure\n");
 39         return -1;
 40     }
 41
 42     /* 设置netlink消息头部 */
 43     nlh = nlmsg_put(nl_skb, 0, 0, NETLINK_TEST, len, 0);
 44     if(nlh == NULL)
 45     {
 46         printk("nlmsg_put failaure \n");
 47         nlmsg_free(nl_skb);
 48         return -1;
 49     }
 50
 51     /* 拷贝数据发送 */
 52     memcpy(nlmsg_data(nlh), pbuf, len);
 53     ret = netlink_unicast(nlsk, nl_skb, USER_PORT, MSG_DONTWAIT);
 54
 55     return ret;
 56 }
 57
 58 static void netlink_rcv_msg(struct sk_buff *skb)
 59 {
 60     struct nlmsghdr *nlh = NULL;
 61     char *umsg = NULL;
 62     char *kmsg = "hello users!!!";
 63
 64     if(skb->len >= nlmsg_total_size(0))
 65     {
 66         nlh = nlmsg_hdr(skb);
 67         umsg = NLMSG_DATA(nlh);
 68         if(umsg)
 69         {
 70             printk("kernel recv from user: %s\n", umsg);
 71             send_usrmsg(kmsg, strlen(kmsg));
 72         }
 73     }
 74 }
 75
 76 struct netlink_kernel_cfg cfg = {
 77         .input  = netlink_rcv_msg, /* set recv callback */
 78 };
 79
 80 int test_netlink_init(void)
 81 {
 82     /* create netlink socket */
 83     nlsk = (struct sock *)netlink_kernel_create(&init_net, NETLINK_TEST, &cfg);
 84     if(nlsk == NULL)
 85     {
 86         printk("netlink_kernel_create error !\n");
 87         return -1;
 88     }
 89     printk("test_netlink_init\n");
 90
 91     return 0;
 92 }
 93
 94 void test_netlink_exit(void)
 95 {
 96     if (nlsk){
 97         netlink_kernel_release(nlsk); /* release ..*/
 98         nlsk = NULL;
 99     }
100     printk("test_netlink_exit!\n");
101 }
102
103 module_init(test_netlink_init);
104 module_exit(test_netlink_exit);

Makeflie:

 1 #
 2 #Desgin of Netlink
 3 #
 4
 5 MODULE_NAME :=netlink_test
 6 obj-m :=$(MODULE_NAME).o
 7
 8 KERNELDIR ?= /lib/modules/$(shell uname -r)/build
 9 PWD := $(shell pwd)
10
11 all:
12     $(MAKE) -C $(KERNELDIR) M=$(PWD)
13
14 clean:
15     $(MAKE) -C $(KERNELDIR) M=$(PWD) clean

运行结果:

首先将编译出来的Netlink内核模块插入到系统当中(insmod netlink_test.ko)可以看到如下:

1 [[email protected] nt_2nd]# insmod netlink_test.ko
2 [[email protected] nt_2nd]# dmesg
3 [25024.276345] test_netlink_init

接着运行应用程序:./a.out

1 [[email protected] nt_2nd]# ./a.out
2 send kernel:hello netlink!!
3 from kernel:hello users!!!
4 [[email protected] nt_2nd]# dmesg
5 [25024.276345] test_netlink_init
6 [25117.548350] kernel recv from user: hello netlink!!
7 [[email protected] nt_2nd]# 
时间: 2024-08-01 18:55:44

linux netlink通信机制的相关文章

linux之通信机制

通信是一个比较重要的概念.只要存在多于一个执行单元(并发),就有可能存在通信. linux上的并发主要分为多进程(任务)和多线程.linux也提供了多个通信机制来支持不同进程或者不同线程之间的信息传递. 通信方式主要包括管道,套接字,消息队列,共享内存,信号量,互斥量,信号(如kill -0检测进程是否正常)等. 管道: 1. CGI技术.deamon进程接收一个请求后,创建子进程执行CGI程序,通过无名管道跟子进程进行通信,父进程负责写fd[0],子进程负责写fd[1].然后deamon程序通

1、netlink 连接器 通信机制

使用netlink之前,先参考一下资料:http://www.ibm.com/developerworks/cn/linux/l-connector/ netlink通信机制介绍:资料来源 linux-4.8.13/Documentation/connector/connector.txt 2016-12-10 21:31:04 /*****************************************/Kernel Connector./***********************

Linux内核通信之netlink机制

前言: 开发和维护内核是一件很繁杂的工作,因此,只有那些最重要或者与系统性能息息相关的代码才将其安排在内核中.其它程序,比如GUI,管理以及控制部分的代码,一般都会作为用户态程序.用户态和内核态的通讯机制IPC(interprocess   communication  )机制:比如系统调用,ioctl接口,proc文件系统以及netlink socket. 介绍: netlink socekt是一种用于在内核态和用户态进程之间进行数据传输的特殊的IPC.它通过为内核模块提供一组特殊的API,并

Linux netlink机制

netlink 是一种特殊的 socket,它是 Linux 所特有的,类似于 BSD 中的AF_ROUTE 但又远比它的功能强大,目前在最新的 Linux 内核(2.6.14)中使用netlink 进行应用与内核通信的应用很多,包括:路由 daemon(NETLINK_ROUTE),1-wire 子系统(NETLINK_W1),用户态 socket 协议(NETLINK_USERSOCK),防火墙(NETLINK_FIREWALL),socket 监视(NETLINK_INET_DIAG),n

linux各种IPC机制(进程通信)

linux各种IPC机制 (2011-07-08 16:58:35)     原文地址:linux各种IPC机制(转)作者:jianpengliu 原帖发表在IBM的developerworks网站上,是一个系列的文章,作者郑彦兴,通过讲解和例子演示了Linux中几种IPC的使用方式,我觉得很好,在这里做一个保留,能看完的话Linux IPC的基础是没有问题的了.一)Linux环境进程间通信(一)管道及有名管道http://www.ibm.com/developerworks/cn/linux/

Android中的常见通信机制和Linux中的通信机制

Handler Handler是Android系统中的一种消息传递机制,起作用是应对多线程场景.将A进程的消息传递给B线程,实现异步消息处理.很多情况是将工作线程中需要更新UI的操作消息传递给UI主线程,而实现更新UI操作. 因为工作线程和主线程是共享地址空间,即Handler实例对象mHandler位于线程间共享的内存堆上,工作线程和主线程直接使用该对象,只需要注意多线程的同步问题.工作系统通过mHandler向其成员变量MessageQueue中添加Message,而主线程一直处于loop中

netlink通信

1:参考网址 netlink机制介绍与实例 linux netlink机制 2:netlink相关socket API netlink用于程序与内核模块之间进行通信.用户态使用netlink与内核态使用netlink方式不同,Linux container在网络管理这一块处于用户空间.netlink 在用户态的API与tcp/ip通信使用的socket api 类似,主要api为socket().bind().sendmsg().recvmsg(). socket() 使用netlink需要包含

Qt的内部进程通信机制 [转]

Qt 作为一种跨平台的基于 C++ 的 GUI系统,能够提供给用户构造图形用户界面的强大功能.自从 1996 年 Qt 被 Trolltech公司发布以来,该系统成为世界上很多成功的图形用户应用所使用的主要系统.更为重要的是,Linux 操作系统的桌面环境系统 KDE 也是基于 Qt构造的.目前,Qt 已经提供了对包括 MS/Windows.Unix/X11 和嵌入式平台的支持,得到了越来越广泛的应用. 在 Qt 系统中,不仅有着构造完善的系统结构,而且为了满足用户对编写图形用户界面应用的种种需

linux内通信及关机重启

linux内通信 关机重启write 用户名开始写内容 Ctrl+退格键或delete删除Ctrl+d保存结束一定要是在线用户 wall(write all)给所有用户发信息wall message mail 用户名 不管用户在不在线邮件在/var/spool/mail/root下>N(new) root(谁给你发的) & 1 看第一封邮件& h 看标题& d 1 删除& q 退出mail主要看系统发的邮件 last 哪个用户什时候登录过,什么方式,从哪登录, 持续登