Taskstats is a netlink-based interface for sending per-task and per-process statistics from the kernel to userspace. –https://goo.gl/aTdgpp
Taskstats接口提供两种方式
- 获取指定进程的统计数据。(用户态程序提供pid号)
- 当进程终止时,获取其统计数据。(用户态程序提供一个cpumask)
详细的接口说明本文不再赘述,请参考kernel doc。本文目的在于通过Taskstats接口学习libnl编程方法。
基于libnl的用户态程序
-
创建一个nl_sock
struct nl_sock *sock = nl_socket_alloc()
其中并没有调用socket系统调用,主要是分配一个nl_sock结构体,填充一些字段,如nl_family = AF_NETLINK
-
创建socket并绑定
该函数才调用了socket(), bind()
sk->s_fd = socket(AF_NETLINK, SOCK_RAW | flags, protocol)
从socket类型可以看出这是无连接的,内核怎么知道哪个进程需要监听taskstats信息呢?
发包告诉内核
-
构造nl_msg
struct nl_msg
{
int nm_protocol;
int nm_flags;
struct sockaddr_nl nm_src;
struct sockaddr_nl nm_dst;
struct ucred nm_creds;
struct nlmsghdr * nm_nlh;
size_t nm_size;
int nm_refcnt;
};
struct nlmsghdr {
__u32 nlmsg_len; /* Length of message including header */
__u16 nlmsg_type; /* Message content */
__u16 nlmsg_flags; /* Additional flags */
__u32 nlmsg_seq; /* Sequence number */
__u32 nlmsg_pid; /* Sending process port ID */
};
struct nlattr {
__u16 nla_len;
__u16 nla_type;
};
nlmsg_alloc分配nl_msg内存和及default_msg_size内存
default_msg_size = getpagesize();
4KB
在nlmsg_alloc
中为nl_msg->nm_nlh分配了4KB大小 的内存,nlmsghdr在4KB头部,nlmsghdr, nlattr, data的关系可以参考下图
<------- NLMSG_ALIGN(hlen) ------> <---- NLMSG_ALIGN(len) --->
+----------------------------+- - -+- - - - - - - - - - -+- - -+
| Header | Pad | Payload | Pad |
| struct nlmsghdr | | | |
+----------------------------+- - -+- - - - - - - - - - -+- - -+
<-------- GENL_HDRLEN -------> <--- hdrlen -->
<------- genlmsg_len(ghdr) ------>
+------------------------+- - -+---------------+- - -+------------+
| Generic Netlink Header | Pad | Family Header | Pad | Attributes |
| struct genlmsghdr | | | | |
+------------------------+- - -+---------------+- - -+------------+
genlmsg_data(ghdr)--------------^ ^
genlmsg_attrdata(ghdr, hdrlen)-------------------------
link
-
发送nl_msg
nl_send_auto_complete(sock, msg);
实际上是调用的nl_sock的callback函数 cb_send_ow
,这一步就让内核知道你这个进程想要获取进程数据,下面就等着内核给你数据就OK了。
-
注册nl_msg处理函数
nl_socket_modify_cb(sock, NL_CB_MSG_IN, NL_CB_CUSTOM, msg_recv_cb, NULL);
用pcap抓过包的同学都知道pcap_loop注册数据包处理函数然后进入循环抓包分析过程。
-
接收nl_msg
nl_recvmsgs_default(sock);
该函数只是接收一个包,对于持续监听终止进程的统计数据,要自己写个死循环。
**下面是完整的代码 **
Tip 1.
发现一个带有函数调用图的源码阅读网站http://sourcecodebrowser.com,体验一下,还是cscope+ctags方便。
留下评论