1. 程式人生 > >Linux程序管理之核心執行緒

Linux程序管理之核心執行緒

    核心原始碼:linux-2.6.38.8.tar.bz2

    目標平臺:ARM體系結構

    在Linux系統中,程序和執行緒都使用task_struct結構體來表示,執行緒只不過是一種特殊(所謂的特殊也不過是在被建立時使用不同的clone標誌組合而已)的程序罷了。

    核心執行緒只執行在核心態,只能使用大於PAGE_OFFSET的線性地址空間。

    1、程序0

    在Linux系統中,只有這個程序是靜態分配的。

/* linux-2.6.38.8/arch/arm/kernel/init_task.c */
struct task_struct init_task = INIT_TASK(init_task);

union thread_union init_thread_union __init_task_data =
	{ INIT_THREAD_INFO(init_task) };

    然後,通過INIT_TASK巨集初始化init_task結構體,通過INIT_THREAD_INFO巨集初始化thread_info結構體。

/* linux-2.6.38.8/include/linux/init_task.h */
#define INIT_TASK(tsk)	\
{									\
	.state		= 0,						\
	.stack		= &init_thread_info,				\
	.usage		= ATOMIC_INIT(2),				\
	.flags		= PF_KTHREAD,					\
	.lock_depth	= -1,						\
	.prio		= MAX_PRIO-20,					\
	.static_prio	= MAX_PRIO-20,					\
	.normal_prio	= MAX_PRIO-20,					\
	.policy		= SCHED_NORMAL,					\
	.cpus_allowed	= CPU_MASK_ALL,					\
	.mm		= NULL,						\
	.active_mm	= &init_mm,					\
	.se		= {						\
		.group_node 	= LIST_HEAD_INIT(tsk.se.group_node),	\
	},								\
	.rt		= {						\
		.run_list	= LIST_HEAD_INIT(tsk.rt.run_list),	\
		.time_slice	= HZ, 					\
		.nr_cpus_allowed = NR_CPUS,				\
	},								\
	.tasks		= LIST_HEAD_INIT(tsk.tasks),			\
	INIT_PUSHABLE_TASKS(tsk)					\
	.ptraced	= LIST_HEAD_INIT(tsk.ptraced),			\
	.ptrace_entry	= LIST_HEAD_INIT(tsk.ptrace_entry),		\
	.real_parent	= &tsk,						\
	.parent		= &tsk,						\
	.children	= LIST_HEAD_INIT(tsk.children),			\
	.sibling	= LIST_HEAD_INIT(tsk.sibling),			\
	.group_leader	= &tsk,						\
	RCU_INIT_POINTER(.real_cred, &init_cred),			\
	RCU_INIT_POINTER(.cred, &init_cred),				\
	.comm		= "swapper",					\
	.thread		= INIT_THREAD,					\
	.fs		= &init_fs,					\
	.files		= &init_files,					\
	.signal		= &init_signals,				\
	.sighand	= &init_sighand,				\
	.nsproxy	= &init_nsproxy,				\
	.pending	= {						\
		.list = LIST_HEAD_INIT(tsk.pending.list),		\
		.signal = {{0}}},					\
	.blocked	= {{0}},					\
	.alloc_lock	= __SPIN_LOCK_UNLOCKED(tsk.alloc_lock),		\
	.journal_info	= NULL,						\
	.cpu_timers	= INIT_CPU_TIMERS(tsk.cpu_timers),		\
	.fs_excl	= ATOMIC_INIT(0),				\
	.pi_lock	= __RAW_SPIN_LOCK_UNLOCKED(tsk.pi_lock),	\
	.timer_slack_ns = 50000, /* 50 usec default slack */		\
	.pids = {							\
		[PIDTYPE_PID]  = INIT_PID_LINK(PIDTYPE_PID),		\
		[PIDTYPE_PGID] = INIT_PID_LINK(PIDTYPE_PGID),		\
		[PIDTYPE_SID]  = INIT_PID_LINK(PIDTYPE_SID),		\
	},								\
	.thread_group	= LIST_HEAD_INIT(tsk.thread_group),		\
	.dirties = INIT_PROP_LOCAL_SINGLE(dirties),			\
	INIT_IDS							\
	INIT_PERF_EVENTS(tsk)						\
	INIT_TRACE_IRQFLAGS						\
	INIT_LOCKDEP							\
	INIT_FTRACE_GRAPH						\
	INIT_TRACE_RECURSION						\
	INIT_TASK_RCU_PREEMPT(tsk)					\
}

