在容器内部执行iotop,会报下面的错误(ENOENT):
Traceback (most recent call last):
File "/usr/sbin/iotop", line 16, in <module>
main()
File "/usr/lib/python2.6/site-packages/iotop/ui.py", line 559, in main
main_loop()
File "/usr/lib/python2.6/site-packages/iotop/ui.py", line 549, in <lambda>
main_loop = lambda: run_iotop(options)
File "/usr/lib/python2.6/site-packages/iotop/ui.py", line 447, in run_iotop
return curses.wrapper(run_iotop_window, options)
File "/usr/lib64/python2.6/curses/wrapper.py", line 43, in wrapper
return func(stdscr, *args, **kwds)
File "/usr/lib/python2.6/site-packages/iotop/ui.py", line 437, in run_iotop_window
taskstats_connection = TaskStatsNetlink(options)
File "/usr/lib/python2.6/site-packages/iotop/data.py", line 113, in __init__
self.family_id = controller.get_family_id('TASKSTATS')
File "/usr/lib/python2.6/site-packages/iotop/genetlink.py", line 54, in get_family_id
m = self.conn.recv()
File "/usr/lib/python2.6/site-packages/iotop/netlink.py", line 190, in recv
raise err
OSError: Netlink error: No such file or directory (2)
iotop是通过内核的taskstats接口获取统计信息的。taskstats是基于generic netlink实现的,而且taskstats不支持net namespace。
static struct genl_family family = {
.id = GENL_ID_GENERATE,
.name = TASKSTATS_GENL_NAME,
.version = TASKSTATS_GENL_VERSION,
.maxattr = TASKSTATS_CMD_ATTR_MAX,
};
static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb)
{
int i, n = 0;
struct genl_family *rt;
struct net *net = sock_net(skb->sk);
int chains_to_skip = cb->args[0];
int fams_to_skip = cb->args[1];
for (i = chains_to_skip; i < GENL_FAM_TAB_SIZE; i++) {
n = 0;
list_for_each_entry(rt, genl_family_chain(i), family_list) {
if (!rt->netnsok && !net_eq(net, &init_net))///don't support netnamespace
continue;
写程序调用genl_ctrl_resolve,会报下面的错误:
#./taskstats --pid 1
genl_ctrl_resolve: Generic Netlink Family not found (errno = No such file or directory)
参考这里。
内核函数调用:
OpenVZ的Andrey Vagin已经提交了相关patch,但还没有合到内核主线。
测试程序
/*
* gcc -o taskstats taskstats_nl.c -lnl
*/
#include <stdlib.h>
#include <linux/taskstats.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
void usage(int argc, char *argv[]);
int msg_recv_cb(struct nl_msg *, void *);
int main(int argc, char *argv[])
{
struct nl_handle *sock;
struct nl_msg *msg;
int family;
int pid = -1;
char *cpumask;
if (argc > 2 && strcmp(argv[1], "--pid") == 0) {
pid = atoi(argv[2]);
} else if (argc > 2 && strcmp(argv[1], "--cpumask") == 0) {
cpumask = argv[2];
} else {
usage(argc, argv);
exit(1);
}
sock = nl_handle_alloc();
if(!sock){
nl_perror("nl_handle_alloc");
exit(1);
}
// Connect to generic netlink socket on kernel side
if(genl_connect(sock) < 0){
nl_perror("genl_connect");
exit(1);
}
// get the id for the TASKSTATS generic family
family = genl_ctrl_resolve(sock, "TASKSTATS");
if(family < 0){
nl_perror("genl_ctrl_resolve");
exit(1);
}
// register for task exit events
msg = nlmsg_alloc();
genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_REQUEST,
TASKSTATS_CMD_GET, TASKSTATS_VERSION);
if (pid > 0) {
nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, pid);
} else {
nla_put_string(msg, TASKSTATS_CMD_ATTR_REGISTER_CPUMASK,
cpumask);
}
nl_send_auto_complete(sock, msg);
nlmsg_free(msg);
// specify a callback for inbound messages
//nl_socket_modify_cb(sock, NL_CB_MSG_IN, NL_CB_CUSTOM, msg_recv_cb, NULL);
if (pid > 0) {
if(nl_recvmsgs_default(sock) < 0){
nl_perror("nl_recvmsgs_default");
exit(-1);
}
} else {
while (1)
nl_recvmsgs_default(sock);
}
return 0;
}
void usage(int argc, char *argv[])
{
printf("USAGE: %s option\nOptions:\n"
"\t--pid pid : get statistics during a task's lifetime.\n"
"\t--cpumask mask : obtain statistics for tasks which are exiting. \n"
"\t\tThe cpumask is specified as an ascii string of comma-separated \n"
"\t\tcpu ranges e.g. to listen to exit data from cpus 1,2,3,5,7,8\n"
"\t\tthe cpumask would be '1-3,5,7-8'.\n", argv[0]);
}
#define printllu(str, value) printf("%s: %llu\n", str, value)
int msg_recv_cb(struct nl_msg *nlmsg, void *arg)
{
struct nlmsghdr *nlhdr;
struct nlattr *nlattrs[TASKSTATS_TYPE_MAX + 1];
struct nlattr *nlattr;
struct taskstats *stats;
int rem;
nlhdr = nlmsg_hdr(nlmsg);
// validate message and parse attributes
genlmsg_parse(nlhdr, 0, nlattrs, TASKSTATS_TYPE_MAX, 0);
if (nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) {
stats = nla_data(nla_next(nla_data(nlattr), &rem));
printf("---\n");
printf("pid: %u\n", stats->ac_pid);
printf("command: %s\n", stats->ac_comm);
printf("status: %u\n", stats->ac_exitcode);
printf("time:\n");
printf(" start: %u\n", stats->ac_btime);
printllu(" elapsed", stats->ac_etime);
printllu(" user", stats->ac_utime);
printllu(" system", stats->ac_stime);
printf("memory:\n");
printf(" bytetime:\n");
printllu(" rss", stats->coremem);
printllu(" vsz", stats->virtmem);
printf(" peak:\n");
printllu(" rss", stats->hiwater_rss);
printllu(" vsz", stats->hiwater_vm);
printf("io:\n");
printf(" bytes:\n");
printllu(" read", stats->read_char);
printllu(" write", stats->write_char);
printf(" syscalls:\n");
printllu(" read", stats->read_syscalls);
printllu(" write", stats->write_syscalls);
}
return 0;
}