Issue
I am really new to this Kernel stuff. I went here and I found this code that outputs process information like its ID.
main.c
:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched/signal.h>
#include <linux/sched.h>
struct task_struct *task; /* Structure defined in sched.h for tasks/processes */
struct task_struct *task_child; /* Structure needed to iterate through task children */
struct list_head *list; /* Structure needed to iterate through the list in each task->children struct */
int iterate_init(void) /* Init Module */
{
printk(KERN_INFO "%s","LOADING MODULE\n"); /* good practice to log when loading/removing modules */
for_each_process( task ){ /* for_each_process() MACRO for iterating through each task in the os located in linux\sched\signal.h */
printk(KERN_INFO "\nPARENT PID: %d PROCESS: %s STATE: %ld",task->pid, task->comm, task->state);/* log parent id/executable name/state */
list_for_each(list, &task->children){ /* list_for_each MACRO to iterate through task->children */
task_child = list_entry( list, struct task_struct, sibling ); /* using list_entry to declare all vars in task_child struct */
printk(KERN_INFO "\nCHILD OF %s[%d] PID: %d PROCESS: %s STATE: %ld",task->comm, task->pid, /* log child of and child pid/name/state */
task_child->pid, task_child->comm, task_child->state);
}
printk("-----------------------------------------------------"); /*for aesthetics*/
}
return 0;
} /* End of Init Module */
void cleanup_exit(void) /* Exit Module */
{
printk(KERN_INFO "%s","REMOVING MODULE\n");
} /* End of Exit Module */
module_init(iterate_init); /* Load Module MACRO */
module_exit(cleanup_exit); /* Remove Module MACRO */
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ITERATE THROUGH ALL PROCESSES/CHILD PROCESSES IN THE OS");
MODULE_AUTHOR("Laerehte");
Makefile
:
obj-m += main.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Executed with ./ins
(with chmod +x)
Code:
sudo insmod main.ko
sudo rmmod main
sudo dmesg -c
I looked up how to find how much memory a process uses, and I found this question: Memory usage of current process in C.
Correct me if I'm wrong here, but I'm thinking that you can read the current RAM usage of these processes by looking in /proc/[process_id]/status
. I found out from another place(forgot where) that within this file, there is something called VmRSS that would hold the current RAM usage of the process.
You can apparently use:
ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos);
to read a file, but I have not been able to modify main.c
with this successfully. I need to find the size of the file, but I also have not been able to use vfs_stat
correctly. When I just try some constant integer, I get all 0s in the buffer anyway. I don't know how to use these functions properly. I'm trying to modify main.c so that I will see the RAM usage of these processes along with the other information. Much of the information I found was outdated. Can anyone help?
Solution
While you can technically open and read files from kernel space, it's usually a bad idea for multiple reasons. Whatever information is provided under /proc
is already available to the kernel, you just need to figure out where it is and how to obtain it, and you will be able to do everything in your module without reading any file.
You are interested in knowing how much RAM is a particular process using given its PID, and you are correct that this statistic is available in /proc/<PID>/status
: in fact, it is labeled as "VmRSS" which means "virtual memory resident set size". If we take a look at the kernel source code, we can see exactly how that number is calculated:
The kernel function called when reading /proc/<PID>/status
is proc_pid_status()
in fs/proc/array.c
, which then calls (among the other things) task_mem()
.
// ...
anon = get_mm_counter(mm, MM_ANONPAGES);
file = get_mm_counter(mm, MM_FILEPAGES);
shmem = get_mm_counter(mm, MM_SHMEMPAGES);
// ...
hiwater_rss = total_rss = anon + file + shmem;
// ...
SEQ_PUT_DEC(" kB\nVmHWM:\t", hiwater_rss);
SEQ_PUT_DEC(" kB\nVmRSS:\t", total_rss); // <===== here it is
SEQ_PUT_DEC(" kB\nRssAnon:\t", anon);
// ...
You can therefore obtain this information in the same way. First get ahold of the task_struct
of the process you want to check, then inspect the ->mm
field like get_mm_counter()
does, or simply using get_mm_rss()
from include/linux/mm.h
, which does exactly what we want.
Note that:
- The value obtained from
get_mm_counter()
orget_mm_rss()
is the number of pages, you will have to multiply this by the page size (simply shift left byPAGE_SHIFT
). - You will only be able to do this if your kernel was compiled with MMU support (
CONFIG_MMU=y
), you can check this in your module code with a simple#ifdef
or#ifndef
.
Here's a working example module to do this for all processes:
// SPDX-License-Identifier: GPL-3.0
#include <linux/kernel.h> // printk(), pr_*()
#include <linux/module.h> // THIS_MODULE, MODULE_VERSION, ...
#include <linux/init.h> // module_{init,exit}
#include <linux/sched/task.h> // struct task_struct, {get,put}_task_struct()
#include <linux/sched/signal.h> // for_each_process()
#include <linux/mm.h> // get_mm_rss()
/* Tested on kernel 5.6, qemu-system-aarch64
* Usage: sudo insmod task_rss
* sudo modprobe task_rss
*/
#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
static int __init modinit(void)
{
struct task_struct *tsk;
unsigned long rss;
#ifndef CONFIG_MMU
pr_err("No MMU, cannot calculate RSS.\n");
return -EINVAL;
#endif
for_each_process(tsk) {
get_task_struct(tsk);
// https://www.kernel.org/doc/Documentation/vm/active_mm.rst
if (tsk->mm) {
rss = get_mm_rss(tsk->mm) << PAGE_SHIFT;
pr_info("PID %d (\"%s\") VmRSS = %lu bytes\n", tsk->pid, tsk->comm, rss);
} else {
pr_info("PID %d (\"%s\") is an anonymous process\n", tsk->pid, tsk->comm);
}
put_task_struct(tsk);
}
return 0;
}
static void __exit modexit(void)
{
// This function is only needed to be able to unload the module.
}
module_init(modinit);
module_exit(modexit);
MODULE_VERSION("0.1");
MODULE_DESCRIPTION("Silly test module to calculare task RSS of all running tasks.");
MODULE_AUTHOR("Marco Bonelli");
MODULE_LICENSE("GPL");
Output on my machine (qemu-system-aarch64
):
/ # insmod task_rss.ko
[ 7.306284] task_rss: loading out-of-tree module taints kernel.
[ 7.312912] task_rss: PID 1 ("init") VmRSS = 4096 bytes
[ 7.313295] task_rss: PID 2 ("kthreadd") is an anonymous process
[ 7.314039] task_rss: PID 3 ("rcu_gp") is an anonymous process
...
[ 7.330169] task_rss: PID 146 ("sh") VmRSS = 1363968 bytes
[ 7.330554] task_rss: PID 147 ("insmod") VmRSS = 1339392 bytes
Answered By - Marco Bonelli