linux内核中的进程列表

Linux链表数据结构及相关操作

定义 

/include/linux/types.h

struct list_head { struct list_head *next, *prev; };
与响应的结构组成双向链表。

 

初始化

/include/linux/list.h

#define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name)

 

方法

插入

static inline void list_add(struct list_head *new,
struct list_head *head)
static inline void list_add_tail(struct list_head *new,
struct list_head *head)

删除

static inline void list_del(struct list_head *entry)


替换

static inline void list_replace(struct list_head *old,
				struct list_head *new)

交换

static inline void list_swap(struct list_head *entry1, 
struct list_head *entry2)

删除

static inline void list_move(struct list_head *list, 
                             struct list_head *head)

遍历

#define list_for_each(pos, head) \
	for (pos = (head)->next; pos != (head); pos = pos->next)

获取节点某一成员

#define list_entry(ptr, type, member) \
	container_of(ptr, type, member)

 

      以上是基于2.4内核版本的宏, 2.6版本后引入RCU新的锁机制,相关宏定义后新增_rcu. 

     RCU (read-copy update), 适用于大量读,小量写入的场景, 核心思想将写分为写,更新两部, 读操作的时候无阻塞, 当执行写操作时, 更新动作会延时到所有读操作完毕后执行。  init_task 第一个进程没有那么复杂,直接使用了LIST_HEAD_INIT初始化

Linux进程列表

      在进程 task_struct结构中 存在一个list_head ,  task 属性用于将当前进程存入进程列表。

      统计init_task为首链时使用RCU模式的链表, 进程内部的 child和sibling还是使用非RCU宏的链表结构处理。 

 

宏定义了一个循环从init_task开始遍历列表

for_each_process 

/include/linux/sched.h

#define for_each_process(p) \
	for (p = &init_task ; (p = next_task(p)) != &init_task ; )

获取task,next指针指向task_struct中task也就是下一个进程的task
#define next_task(p) \
	list_entry_rcu((p)->tasks.next, struct task_struct, tasks)

/include/linux/rculist.h 
#define list_entry_rcu(ptr, type, member) \
({typeof (*ptr) __rcu *__ptr = (typeof (*ptr) __rcu __force *)ptr; \
container_of((typeof(ptr))rcu_dereference_raw(__ptr), type, member); \
})

找到结构体中member的偏移量
/include/linux/kernel.h
#define container_of(ptr, type, member) ({			\
	const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
	(type *)( (char *)__mptr - offsetof(type,member) );})

Linux进程遍历及实现

环境: cenos7

kernel: 3.10.0-862.el7.x86_64

gcc: gcc version 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC)

task_list_lr.c 文件:

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched/signal.h>
#include <linux/sched.h>

int task_list_lr_init(void)
{
printk(KERN_INFO "module task_list_lr init");
struct task_struct *task;
for_each_process(task) {
printk(KERN_INFO "pid %d, name %s , state %d \n", \
task->pid, task->comm, task->state);
}
return 0;
}

void task_list_lr_exit(void)
{
printk(KERN_INFO "module task_list_lr exit");
}

module_init(task_list_lr_init);
module_exit(task_list_lr_exit);

Makefile文件

obj-m += task_list_lr.o
KERNELDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean

 

加载自定义模块:

[root@centosgpt kmodule]# insmod task_list_lr.ko
[root@centosgpt kmodule]# lsmod |grep task
task_list_lr 12510 0
[root@centosgpt kmodule]# rmmod task_list_lr

查看/var/log/messages输出结果:

00:24:25 centosgpt kernel: module task_list_lr init
00:24:25 centosgpt kernel: pid 1, name systemd , state 1
00:24:25 centosgpt kernel: pid 2, name kthreadd , state 1
00:24:25 centosgpt kernel: pid 3, name ksoftirqd/0 , state 1
00:24:25 centosgpt kernel: pid 5, name kworker/0:0H , state 1
00:24:25 centosgpt kernel: pid 7, name migration/0 , state 1
00:24:25 centosgpt kernel: pid 8, name rcu_bh , state 1
。。。

 

问题

报错提示build目录不存:

[root@centosgpt kmodule]# make
make -C /lib/modules/3.10.0-862.el7.x86_64/build M=/root/src/kmodule modules
make: *** /lib/modules/3.10.0-862.el7.x86_64/build: No such file or directory. Stop.
make: *** [all] Error 2

处理方法:

查看 build目录属性发现他是一个链接文件:

lrwxrwxrwx. 1 root root 38 Dec 16 15:50 build -> /usr/src/kernels/3.10.0- 862.el7.x86_64

build链接到 /usr/src/kernels/3.10.0- 862.el7.x86_64,但  3.10.0- 862.el7.x86_64不存在实际目录为  3.10.0-957.el7 .x86_64

重新链接build到有效目录:

ln -s -f /usr/src/kernels/3.10.0-957.el7 .x86_64 build

not supported by the compiler

[root@centosgpt kmodule]# make
make -C /lib/modules/3.10.0-862.el7.x86_64/build M=/root/src/kmodule modules
make[1]: Entering directory `/usr/src/kernels/3.10.0-957.el7.x86_64'
arch/x86/Makefile:96: stack-protector enabled but compiler support broken
arch/x86/Makefile:166: *** CONFIG_RETPOLINE=y, but not supported by the compiler . Compiler update recommended.. Stop.
make[1]: Leaving directory `/usr/src/kernels/3.10.0-957.el7.x86_64'
make: *** [all] Error 2

处理方法:

安装GCC编译器:

yum install gcc

未找到Makefile

[root@centosgpt kmodule]# make
make -C /lib/modules/3.10.0-862.el7.x86_64/build M=/root/src/kmodule modules
make[1]: Entering directory `/usr/src/kernels/3.10.0-957.el7.x86_64'
scripts/Makefile.build:44: /root/src/kmodule/Makefile: No such file or directory
make[2]: *** No rule to make target `/root/src/kmodule/Makefile'. Stop.
make[1]: *** [_module_/root/src/kmodule] Error 2
make[1]: Leaving directory `/usr/src/kernels/3.10.0-957.el7.x86_64'
make: *** [all] Error 2

处理方法:

将makefile 名称改为Makefile

极客时间 刘超《趣谈Linux操作系统》的课堂作业, 练习后整理了相关知识点。

参考链接中有关于进程列表深度优先的遍历方法。

参考:

/build: No such file or directory

Tasks Lister

Kernel module that iterates over all tasks using depth first tree

Kernel module - List all processes / process information.

list_for_each and list_entry

Be First to Comment

发表评论

电子邮件地址不会被公开。 必填项已用*标注