文章目录
前言
宏观上看,操作系统能够同时运行多个进程;微观上看,如果操作系统只有一个处理器,那么在同一时刻只能运行一个进程。
操作系统以很短的时间间隔不停地切换多个进程的执行,这种管理方式需要注意以下问题:
- 进程不能彼此干扰。进程 A 的错误不能传播到进程 B。Linux 是一个多用户系统,所以也要确保进程不能读取或修改其它进程的内存,否则就很容易访问其它用户的私有数据。
- CPU 时间必须在各种应用程序之间尽可能公平地共享 ,其中一些程序可能比其它程序更重要。内核必须决定为各个进程分配多长时间 ,何时切换到下一个进程 。这又引出了哪个进程是下一个地问题。
- 在内核从进程 A 切换到进程 B 时,必须确保进程 B 的执行环境与上一次撤销其处理器资源时完全相同。例如,处理器寄存器的内容和虚拟地址空间的结构必须与此前相同。
第 3 项工作与处理器极度相关,不能只用 C 语言实现,还需要汇编代码的帮助。
第 2、3 项工作是称之为调度器的内核子系统的职责。
task_struct
内核在实现上述功能时,使用的最主要的一个数据结构就是 task_struct。
- 调度相关使用 sched_info 成员,包括优先级、vruntime、调度策略等
- 上下文信息使用 thread_stack、task_info、stack 成员存储,不同架构的 CPU,这些成员的定义也不同。
- 内存管理使用 mm 成员,其中页全局目录使用 mm->pgd 存储
除此之外还包括
- pid,进程号
- __state,进程状态,如,0: TASK_RUNNING
- parent,父进程
- comm,进程名
- files,进程打开的文件
- cpu,进程所在的处理器
- nvcsw:自愿进行上下文切换的次数
- nivcsw:非自愿进行上下文切换的次数
- stime:进程在内核态所经过的节拍数
- utime:进程在用户态所经过的节拍数
等
打印各字段
task_struct 结构体比较复杂,盯着代码看各个成员有时也不容易理解,最直接的办法就是将各个成员打印出来,理论结合实际,这样可以更加深刻的理解各个字段的含义。
下面我们就写一个内核模块,打印当前系统中各个进程的 task_struct 成员信息
task_struct.c
c
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h> //task结构体
#include <linux/fdtable.h> //files
#include <linux/fs_struct.h> //fs
#include <linux/mm_types.h> //打印内存信息
#include <linux/init_task.h>
#include <linux/types.h>
#include <linux/atomic.h>
#include <linux/delay.h>
void show(struct task_struct *p)
{
printk("pid: %3d, %16.16s, state: %4d, prior: %3d, static_pri: %3d, "
"parent_pid: %3d, count: %3d, umask: %3d, stack:0x%x, \n\t\t"
"cpu:%d, nvcsw:%ld, nivcsw:%ld, stime:%lld, utime:%lld, "
"policy:%d, vruntime:%lld"
"\n",
p->pid, p->comm, p->__state, p->prio, p->static_prio, (p->parent)->pid, atomic_read(&(p->files)->count),
(p->fs)->umask, p->stack, task_thread_info(p)->cpu, p->nvcsw, p->nivcsw, p->stime, p->utime, p->policy,
p->se.vruntime);
// linux中内核线程的 mm 是空的,要对它进行打印,就会出错,指针错误
if ((p->mm) != NULL) {
// 线性区总的页数, 页全局目录
printk("Total_vm: %ld, pgd:0x%x, ", (p->mm)->total_vm, p->mm->pgd[0]);
}
if ((p->active_mm) != NULL) {
printk("active_mm_pgd:0x%x", p->active_mm->pgd[0]);
}
}
static int __init print_pcb(void)
{
struct task_struct *task, *p;
struct list_head *pos; //双向链表
int count = 0; //统计当前系统一共有多少个进程
printk("begin...\n");
// 对链表遍历时,希望从第一个开始,指向 0 号进程 pcb
task = &init_task;
show(task);
//遍历操作,使用pos指向,传入的参数task指向tasks字段.0号进程的tasks进行遍历。tasks将所有的进程连在一块。
list_for_each (pos, &task->tasks) {
//找到一个节点,就可以用这个节点的 tasks 字段,找到这个结构体的地址.对应的字段 tasks
//此时的p指针已经指向task_struct结构体的首部,后面就可以通过p指针进行操作
p = list_entry(pos, struct task_struct, tasks);
show(p);
count++;
}
printk("进程的个数:%d\n", count);
return 0;
}
static void __exit exit_pcb(void)
{
printk("Exiting...\n");
}
module_init(print_pcb);
module_exit(exit_pcb);
MODULE_LICENSE("GPL");
Makefile
c
obj-m:=task_struct.o
KDIR=/home/liyongjun/project/board/buildroot/Vexpress/build/linux-5.15.18
CROSS_COMPILE=/home/liyongjun/project/board/buildroot/Vexpress/build/linux-5.15.18/../../host/bin/arm-buildroot-linux-uclibcgnueabihf-
all:
make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) modules
clean:
make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) clean
运行,
使用 qemu 运行一个 4 核 arm 系统,然后安装我们编译的内核模块
bash
# tftp -gr task_struct.ko 192.168.31.223
# insmod task_struct.ko
begin...
pid: 0, swapper/0, state: 0, prior: 120, static_pri: 120, parent_pid: 0, count: 54, umask: 18, stack:0x80c00000,
cpu:0, nvcsw:0, nivcsw:273299, stime:100260000000, utime:0, policy:0, vruntime:0
active_mm_pgd:0x81b70000
pid: 1, init, state: 1, prior: 120, static_pri: 120, parent_pid: 0, count: 1, umask: 18, stack:0x81096000,
cpu:2, nvcsw:311, nivcsw:11, stime:650000000, utime:40000000, policy:0, vruntime:1271261734
Total_vm: 393, pgd:0x811b0000,
active_mm_pgd:0x811b0000
pid: 2, kthreadd, state: 1, prior: 120, static_pri: 120, parent_pid: 0, count: 54, umask: 18, stack:0x81098000,
cpu:2, nvcsw:59, nivcsw:0, stime:40000000, utime:0, policy:0, vruntime:2859880532
pid: 3, rcu_gp, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x8109a000,
cpu:0, nvcsw:2, nivcsw:0, stime:0, utime:0, policy:0, vruntime:10781931
pid: 4, rcu_par_gp, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x8109c000,
cpu:0, nvcsw:2, nivcsw:0, stime:0, utime:0, policy:0, vruntime:12672772
pid: 6, kworker/0:0H, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x810cc000,
cpu:0, nvcsw:4, nivcsw:0, stime:0, utime:0, policy:0, vruntime:467744348
pid: 7, kworker/u8:0, state: 1026, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x810ce000,
cpu:3, nvcsw:8, nivcsw:1, stime:90000000, utime:0, policy:0, vruntime:194562752
pid: 8, mm_percpu_wq, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x810d0000,
cpu:0, nvcsw:2, nivcsw:0, stime:0, utime:0, policy:0, vruntime:19554971
pid: 9, ksoftirqd/0, state: 1, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x810d2000,
cpu:0, nvcsw:10009, nivcsw:1, stime:0, utime:0, policy:0, vruntime:84093189061
pid: 10, rcu_sched, state: 1026, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x810d4000,
cpu:2, nvcsw:526, nivcsw:0, stime:10000000, utime:0, policy:0, vruntime:3005160995
pid: 11, migration/0, state: 1, prior: 0, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x810d6000,
cpu:0, nvcsw:6, nivcsw:0, stime:20000000, utime:0, policy:1, vruntime:0
pid: 12, cpuhp/0, state: 1, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x810de000,
cpu:0, nvcsw:9, nivcsw:0, stime:0, utime:0, policy:0, vruntime:439359833
pid: 13, cpuhp/1, state: 1, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x810e0000,
cpu:1, nvcsw:8, nivcsw:0, stime:0, utime:0, policy:0, vruntime:259698436
pid: 14, migration/1, state: 1, prior: 0, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x810e2000,
cpu:1, nvcsw:6, nivcsw:0, stime:20000000, utime:0, policy:1, vruntime:0
pid: 15, ksoftirqd/1, state: 1, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x810e4000,
cpu:1, nvcsw:1211, nivcsw:0, stime:0, utime:0, policy:0, vruntime:40372256279
pid: 17, kworker/1:0H, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x810e8000,
cpu:1, nvcsw:5, nivcsw:0, stime:0, utime:0, policy:0, vruntime:109593599
pid: 18, cpuhp/2, state: 1, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x810ea000,
cpu:2, nvcsw:8, nivcsw:0, stime:0, utime:0, policy:0, vruntime:59346256
pid: 19, migration/2, state: 1, prior: 0, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x810ec000,
cpu:2, nvcsw:7, nivcsw:0, stime:20000000, utime:0, policy:1, vruntime:0
pid: 20, ksoftirqd/2, state: 1, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x810ee000,
cpu:2, nvcsw:686, nivcsw:0, stime:0, utime:0, policy:0, vruntime:2800704118
pid: 21, kworker/2:0, state: 1026, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x810f0000,
cpu:2, nvcsw:5, nivcsw:0, stime:10000000, utime:0, policy:0, vruntime:11475716
pid: 23, cpuhp/3, state: 1, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x810f4000,
cpu:3, nvcsw:8, nivcsw:0, stime:0, utime:0, policy:0, vruntime:99372461
pid: 24, migration/3, state: 1, prior: 0, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x81104000,
cpu:3, nvcsw:7, nivcsw:0, stime:20000000, utime:0, policy:1, vruntime:0
pid: 25, ksoftirqd/3, state: 1, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x81106000,
cpu:3, nvcsw:563, nivcsw:0, stime:0, utime:0, policy:0, vruntime:15162602723
pid: 26, kworker/3:0, state: 1026, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x81118000,
cpu:3, nvcsw:5, nivcsw:0, stime:0, utime:0, policy:0, vruntime:9468662
pid: 28, kdevtmpfs, state: 1, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x8111c000,
cpu:2, nvcsw:142, nivcsw:0, stime:10000000, utime:0, policy:0, vruntime:68424254
pid: 29, inet_frag_wq, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x81120000,
cpu:1, nvcsw:2, nivcsw:0, stime:0, utime:0, policy:0, vruntime:8684611
pid: 30, kworker/0:1, state: 1026, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x81192000,
cpu:0, nvcsw:14, nivcsw:0, stime:10000000, utime:0, policy:0, vruntime:82145485871
pid: 31, kworker/2:1, state: 1026, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x81194000,
cpu:2, nvcsw:28, nivcsw:0, stime:0, utime:0, policy:0, vruntime:2960087740
pid: 32, kworker/1:1, state: 1026, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x81196000,
cpu:1, nvcsw:116960, nivcsw:0, stime:4110000000, utime:0, policy:0, vruntime:40372906611
pid: 33, kworker/3:1, state: 1026, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x811aa000,
cpu:3, nvcsw:364, nivcsw:0, stime:10000000, utime:0, policy:0, vruntime:15162450516
pid: 34, khungtaskd, state: 1, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x81178000,
cpu:2, nvcsw:321, nivcsw:0, stime:0, utime:0, policy:0, vruntime:2967078288
pid: 35, oom_reaper, state: 1, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x8117a000,
cpu:3, nvcsw:2, nivcsw:0, stime:0, utime:0, policy:0, vruntime:37961818
pid: 36, writeback, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x81198000,
cpu:1, nvcsw:2, nivcsw:0, stime:0, utime:0, policy:0, vruntime:38309928
pid: 37, kcompactd0, state: 1, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x8119a000,
cpu:2, nvcsw:76806, nivcsw:0, stime:1090000000, utime:0, policy:0, vruntime:3011151327
pid: 38, kblockd, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x811ac000,
cpu:1, nvcsw:2, nivcsw:0, stime:0, utime:0, policy:0, vruntime:47985364
pid: 39, ata_sff, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x811ae000,
cpu:1, nvcsw:2, nivcsw:0, stime:10000000, utime:0, policy:0, vruntime:58046861
pid: 41, rpciod, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x81256000,
cpu:3, nvcsw:2, nivcsw:0, stime:10000000, utime:0, policy:0, vruntime:57739139
pid: 42, kworker/u9:0, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x812cc000,
cpu:3, nvcsw:2, nivcsw:0, stime:0, utime:0, policy:0, vruntime:67126213
pid: 43, xprtiod, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x812ce000,
cpu:3, nvcsw:2, nivcsw:0, stime:0, utime:0, policy:0, vruntime:76917350
pid: 44, kworker/1:1H, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x812d0000,
cpu:1, nvcsw:70, nivcsw:0, stime:30000000, utime:0, policy:0, vruntime:40370062181
pid: 45, kswapd0, state: 1, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x812d2000,
cpu:0, nvcsw:3, nivcsw:0, stime:0, utime:0, policy:0, vruntime:433440459
pid: 46, nfsiod, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x812d4000,
cpu:0, nvcsw:2, nivcsw:0, stime:0, utime:0, policy:0, vruntime:432884339
pid: 47, kworker/1:2, state: 1026, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x812d6000,
cpu:1, nvcsw:49, nivcsw:0, stime:40000000, utime:0, policy:0, vruntime:359242846
pid: 48, kworker/u8:2, state: 1026, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x813cc000,
cpu:3, nvcsw:149, nivcsw:7, stime:270000000, utime:0, policy:0, vruntime:15178519976
pid: 52, irq/33-mmci-pl1, state: 1, prior: 49, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x813d4000,
cpu:0, nvcsw:2, nivcsw:0, stime:0, utime:0, policy:1, vruntime:0
pid: 54, mmc_complete, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x81aa0000,
cpu:0, nvcsw:2, nivcsw:0, stime:0, utime:0, policy:0, vruntime:476729289
pid: 55, kworker/0:1H, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x81aa2000,
cpu:0, nvcsw:94, nivcsw:1, stime:130000000, utime:0, policy:0, vruntime:84079734629
pid: 56, kworker/0:2, state: 1026, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x81aa4000,
cpu:0, nvcsw:257435, nivcsw:1, stime:7380000000, utime:0, policy:0, vruntime:84094426935
pid: 57, card0-crtc0, state: 1, prior: 49, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x81aa6000,
cpu:3, nvcsw:2, nivcsw:0, stime:0, utime:0, policy:1, vruntime:0
pid: 58, kworker/3:1H, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x81882000,
cpu:3, nvcsw:74, nivcsw:0, stime:30000000, utime:0, policy:0, vruntime:15161848812
pid: 59, ext4-rsv-conver, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x81884000,
cpu:0, nvcsw:2, nivcsw:0, stime:0, utime:0, policy:0, vruntime:764178885
pid: 61, kworker/3:2H, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x81a88000,
cpu:3, nvcsw:3, nivcsw:0, stime:0, utime:0, policy:0, vruntime:279463688
pid: 62, kworker/2:1H, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x81a8a000,
cpu:2, nvcsw:100, nivcsw:1, stime:80000000, utime:0, policy:0, vruntime:1121465971
pid: 63, kworker/2:2H, state: 1026, prior: 100, static_pri: 100, parent_pid: 2, count: 54, umask: 18, stack:0x81a8c000,
cpu:2, nvcsw:5, nivcsw:0, stime:0, utime:0, policy:0, vruntime:185536371
pid: 80, syslogd, state: 1, prior: 120, static_pri: 120, parent_pid: 1, count: 1, umask: 18, stack:0x81bf8000,
cpu:2, nvcsw:377, nivcsw:10, stime:190000000, utime:220000000, policy:0, vruntime:3021892453
Total_vm: 391, pgd:0x81c08000,
active_mm_pgd:0x81c08000
pid: 84, klogd, state: 0, prior: 120, static_pri: 120, parent_pid: 1, count: 1, umask: 18, stack:0x81ba4000,
cpu:3, nvcsw:15, nivcsw:42, stime:350000000, utime:300000000, policy:0, vruntime:15200845932
Total_vm: 390, pgd:0x81b8c000,
active_mm_pgd:0x81b8c000
pid: 136, udhcpc, state: 1, prior: 120, static_pri: 120, parent_pid: 1, count: 1, umask: 18, stack:0x81ba6000,
cpu:3, nvcsw:4, nivcsw:0, stime:0, utime:0, policy:0, vruntime:14252907400
Total_vm: 392, pgd:0x81cac000,
active_mm_pgd:0x81cac000
pid: 138, sh, state: 1, prior: 120, static_pri: 120, parent_pid: 1, count: 1, umask: 18, stack:0x81bbc000,
cpu:0, nvcsw:121, nivcsw:7, stime:200000000, utime:130000000, policy:0, vruntime:84105463853
Total_vm: 396, pgd:0x81b70000,
active_mm_pgd:0x81b70000
pid: 155, kworker/0:0, state: 1026, prior: 120, static_pri: 120, parent_pid: 2, count: 54, umask: 18, stack:0x81bb2000,
cpu:0, nvcsw:2, nivcsw:0, stime:0, utime:0, policy:0, vruntime:82150084578
pid: 158, insmod, state: 0, prior: 120, static_pri: 120, parent_pid: 138, count: 1, umask: 18, stack:0x8111a000,
cpu:1, nvcsw:0, nivcsw:0, stime:70000000, utime:0, policy:0, vruntime:40458619481
Total_vm: 391, pgd:0x81b88000,
active_mm_pgd:0x81b88000
进程的个数:59
#
简单分析:
一、
处于运行状态的进程有 3 个,分别是
- 0 号进程 swapper/0,运行在 cpu0 上
- 84 号进程 klogd,运行在 cpu3 上
- 158 号进程 insmod,运行在 cpu1 上
二、
insmod 进程为单次运行,所以 nvcsw(自愿进行上下文切换的次数) 和 nivcsw(非自愿进行上下文切换的次数) 均为 0。
0 号进程为空闲进程,只有系统空闲时才会被调度,它的优先级最低,所以自愿进行上下文切换的次数为 0,全都是非自愿进行上下文切换,nvcsw:0, nivcsw:273299
。
三、
大部分进程的调度策略 policy 都为 0: SCHED_NORMAL,该策略使用 CFS(完全公平调度器) 调度器实现。所以 CFS 是内核用的最多的调度算法。该算法使用 vruntime 这个参数,谁小,下一个就调度谁。关于 CFS 后面有机会再展开讲。
四、
每个进程(包括内核线程和用户空间进程)的栈地址都不同,说明每个进程都有自己独立的栈空间。
五、
所有内核线程的 utime 都为 0,因为它只会在内核态运行。
用户空间进程的 utime 和 stime 都有值,因为它既可以运行在用户空间,也可以通过系统调用让内核帮自己完成某些工作,这个时候该进程就运行于内核空间。
六、
kthreadd 是所有内核线程的父进程,它生于 0 号进程。
init 是所有用户进程的父进程,它也生于 0 号进程。
七、
最繁忙的进程是如下几个
- swapper/0
- init
- kworker/2:1
- kworker/0:2
- kworker/1:2
swapper/0 进程很好理解,系统空闲时就会执行它
init 会监控所有用户进程,必要时为其"收尸",所以 CPU 占用较高
kworker 处理中断下半部,内存管理、进程调度也都有参与,所以 CPU 占用率较高