X

News, tips, partners, and perspectives for the Oracle Linux operating system and upstream Linux kernel work

Getting system resource information with a Standard API

Oracle Linux kernel developer Rahul Yadav kicked off a new project in LXC this year, called libresource. In this blog post, he talks about how to use libresource to effectively read system statistics in a stable manner. This project is hosted on github at https://github.com/lxc/libresource

System resource information, like memory, network and device statistics, are crucial for system administrators to understand the inner workings of their systems, and are increasingly being used by applications to fine tune performance on different environments.

Getting system resource information on Linux is not a straightforward affair. Many tools like top, free and sar can gather system statistics. The best way is to collect the information from procfs or sysfs, but getting such information from procfs or sysfs presents many challenges.  Each time an application wants to get a system resource information, it has to open a file, read the content and then parse the content to get actual information. Over time, the format in which information is provided might change and with that each application has to change its own code to read the data in the correct manner. Libresource tries to fix few of these problems by providing a standard library with set of APIs through which we can get system resource information e.g. memory, CPU, stat, networking, security related information. Find libresource on github at https://github.com/lxc/libresource

Libresource provides following benefits:

  • Ease of use: Currently applications needs to read this info mostly from /proc and /sys file-systems. In most of the cases complex string parsing is involved which is needed to be done in application code. With the library APIs application can get the information directly and all the string parsing, if any, will be done by library.
  • Stability: If the format in which the information is provided in /proc or /sys file-system is changed then the application code is changed to align with those changes. Also if a better way to get information comes in future, like through a syscall or a sysconf then again application code needs to be changed to get the benefit of it. Library will take care of such changes and the application will never have to change the code.
  • Virtualization: In cases where DB is running in a virtualized environment using cgroup or namespaces, reading from /proc and /sys file-systems might not give correct information as these are not cgroup aware. Library API will take care of this e.g. if a process is running in a cgroup then library should provide information which is local to that cgroup. 

Interfaces to libresource

Reading a single resource ID

/* This is to read a resource information. A valid resource id should be
 * provided in res_id, out should be properly allocated on the basis of
 * size of resource information, hint should be given where needed.
 * Currently pid and flags are not used, they are for future extensions.
 */
int resread(int resid, void out, void hint, int pid, int flags);

/* Available Resource IDs */
RES_MEM_ACTIVE	Total amount of buffer or page cache memory, in kilobytes, that is in active use.
RES_MEM_INACTIVE	Total amount of buffer or page cache memory, in kilobytes, that are free and available
RES_MEM_AVAILABLE	An estimate of how much memory is available for starting new applications, without swapping.
RES_MEM_FREE	The amount of physical RAM, in kilobytes, left unused by the system.
RES_MEM_TOTAL	Total amount of physical RAM, in kilobytes.
RES_MEM_PAGESIZE	Size of a page in bytes
RES_MEM_SWAPFREE	Total amount of swap free, in kilobytes.
RES_MEM_SWAPTOTAL	The total amount of swap available, in kilobytes.
RES_KERN_COMPILE_TIME	Kernel compile time
RES_KERN_RELEASE	Kernel version
RES_NET_ALLIFSTAT	Network stat for all interfaces on system. 
RES_NET_IFSTAT	Network stat for an interface
RES_MEM_INFOALL	All Memory related information

Reading multiple resources in one call

If an application wants to read multiple resource information in one call, it can call res_*_blk APIs to do so which are described below.
#define RES_UNIT_OUT_SIZE   256
 
/* This union is used to return resource information of various types */
union r_data {
    int i;
    size_t sz;
    long l;
    char str[RES_UNIT_OUT_SIZE];
    void *ptr;
};
 
/* In case of res_read_blk, each resource information will be represented by
 * following structure.
 */
typedef struct res_unit {
    int status;
    unsigned int res_id;
    void *hint;
    union r_data data;
} res_unit_t;
 
/* In case of bulk read (res_read_blk), this structure will hold all required
 * information needed to do so.
 */
typedef struct res_blk {
    int res_count;
    res_unit_t *res_unit[0];
} res_blk_t;
/* It allocates memory for resources and initiates them properly.
 * res_ids holds an array of valid resource ids and res_count holds
 * number of resource ids. It also initializes struct fields properly.
 */
extern res_blk_t *res_build_blk(int *res_ids, int res_count);
 