/* linux-2.6.38.8/arch/arm/include/asm/thread_info.h */
#define INIT_THREAD_INFO(tsk)						\
{									\
	.task		= &tsk,						\
	.exec_domain	= &default_exec_domain,				\
	.flags		= 0,						\
	.preempt_count	= INIT_PREEMPT_COUNT,				\
	.addr_limit	= KERNEL_DS,					\
	.cpu_domain	= domain_val(DOMAIN_USER, DOMAIN_MANAGER) |	\
			  domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) |	\
			  domain_val(DOMAIN_IO, DOMAIN_CLIENT),		\
	.restart_block	= {						\
		.fn	= do_no_restart_syscall,			\
	},								\
}

    2、程序1和kthreadd核心執行緒

/* linux-2.6.38.8/init/main.c */	
	kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);

	pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
	kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);

/* linux-2.6.38.8/arch/arm/kernel/process.c */
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
	struct pt_regs regs;

	memset(®s, 0, sizeof(regs));

	regs.ARM_r4 = (unsigned long)arg;
	regs.ARM_r5 = (unsigned long)fn;
	regs.ARM_r6 = (unsigned long)kernel_thread_exit;
	regs.ARM_r7 = SVC_MODE | PSR_ENDSTATE | PSR_ISETSTATE;
	regs.ARM_pc = (unsigned long)kernel_thread_helper;
	regs.ARM_cpsr = regs.ARM_r7 | PSR_I_BIT;

	return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);
}

    當建立的程序被排程時,就會執行fn所指向的函式,引數arg用來給fn所指向的函式傳遞引數。引數flags用來儲存傳遞給do_fork函式的clone標誌。

    核心執行緒kthreadd用來建立其它的核心執行緒。在Ubuntu 11.04中,所有其它的核心執行緒的父程序都是此執行緒。

$ ps -ef | grep "00 \[.*\]$"  //在這裡,核心執行緒都用中括號括起來以示區別

root         2     0  0 12:48 ?        00:00:00 [kthreadd]
root         3     2  0 12:48 ?        00:00:00 [ksoftirqd/0]
root         4     2  0 12:48 ?        00:00:00 [kworker/0:0]
root         6     2  0 12:48 ?        00:00:00 [migration/0]
root         7     2  0 12:48 ?        00:00:00 [cpuset]
root         8     2  0 12:48 ?        00:00:00 [khelper]
root         9     2  0 12:48 ?        00:00:00 [netns]
root        10     2  0 12:48 ?        00:00:00 [sync_supers]
root        11     2  0 12:48 ?        00:00:00 [bdi-default]
root        12     2  0 12:48 ?        00:00:00 [kintegrityd]
root        13     2  0 12:48 ?        00:00:00 [kblockd]
root        14     2  0 12:48 ?        00:00:00 [kacpid]
root        15     2  0 12:48 ?        00:00:00 [kacpi_notify]
root        16     2  0 12:48 ?        00:00:00 [kacpi_hotplug]
root        17     2  0 12:48 ?        00:00:00 [ata_sff]
root        18     2  0 12:48 ?        00:00:00 [khubd]
root        19     2  0 12:48 ?        00:00:00 [md]
root        22     2  0 12:48 ?        00:00:00 [khungtaskd]
root        23     2  0 12:48 ?        00:00:00 [kswapd0]
root        24     2  0 12:48 ?        00:00:00 [ksmd]
root        25     2  0 12:48 ?        00:00:00 [fsnotify_mark]
root        26     2  0 12:48 ?        00:00:00 [aio]
root        27     2  0 12:48 ?        00:00:00 [ecryptfs-kthrea]
root        28     2  0 12:48 ?        00:00:00 [crypto]
root        32     2  0 12:48 ?        00:00:00 [kthrotld]
root        34     2  0 12:48 ?        00:00:00 [scsi_eh_0]
root        35     2  0 12:48 ?        00:00:00 [scsi_eh_1]
root        36     2  0 12:48 ?        00:00:00 [kworker/u:3]
root        38     2  0 12:48 ?        00:00:00 [kworker/0:2]
root        39     2  0 12:48 ?        00:00:00 [kmpathd]
root        40     2  0 12:48 ?        00:00:00 [kmpath_handlerd]
root        41     2  0 12:48 ?        00:00:00 [kondemand]
root        42     2  0 12:48 ?        00:00:00 [kconservative]
root       192     2  0 12:48 ?        00:00:00 [mpt_poll_0]
root       193     2  0 12:48 ?        00:00:00 [mpt/0]
root       194     2  0 12:48 ?        00:00:00 [scsi_eh_2]
root       210     2  0 12:48 ?        00:00:00 [kjournald]
root       448     2  0 12:50 ?        00:00:00 [kpsmoused]
root       486     2  0 12:50 ?        00:00:00 [vmmemctl]
root       642     2  0 12:50 ?        00:00:00 [rpciod]
root       659     2  0 12:50 ?        00:00:00 [nfsiod]
root       703     2  0 12:50 ?        00:00:00 [flush-8:0]
root       876     2  0 12:50 ?        00:00:00 [lockd]
root       877     2  0 12:50 ?        00:00:00 [nfsd4]
root       878     2  0 12:50 ?        00:00:00 [nfsd4_callbacks]
root       879     2  0 12:50 ?        00:00:00 [nfsd]
root       880     2  0 12:50 ?        00:00:00 [nfsd]
root       881     2  0 12:50 ?        00:00:00 [nfsd]
root       882     2  0 12:50 ?        00:00:00 [nfsd]
root       883     2  0 12:50 ?        00:00:00 [nfsd]
root       884     2  0 12:50 ?        00:00:00 [nfsd]
root       885     2  0 12:50 ?        00:00:00 [nfsd]
root       886     2  0 12:50 ?        00:00:00 [nfsd]

    其中用來建立其它核心執行緒的函式如下:

