Initial commit
This commit is contained in:
183
ch6/foreach/thrd_showall/Makefile
Normal file
183
ch6/foreach/thrd_showall/Makefile
Normal file
@@ -0,0 +1,183 @@
|
||||
# ch6/foreach/thrd_showall/Makefile
|
||||
# ***************************************************************
|
||||
# This program is part of the source code released for the book
|
||||
# "Linux Kernel Programming"
|
||||
# (c) Author: Kaiwan N Billimoria
|
||||
# Publisher: Packt
|
||||
# GitHub repository:
|
||||
# https://github.com/PacktPublishing/Linux-Kernel-Programming
|
||||
#
|
||||
# From: Ch 5 : Writing Your First Kernel Module LKMs, Part 2
|
||||
# ***************************************************************
|
||||
# Brief Description:
|
||||
# A 'better' Makefile template for Linux LKMs (Loadable Kernel Modules); besides
|
||||
# the 'usual' targets (the build, install and clean), we incorporate targets to
|
||||
# do useful (and indeed required) stuff like:
|
||||
# - adhering to kernel coding style (indent+checkpatch)
|
||||
# - several static analysis targets (via sparse, gcc, flawfinder, cppcheck)
|
||||
# - two 'dummy' dynamic analysis targets (KASAN, LOCKDEP)
|
||||
# - a packaging (.tar.xz) target and
|
||||
# - a help target.
|
||||
#
|
||||
# To get started, just type:
|
||||
# make help
|
||||
#
|
||||
# For details, please refer the book, Ch 5.
|
||||
|
||||
# To support cross-compiling for kernel modules:
|
||||
# For architecture (cpu) 'arch', invoke make as:
|
||||
# make ARCH=<arch> CROSS_COMPILE=<cross-compiler-prefix>
|
||||
ifeq ($(ARCH),arm)
|
||||
# *UPDATE* 'KDIR' below to point to the ARM Linux kernel source tree on your box
|
||||
KDIR ?= ~/rpi_work/kernel_rpi/linux
|
||||
else ifeq ($(ARCH),arm64)
|
||||
# *UPDATE* 'KDIR' below to point to the ARM64 (Aarch64) Linux kernel source
|
||||
# tree on your box
|
||||
KDIR ?= ~/kernel/linux-4.14
|
||||
else ifeq ($(ARCH),powerpc)
|
||||
# *UPDATE* 'KDIR' below to point to the PPC64 Linux kernel source tree on your box
|
||||
KDIR ?= ~/kernel/linux-4.9.1
|
||||
else
|
||||
# 'KDIR' is the Linux 'kernel headers' package on your host system; this is
|
||||
# usually an x86_64, but could be anything, really (f.e. building directly
|
||||
# on a Raspberry Pi implies that it's the host)
|
||||
KDIR ?= /lib/modules/$(shell uname -r)/build
|
||||
endif
|
||||
|
||||
PWD := $(shell pwd)
|
||||
obj-m += thrd_showall.o
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
|
||||
all:
|
||||
@echo
|
||||
@echo '--- Building : KDIR=${KDIR} ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} EXTRA_CFLAGS=${EXTRA_CFLAGS} ---'
|
||||
@echo
|
||||
make -C $(KDIR) M=$(PWD) modules
|
||||
install:
|
||||
@echo
|
||||
@echo "--- installing ---"
|
||||
@echo
|
||||
make -C $(KDIR) M=$(PWD) modules_install
|
||||
clean:
|
||||
@echo
|
||||
@echo "--- cleaning ---"
|
||||
@echo
|
||||
make -C $(KDIR) M=$(PWD) clean
|
||||
rm -f *~ # from 'indent'
|
||||
|
||||
#--------------- More (useful) targets! -------------------------------
|
||||
INDENT := indent
|
||||
|
||||
# code-style : "wrapper" target over the following kernel code style targets
|
||||
code-style:
|
||||
make indent
|
||||
make checkpatch
|
||||
|
||||
# indent- "beautifies" C code - to conform to the the Linux kernel
|
||||
# coding style guidelines.
|
||||
# Note! original source file(s) is overwritten, so we back it up.
|
||||
indent:
|
||||
@echo
|
||||
@echo "--- applying kernel code style indentation with indent ---"
|
||||
@echo
|
||||
mkdir bkp 2> /dev/null; cp -f *.[chsS] bkp/
|
||||
${INDENT} -linux --line-length95 *.[chsS]
|
||||
# add source files as required
|
||||
|
||||
# Detailed check on the source code styling / etc
|
||||
checkpatch:
|
||||
make clean
|
||||
@echo
|
||||
@echo "--- kernel code style check with checkpatch.pl ---"
|
||||
@echo
|
||||
$(KDIR)/scripts/checkpatch.pl --no-tree -f --max-line-length=95 *.[ch]
|
||||
# add source files as required
|
||||
|
||||
#--- Static Analysis
|
||||
# sa : "wrapper" target over the following kernel static analyzer targets
|
||||
sa:
|
||||
make sa_sparse
|
||||
make sa_gcc
|
||||
make sa_flawfinder
|
||||
make sa_cppcheck
|
||||
|
||||
# static analysis with sparse
|
||||
sa_sparse:
|
||||
make clean
|
||||
@echo
|
||||
@echo "--- static analysis with sparse ---"
|
||||
@echo
|
||||
# if you feel it's too much, use C=1 instead
|
||||
make C=2 CHECK="/usr/bin/sparse" -C $(KDIR) M=$(PWD) modules
|
||||
|
||||
# static analysis with gcc
|
||||
sa_gcc:
|
||||
make clean
|
||||
@echo
|
||||
@echo "--- static analysis with gcc ---"
|
||||
@echo
|
||||
make W=1 -C $(KDIR) M=$(PWD) modules
|
||||
|
||||
# static analysis with flawfinder
|
||||
sa_flawfinder:
|
||||
make clean
|
||||
@echo
|
||||
@echo "--- static analysis with flawfinder ---"
|
||||
@echo
|
||||
flawfinder *.[ch]
|
||||
|
||||
# static analysis with cppcheck
|
||||
sa_cppcheck:
|
||||
make clean
|
||||
@echo
|
||||
@echo "--- static analysis with cppcheck ---"
|
||||
@echo
|
||||
cppcheck -v --force .
|
||||
|
||||
# Packaging; just tar.xz as of now
|
||||
PKG_NAME := lkm_template
|
||||
tarxz-pkg:
|
||||
rm -f ../${PKG_NAME}.tar.xz 2>/dev/null
|
||||
make clean
|
||||
@echo
|
||||
@echo "--- packaging ---"
|
||||
@echo
|
||||
tar caf ../${PKG_NAME}.tar.xz *
|
||||
ls -l ../${PKG_NAME}.tar.xz
|
||||
@echo '=== package created: ../$(PKG_NAME).tar.xz ==='
|
||||
|
||||
help:
|
||||
@echo '=== Makefile Help : additional targets available ==='
|
||||
@echo
|
||||
@echo 'TIP: type make <tab><tab> to show all valid targets'
|
||||
@echo
|
||||
|
||||
@echo '--- 'usual' kernel LKM targets ---'
|
||||
@echo 'typing "make" or "all" target : builds the kernel module object (the .ko)'
|
||||
@echo 'install : installs the kernel module(s) to INSTALL_MOD_PATH (default: /lib/modules/$(shell uname -r)/)'
|
||||
@echo 'clean : cleanup - remove all kernel objects, temp files/dirs, etc'
|
||||
|
||||
@echo
|
||||
@echo '--- kernel code style targets ---'
|
||||
@echo 'code-style : "wrapper" target over the following kernel code style targets'
|
||||
@echo ' indent : run the $(INDENT) utility on source file(s) to indent them as per the kernel code style'
|
||||
@echo ' checkpatch : run the kernel code style checker tool on source file(s)'
|
||||
|
||||
@echo
|
||||
@echo '--- kernel static analyzer targets ---'
|
||||
@echo 'sa : "wrapper" target over the following kernel static analyzer targets'
|
||||
@echo ' sa_sparse : run the static analysis sparse tool on the source file(s)'
|
||||
@echo ' sa_gcc : run gcc with option -W1 ("Generally useful warnings") on the source file(s)'
|
||||
@echo ' sa_flawfinder : run the static analysis flawfinder tool on the source file(s)'
|
||||
@echo 'TIP: use coccinelle as well (requires spatch): https://www.kernel.org/doc/html/v4.15/dev-tools/coccinelle.html'
|
||||
|
||||
@echo
|
||||
@echo '--- kernel dynamic analysis targets ---'
|
||||
@echo 'da_kasan : DUMMY target: this is to remind you to run your code with the dynamic analysis KASAN tool enabled; requires configuring the kernel with CONFIG_KASAN On, rebuild and boot it'
|
||||
@echo 'da_lockdep : DUMMY target: this is to remind you to run your code with the dynamic analysis LOCKDEP tool (for deep locking issues analysis) enabled; requires configuring the kernel with CONFIG_PROVE_LOCKING On, rebuild and boot it'
|
||||
@echo 'TIP: best to build a debug kernel with several kernel debug config options turned On, boot via it and run all your test cases'
|
||||
|
||||
@echo
|
||||
@echo '--- misc targets ---'
|
||||
@echo 'tarxz-pkg : tar and compress the LKM source files as a tar.xz into the dir above; allows one to transfer and build the module on another system'
|
||||
@echo 'help : this help target'
|
||||
156
ch6/foreach/thrd_showall/thrd_showall.c
Normal file
156
ch6/foreach/thrd_showall/thrd_showall.c
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* ch6/foreach/thrd_showall/thrd_showall.c
|
||||
***************************************************************
|
||||
* This program is part of the source code released for the book
|
||||
* "Linux Kernel Programming" 2E
|
||||
* (c) Author: Kaiwan N Billimoria
|
||||
* Publisher: Packt
|
||||
* GitHub repository:
|
||||
* https://github.com/PacktPublishing/Linux-Kernel-Programming_2E
|
||||
*
|
||||
* From: Ch 6 : Kernel and MM Internals Essentials
|
||||
****************************************************************
|
||||
* Brief Description:
|
||||
* This kernel module iterates over the task structures of all *threads*
|
||||
* currently alive on the box, printing out some details.
|
||||
* We use the do_each_thread() { ... } while_each_thread() macros to do
|
||||
* so here.
|
||||
*
|
||||
* For details, please refer the book, Ch 6.
|
||||
*/
|
||||
//#define pr_fmt(fmt) "%s: " fmt, KBUILD_MODNAME
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h> /* current() */
|
||||
#include <linux/version.h>
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 10, 0)
|
||||
#include <linux/sched/signal.h>
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Kaiwan N Billimoria");
|
||||
MODULE_DESCRIPTION("LKP 2E:ch6/foreach/thrd_showall: \
|
||||
demo to display all threads by iterating over the task list");
|
||||
MODULE_LICENSE("Dual MIT/GPL");
|
||||
MODULE_VERSION("0.2");
|
||||
|
||||
/* Display just CPU 0's idle thread, i.e., the pid 0 task,
|
||||
* the (terribly named) 'swapper/n'; n = 0, 1, 2,...
|
||||
* Again, init_task is always the task structure of the first CPU's
|
||||
* idle thread, i.e., we're referencing swapper/0.
|
||||
*/
|
||||
static inline void disp_idle_thread(void)
|
||||
{
|
||||
struct task_struct *t = &init_task;
|
||||
|
||||
/* We know that the swapper is a kernel thread */
|
||||
pr_info("%8d %8d 0x%px 0x%px [%16s]\n",
|
||||
t->pid, t->pid, t, t->stack, t->comm);
|
||||
}
|
||||
|
||||
static int showthrds(void)
|
||||
{
|
||||
struct task_struct *g = NULL, *t = NULL; /* 'g' : process ptr; 't': thread ptr */
|
||||
int nr_thrds = 1, total = 1; /* total init to 1 for the idle thread */
|
||||
#define BUFMAX 256
|
||||
#define TMPMAX 128
|
||||
char buf[BUFMAX], tmp[TMPMAX];
|
||||
const char hdr[] =
|
||||
"------------------------------------------------------------------------------------------\n"
|
||||
" TGID PID current stack-start Thread Name MT? # thrds\n"
|
||||
"------------------------------------------------------------------------------------------\n";
|
||||
|
||||
pr_info("%s", hdr);
|
||||
disp_idle_thread();
|
||||
|
||||
/*
|
||||
* The do_each_thread() / while_each_thread() is a pair of macros that iterates over
|
||||
* _all_ task structures in memory.
|
||||
* The task structs are global of course; this implies we should hold a lock of some
|
||||
* sort while working on them (even if only reading!). So, doing
|
||||
* read_lock(&tasklist_lock);
|
||||
* [...]
|
||||
* read_unlock(&tasklist_lock);
|
||||
* BUT, this lock - tasklist_lock - isn't exported and thus unavailable to modules.
|
||||
* So, using an RCU read lock is indicated here (this has been added later to this code).
|
||||
* FYI: a) Ch 12 and Ch 13 cover the details on kernel synchronization.
|
||||
* b) Read Copy Update (RCU) is a complex synchronization mechanism; it's
|
||||
* conceptually explained really well in this blog article:
|
||||
* https://reberhardt.com/blog/2020/11/18/my-first-kernel-module.html
|
||||
*/
|
||||
rcu_read_lock();
|
||||
do_each_thread(g, t) { /* 'g' : process ptr; 't': thread ptr */
|
||||
// task_lock(t);
|
||||
get_task_struct(t); // take a reference to the task struct
|
||||
|
||||
snprintf(buf, BUFMAX-1, "%8d %8d ", g->tgid, t->pid);
|
||||
|
||||
/* task_struct addr and kernel-mode stack addr */
|
||||
snprintf(tmp, TMPMAX-1, " 0x%px", t);
|
||||
/*
|
||||
* To concatenate the temp string to our buffer, we could go with the
|
||||
* strncat() here; flawfinder, though, points out this is potentially
|
||||
* dangerous; so we simply use another snprintf() to achieve the same.
|
||||
* Why not use strlcat() instead? Here, it runs into trouble - being
|
||||
* called in an atomic context, which isn't ok (due to the
|
||||
* might_sleep() within it's code)...
|
||||
*/
|
||||
snprintf(buf, BUFMAX-1, "%s%s 0x%px", buf, tmp, t->stack);
|
||||
|
||||
if (!g->mm) { // kernel thread
|
||||
/* One might question why we don't use the get_task_comm() to obtain
|
||||
* the task's name here; the short reason: it causes a deadlock! We
|
||||
* shall explore this (and how to avoid it) in some detail in Ch 17 -
|
||||
* Kernel Synchronization Part 2. For now, we just do it the simple way
|
||||
*/
|
||||
snprintf(tmp, TMPMAX-1, " [%16s]", t->comm);
|
||||
} else {
|
||||
snprintf(tmp, TMPMAX-1, " %16s ", t->comm);
|
||||
}
|
||||
snprintf(buf, BUFMAX-1, "%s%s", buf, tmp);
|
||||
|
||||
/* Is this the "main" thread of a multithreaded process?
|
||||
* We check by seeing if (a) it's a userspace thread,
|
||||
* (b) it's TGID == it's PID, and (c), there are >1 threads in
|
||||
* the process.
|
||||
* If so, display the number of threads in the overall process
|
||||
* to the right..
|
||||
*/
|
||||
nr_thrds = get_nr_threads(g);
|
||||
if (g->mm && (g->tgid == t->pid) && (nr_thrds > 1)) {
|
||||
snprintf(tmp, TMPMAX-1, " %3d", nr_thrds);
|
||||
snprintf(buf, BUFMAX-1, "%s%s", buf, tmp);
|
||||
}
|
||||
|
||||
snprintf(buf, BUFMAX-1, "%s\n", buf);
|
||||
pr_info("%s", buf);
|
||||
|
||||
total++;
|
||||
memset(buf, 0, sizeof(buf));
|
||||
memset(tmp, 0, sizeof(tmp));
|
||||
|
||||
put_task_struct(t); // release reference to the task struct
|
||||
// task_unlock(t);
|
||||
} while_each_thread(g, t);
|
||||
rcu_read_unlock();
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static int __init thrd_showall_init(void)
|
||||
{
|
||||
int total;
|
||||
|
||||
pr_info("%s: inserted\n", KBUILD_MODNAME);
|
||||
total = showthrds();
|
||||
pr_info("%s: total # of threads on the system: %d\n", KBUILD_MODNAME, total);
|
||||
|
||||
return 0; /* success */
|
||||
}
|
||||
|
||||
static void __exit thrd_showall_exit(void)
|
||||
{
|
||||
pr_info("%s: removed\n", KBUILD_MODNAME);
|
||||
}
|
||||
|
||||
module_init(thrd_showall_init);
|
||||
module_exit(thrd_showall_exit);
|
||||
Reference in New Issue
Block a user