转载请标明出处floater的csdn blog,http://blog.csdn.net/flaoter
进程的管理与调度是所有操作系统的核心功能。从内核的角度来看,进程是内核分配资源(CPU,Memory)的重要单元,是计算机用来管理这些资源的一种抽象。从本节开始,将对linux内核的进程管理与调度子系统进行分析,其中使用的内核版本是4.4。
1 进程描述
在linux内核中,每一个进程唯一对应一个struct task_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;
#ifdef CONFIG_SMP
struct llist_node wake_entry;
int on_cpu;
unsigned int wakee_flips;
unsigned long wakee_flip_decay_ts;
struct task_struct *last_wakee;
int wake_cpu;
#endif
int on_rq;
....
}
按照结构成员的用途,大体上分成了如下的类别,
(1) 基本信息。comm, pid, tgid, uid, gid, stack, on_cpu, on_rq, in_execve, in_iowait…
(2) 进程关系。real_parent, children, sibling…
(3) 状态相关。state, exit_state, exit_code…
(4) 使用内存。mm, active_mm, min_flt, maj_flt…
(5) 调度相关。prio, static_prio, normal_prio, rt_prio, sched_class, se, policy…
(6) 事件相关。utime, stime, start_time, real_start_time…
(7) 信号相关。signal, sighand, blocked, real_blocked, pending…
(8)文件系统。fs, files, nameidata…
(9) Misc
2 进程状态
进程的状态定义如下所示,
#define TASK_RUNNING 0 //R态,进程正在运行或在rq中等待运行
#define TASK_INTERRUPTIBLE 1 //S态,阻塞等待资源或者信号
#define TASK_UNINTERRUPTIBLE 2 //D态,阻塞等待资源
#define __TASK_STOPPED 4 //暂停状态
#define __TASK_TRACED 8 //跟踪状态
/* in tsk->exit_state */
#define EXIT_DEAD 16
#define EXIT_ZOMBIE 32
#define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD)
/* in tsk->state again */
#define TASK_DEAD 64
#define TASK_WAKEKILL 128
#define TASK_WAKING 256
#define TASK_PARKED 512
#define TASK_NOLOAD 1024
#define TASK_STATE_MAX 2048
状态转换的状态机如图所示,
3 进程初始化
进程的初始化是在start_kernel中的reset_init中实现的,创建了两个进程init和kthreadd。
kernel_thread(kernel_init, NULL, CLONE_FS); //init进程创建
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); //kthread内核线程创建
创建的init进程再通过解析init.rc等文件进行用户进程的创建。
static int __ref kernel_init(void *unused)
{
...
if (!try_to_run_init_process("/sbin/init") ||
!try_to_run_init_process("/etc/init") ||
!try_to_run_init_process("/bin/init") ||
!try_to_run_init_process("/bin/sh")) //执行init程序,parse init.rc,创建子进程
return 0;
...
}
init进程与用户进程的关系如下:
init-+-adbd-+-sh—busybox
| -4*[{adbd}]
-{Binder_2}
|-cndaemon
|-cp_diskserver---{cp_diskserver}
|-debuggerd
|-debuggerd64
|-drmserver-+-{Binder_1}
|
|-gatekeeperd
|-gnss_download—2*[{gnss_download}]
|-gpsd—2*[{gpsd}]
|-healthd
|-installd
|-keystore
|-lmfs
|-lmkd
|-logd-+-{logd.auditd}
| |-{logd.control}
| |-{logd.daemon}
| |-5*[{logd.reader.per}]
| |-{logd.reader}
| `-{logd.writer}
|-main-+-.quicksearchbox-+-{ApplicationsPro}
| | |-{Binder_1}
| | |-{Binder_2}
| | |-{FinalizerDaemon}
| | |-{FinalizerWatchd}
| | |-{HeapTaskDaemon}
…
创建的kthreadd内核线程通过遍历kthread_create_list创建内核线程。
int kthreadd(void *unused)
{
...
set_task_comm(tsk, "kthreadd");
for (;;) {
...
spin_lock(&kthread_create_lock);
while (!list_empty(&kthread_create_list)) {
struct kthread_create_info *create;
create = list_entry(kthread_create_list.next,
struct kthread_create_info, list); //遍历kthread_create_list,创建子线程
list_del_init(&create->list);
spin_unlock(&kthread_create_lock);
create_kthread(create);
spin_lock(&kthread_create_lock);
}
spin_unlock(&kthread_create_lock);
}
return 0;
}
kthreadd与内核线程的关系如下:
kthreadd-+-VserTestWq
|-adaptive_ts_wor
|-agdsp_access
|-ata_sff
|-aud_sblock-1-3
|-aud_sblock-1-4
|-binder
|-bioset
|-bm_perf
|-carveout_camera
|-carveout_fb
|-carveout_mm
|-carveout_overla
|-cfg80211
|-cfinteractive
|-compr drain
|-compr monitor
|-crypto
|-deferwq
|-devfreq_wq
|-dhd_dpc
|-dhd_rxf
|-dhd_watchdog_th
…
4 进程创建
常用的进程创建方法如下:
fork—————–用户进程
pthread_create—- 用户线程
do_fork ————轻量级进程
kthread_create —-内核线程
5 thread_info与内核栈
thread_info保存了特定体系结构的汇编代码段需要访问的那部分进程的数据。内核栈供用户进程的内核代码或内核线程使用。arm 32bit 处理器thread_info和内核栈task->stack合用2页内存空间。
union thread_union {
struct thread_info thread_info;
unsigned long stack[THREAD_SIZE/sizeof(long)];
};
常用的current指针指向了当前的thread_info的task成员。
#define get_current() (current_thread_info()->task)
#define current get_current()
static inline struct thread_info *current_thread_info(void)
{
return (struct thread_info *)
(current_stack_pointer & ~(THREAD_SIZE - 1));
}
current_thread_info就是当前sp屏蔽掉低12位。