/* linux-2.6.38.8/kernel/kthread.c */
int kthreadd(void *unused)
{
	struct task_struct *tsk = current;

	/* Setup a clean context for our children to inherit. */
	set_task_comm(tsk, "kthreadd");
	ignore_signals(tsk);
	set_cpus_allowed_ptr(tsk, cpu_all_mask);
	set_mems_allowed(node_states[N_HIGH_MEMORY]);

	current->flags |= PF_NOFREEZE | PF_FREEZER_NOSIG;

	for (;;) {
		set_current_state(TASK_INTERRUPTIBLE);
		if (list_empty(&kthread_create_list))
			schedule();
		__set_current_state(TASK_RUNNING);

		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);
			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函式通過死迴圈不斷地檢查kthread_create_list連結串列是否為空,如果不為空,則呼叫create_kthread函式來建立新的核心執行緒。

/* linux-2.6.38.8/kernel/kthread.c */
static void create_kthread(struct kthread_create_info *create)
{
	int pid;

	/* We want our own signal handler (we take no signals by default). */
	pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD);
	if (pid < 0) {
		create->result = ERR_PTR(pid);
		complete(&create->done);
	}
}

    在Linux核心中,可通過呼叫kthread_create函式來發起核心執行緒的建立。

/* linux-2.6.38.8/kernel/kthread.c */
struct task_struct *kthread_create(int (*threadfn)(void *data),
				   void *data,
				   const char namefmt[],
				   ...)
{
	struct kthread_create_info create;

	create.threadfn = threadfn;
	create.data = data;
	init_completion(&create.done);

	spin_lock(&kthread_create_lock);
	list_add_tail(&create.list, &kthread_create_list);
	spin_unlock(&kthread_create_lock);

	wake_up_process(kthreadd_task); //喚醒kthreadd來建立核心執行緒
	wait_for_completion(&create.done); //等待核心執行緒建立完畢

	if (!IS_ERR(create.result)) {
		static const struct sched_param param = { .sched_priority = 0 };
		va_list args;

		va_start(args, namefmt);
		vsnprintf(create.result->comm, sizeof(create.result->comm),
			  namefmt, args);
		va_end(args);
		/*
		 * root may have changed our (kthreadd's) priority or CPU mask.
		 * The kernel thread should not inherit these properties.
		 */
		sched_setscheduler_nocheck(create.result, SCHED_NORMAL, ¶m);
		set_cpus_allowed_ptr(create.result, cpu_all_mask);
	}
	return create.result;
}