/* Reading bulk resource information. Memory must be properly allocated and
 * all fields should be properly filled to return error free resource
 * information. res_build_blk call is suggested to allocate build res_blk_t
 * structure.
 */
extern int res_read_blk(res_blk_t *resblk, int pid, int flags);
 
/* Free allocated memory from res_build_blk */
extern void res_destroy_blk(res_blk_t *resblk);

Some Examples

Reading total memory

size_t stemp = 0;
res_read(RES_MEM_TOTAL,&stemp,NULL, 0, 0);
printf("MEMTOTAL is: %zu\n", stemp);

Reading network interface related statistics for interface named "lo"

res_net_ifstat_t ifstat;
res_read(RES_NET_IFSTAT,&ifstat, (void *)"lo",0, 0);
printf("status for %s: %llu %llu\n", ifstat.ifname,
    ifstat.rx_bytes,
    ifstat.rx_packets
);

Reading multiple resources in one call

res_blk_t *b = NULL;
int a[NUM] = {RES_MEM_PAGESIZE,
            RES_MEM_TOTAL,
            RES_MEM_AVAILABLE,
            RES_MEM_INFOALL,
            RES_KERN_RELEASE,
            RES_NET_IFSTAT,
            RES_NET_ALLIFSTAT,
            RES_KERN_COMPILE_TIME
            };
b = res_build_blk(a, NUM);
b->res_unit[5]->hint = (void *)"lo";
 
res_read_blk(b, 0, 0);
 
printf("pagesize %ld bytes,\n memtotal %ld kb,\n memavailable %ld kb,\n"
        " memfree %ld kb,\n release %s,\n compile time %s\n",
        b->res_unit[0]->data.sz,
        b->res_unit[1]->data.sz,
        b->res_unit[2]->data.sz,
        ((res_mem_infoall_t *)(b->res_unit[3]->data.ptr))->memfree,
        b->res_unit[4]->data.str,
        b->res_unit[7]->data.str
);
 
res_net_ifstat_t *ip = (res_net_ifstat_t *)b->res_unit[5]->data.ptr;
printf("stat for interface %s: %llu %llu\n", ip->ifname,
    ip->rx_bytes,
    ip->rx_packets
    );
 
int k = (int)(long long)b->res_unit[6]->hint;
res_net_ifstat_t *ipp = (res_net_ifstat_t *)b->res_unit[6]->data.ptr;
for (int j=0; j< k; j++) {
    printf("stat for interface %s: %llu %llu\n", ipp[j].ifname,
        ipp[j].rx_bytes,
        ipp[j].rx_packets
    );
}
 
free(ipp);
res_destroy_blk(b);
res_blk_t *b = NULL;
int a[NUM] = {RES_MEM_PAGESIZE,
            RES_MEM_TOTAL,
            RES_MEM_AVAILABLE,
            RES_MEM_INFOALL,
            RES_KERN_RELEASE,
            RES_NET_IFSTAT,
            RES_NET_ALLIFSTAT,
            RES_KERN_COMPILE_TIME
            };
b = res_build_blk(a, NUM);
b->res_unit[5]->hint = (void *)"lo";
 
res_read_blk(b, 0, 0);
 
printf("pagesize %ld bytes,\n memtotal %ld kb,\n memavailable %ld kb,\n"
        " memfree %ld kb,\n release %s,\n compile time %s\n",
        b->res_unit[0]->data.sz,
        b->res_unit[1]->data.sz,
        b->res_unit[2]->data.sz,
        ((res_mem_infoall_t *)(b->res_unit[3]->data.ptr))->memfree,
        b->res_unit[4]->data.str,
        b->res_unit[7]->data.str
);
 
res_net_ifstat_t *ip = (res_net_ifstat_t *)b->res_unit[5]->data.ptr;
printf("stat for interface %s: %llu %llu\n", ip->ifname,
    ip->rx_bytes,
    ip->rx_packets
    );
 
int k = (int)(long long)b->res_unit[6]->hint;
res_net_ifstat_t *ipp = (res_net_ifstat_t *)b->res_unit[6]->data.ptr;
for (int j=0; j< k; j++) {
    printf("stat for interface %s: %llu %llu\n", ipp[j].ifname,
        ipp[j].rx_bytes,
        ipp[j].rx_packets
    );
}
 
free(ipp);
res_destroy_blk(b);

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha
Oracle

Integrated Cloud Applications & Platform Services