40. linux内核设计与实现¶
在X86上,struct thread_info在文件<asm/thread_info.h>中定义.
struct task_struct 在include/linux/sched.h中定义。
struct task_struct {
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
void *stack;
atomic_t usage;
unsigned int flags; /* per process flags, defined below */
unsigned int ptrace;
int lock_depth; /* BKL lock depth */
struct task_struct *real_parent; /* real parent process */
struct task_struct *parent; /* recipient of SIGCHLD, wait4() reports */
/*
* children/sibling forms the list of my natural children
*/
struct list_head children; /* list of my children */
struct list_head sibling; /* linkage in my parent's children list */
struct task_struct *group_leader; /* threadgroup leader */
创建进程:
fork() vfork() __clone()
+ + +
| | |
+-----------------+
clone()
+
+ do_fork()
+
copy_process()
+
dup_task_struct()
thread_info
task_struct
copy_flags()
alloc_pid()
进程终结: 主要由定义在kernel/exit.c中的do_exit()函数执行
exit()
do_exit()
PF_EXITING
acct_update_integrals(tsk);
exit_mm()
exit_sem()
exit_sem(tsk);
exit_files(tsk);
exit_fs(tsk);
exit_notify(tsk, group_dead);
schedule();
进程的优先级: linux采用了两种不同的优先级范围。一种是nice值。范围-20~+19,默认是0. nice值越小,优先级越高,也就是-20拥有最高优先级。
me@ubuntu:~/code/linux$ ps -el
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 1 0 0 80 0 - 40391 - ? 00:01:09 systemd
1 S 0 2 0 0 80 0 - 0 - ? 00:00:00 kthreadd
1 I 0 4 2 0 60 -20 - 0 - ? 00:00:00 kworker/0:0H
1 I 0 7 2 0 60 -20 - 0 - ? 00:00:00 mm_percpu_wq
1 S 0 8 2 0 80 0 - 0 - ? 00:04:04 ksoftirqd/0
1 I 0 9 2 0 80 0 - 0 - ? 00:07:36 rcu_sched
1 I 0 10 2 0 80 0 - 0 - ? 00:00:00 rcu_bh
1 S 0 11 2 0 -40 - - 0 - ? 00:00:00 migration/0
5 S 0 12 2 0 -40 - - 0 - ? 00:00:07 watchdog/0
1 S 0 13 2 0 80 0 - 0 - ? 00:00:00 cpuhp/0
1 S 0 14 2 0 80 0 - 0 - ? 00:00:00 cpuhp/1
5 S 0 15 2 0 -40 - - 0 - ? 00:00:04 watchdog/1
1 S 0 16 2 0 -40 - - 0 - ? 00:00:00 migration/1
me@ubuntu:~/code/linux$ ps -eo state,uid,pid,ppid,rtprio,time,comm
S UID PID PPID RTPRIO TIME COMMAND
S 0 1 0 - 00:01:09 systemd
S 0 2 0 - 00:00:00 kthreadd
I 0 4 2 - 00:00:00 kworker/0:0H
I 0 7 2 - 00:00:00 mm_percpu_wq
S 0 8 2 - 00:04:04 ksoftirqd/0
I 0 9 2 - 00:07:38 rcu_sched
I 0 10 2 - 00:00:00 rcu_bh
S 0 11 2 99 00:00:00 migration/0
S 0 12 2 99 00:00:07 watchdog/0
S 0 13 2 - 00:00:00 cpuhp/0
S 0 14 2 - 00:00:00 cpuhp/1
S 0 15 2 99 00:00:04 watchdog/1
S 0 16 2 99 00:00:00 migration/1
S 0 17 2 - 00:02:14 ksoftirqd/1
I 0 19 2 - 00:00:00 kworker/1:0H
S 0 20 2 - 00:00:00 cpuhp/2
S 0 21 2 99 00:00:04 watchdog/2
S 0 22 2 99 00:00:00 migration/2
S 0 23 2 - 00:02:11 ksoftirqd/2
I 0 25 2 - 00:00:00 kworker/2:0H
42. 时间,节拍,系统定时器¶
<arm/param.h> 定义了节拍率。
include/asm-generic/param.h
#ifdef __KERNEL__
# define HZ CONFIG_HZ /* Internal kernel timer frequency */
# define USER_HZ 100 /* some user interfaces are */
# define CLOCKS_PER_SEC (USER_HZ) /* in "ticks" like times() */
#endif
me@ubuntu:~/code/linux$ grep -rn CONFIG_HZ . | grep x86
./arch/x86/configs/i386_defconfig:340:CONFIG_HZ=1000
./arch/x86/configs/x86_64_defconfig:341:CONFIG_HZ=1000
找了一些设备进行验证
名称 | 架构 | OS | 内核版本 | 时钟中断频率 | 用户接口时钟频率 | log |
---|---|---|---|---|---|---|
RH2288 V3 | x86_64 | RHEL7.6 | 3.10.0-957.el7.x86_64 | 1000HZ | 100HZ 10ms | |
Taishan2280v2 | ARM | RHEL7.6 | 4.18.0-74.el8.aarch64 | 100HZ | 100HZ 10ms | [log] |
Red Hat kvm | x86_64 | ubuntu | 4.15.0-20-generic | 250HZ | 100HZ 10ms | [log] |
43. 实际时间¶
当前时间(墙上时间)定义在文件kernel/time/timekeeping.c中:
struct timespec xtime;
timespec数据结构定义在文件<linux/time.h>中:
struct timespec{
_kernel_time_t tv_sec;
long tc_nsec;
}
44. 竞争和锁¶
各种锁的机制区别在于:当锁已经被其他线程持有,因而不可用时的行为表现—–一些锁会简单地执行忙等待,而另外一些锁会使当前任务睡眠直到锁可用为止。 锁解决竞争条件地前提是,锁是原子操作实现的。 在X86体系结构总,锁的实现使用了成为compare和exchange的类似指令。
45. 内核提供了两组原子操作接口¶
一组针对整数进行操作,另一组针对单独的位进行操作.
整数原子操作数据类型定义在include/linux/types.h
typedef struct {
volatile int counter;
} atomic_t;
整数原子操作定义在:
include/asm-generic/atomic.h
位原子操作定义在:
include/linux/bitops.h
asm/bitops.h
46. 锁¶
自旋锁。申请锁的进程旋转等待,耗费处理器时间,持有自旋锁的时间应该小于进程两次上下文切换的时间。 信号量。申请信号量的进程会被睡眠,等待唤醒,不消耗处理器时间。 读写自旋锁。 多个线程可以同时获得读锁,读锁可以递归。写锁会保证没有人能在读或者写。
自旋锁定义在 asm/spinlock.h, 调用结构定义在linux/spinlock.h
48. 进程调度¶
红黑树,最左侧是要执行的进程
X
X
XX
X 当代队列
XXX +---+---+----+---+---+--+--+
XXXXXX XX XX <----- | | | | | | | |
XXXXXXX XX XX X X X X | | | | | | | |
XX XX XXX X +---+---+----+---+---+--+--+
X XXX XX X XXX
XX XXXXX X X X
XX X X X XXX
XXXX X XX XX
XX X XX XX XX XXX X XXXXXX
XXX XX X X XX XX XX XXX X XX X X X 进程调度的入口时schedule(). pick_next_task()负责查找下一个要运行的task。查找最高优先级的调度类。 调度类缓存有下一个要执行的task,直接返回。 CFS是朴廷进程的调度类。 休眠的进程在等待队列wake_queue_haed_t处理. DEFILE_WAIT()创建一个等待队列项。 add_wait_queue()把自己加入到队列中. prepare_to_wait()将进程状态变更为TASK_Interruptible或者TASK_UNINTERRUPTIBLE。条件满足后调用finish_wait()把自己移出等待队列