内核线程
时间:2006-03-06 来源:ChinaE_OS
内核线程 内核线程只运行在内核态,普通进程既可以运行在内核态也可以运行在用户态。因为内核线程运行在内核态,所以只访问大于PAGE_OFFSET以上的线性地址空间,而普通进程使用所有的4G空间。
1:内核线程的创建时间: kernel_thread创建内核线程。 int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) { struct pt_regs regs;
memset(®s, 0, sizeof(regs));
regs.ebx = (unsigned long) fn; regs.edx = (unsigned long) arg;
regs.xds = __USER_DS; regs.xes = __USER_DS; regs.orig_eax = -1; regs.eip = (unsigned long) kernel_thread_helper; regs.xcs = __KERNEL_CS; regs.eflags = X86_EFLAGS_IF | X86_EFLAGS_SF | X86_EFLAGS_PF | 0x2;
/* Ok, create the new process.. */ return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); } CLONE_VM:避免复制正在运行进程的页表。 CLONE_UNTRACED:保证即使父进程正在被调试,内核线程也不会被调试。 Regs:内核栈中的一个地址,通过这个地址,可以找到内核栈CPU寄存器的初始化值。 (1) 通过fn和arg分别设置ebx和edx寄存器。 (2) kernel_thread_helper是一个汇编函数,代码如下: extern void kernel_thread_helper(void); __asm__(".section .text\n" ".align 4\n" "kernel_thread_helper:\n\t" "movl %edx,%eax\n\t" "pushl %edx\n\t" "call *%ebx\n\t" "pushl %eax\n\t" "call do_exit\n" ".previous"); 所以内核线程是通过fn函数开始的。当结束时,执行系统调用do_exit. |
|
||
14:54 | | | 固定链接 | 引用通告 (0) | 记录它 | 计算机与 Internet |
|
||||||||
Linux创建进程(3)copy_process的下半部分 |
(14) 调用sched_fork,完成新进程调度其数据结构的初始化工作。该函数设置进程的状态为TASK_RUNNING,并且设置thread_info的preempt_count为1,关系内核可抢占。同时,为了调度公平,子进程和父进程共享父进程的时间片。 Sched_fork的代码如下(kernel/sched.c): void fastcall sched_fork(task_t *p, int clone_flags) { int cpu = get_cpu();
#ifdef CONFIG_SMP cpu = sched_balance_self(cpu, SD_BALANCE_FORK); #endif set_task_cpu(p, cpu);
/* * We mark the process as running here, but have not actually * inserted it onto the runqueue yet. This guarantees that * nobody will actually run it, and a signal or other external * event cannot wake it up and insert it on the runqueue either. */ p->state = TASK_RUNNING; INIT_LIST_HEAD(&p->run_list); p->array = NULL; #ifdef CONFIG_SCHEDSTATS memset(&p->sched_info, 0, sizeof(p->sched_info)); #endif #if defined(CONFIG_SMP) && defined(__ARCH_WANT_UNLOCKED_CTXSW) p->oncpu = 0; #endif #ifdef CONFIG_PREEMPT /* Want to start with kernel preemption disabled. */ p->thread_info->preempt_count = 1; #endif /* * Share the timeslice between parent and child, thus the * total amount of pending timeslices in the system doesn't change, * resulting in more scheduling fairness. */ local_irq_disable(); p->time_slice = (current->time_slice + 1) >> 1; /* * The remainder of the first timeslice might be recovered by * the parent if the child exits early enough. */ p->first_time_slice = 1; current->time_slice >>= 1; p->timestamp = sched_clock(); if (unlikely(!current->time_slice)) { /* * This case is rare, it happens when the parent has only * a single jiffy left from its timeslice. Taking the * runqueue lock is not a problem. */ current->time_slice = 1; scheduler_tick(); } local_irq_enable(); put_cpu(); } (15) 设置新进程thread_info的CPU域为smp_processor_id()返回的当前CPU. p->cpus_allowed = current->cpus_allowed; if (unlikely(!cpu_isset(task_cpu(p), p->cpus_allowed))) set_task_cpu(p, smp_processor_id()); (16) 设置父子关系。 /* CLONE_PARENT re-uses the old parent */ if (clone_flags & (CLONE_PARENT|CLONE_THREAD)) p->real_parent = current->real_parent; else p->real_parent = current; p->parent = p->real_parent; (17) 如果设置CLONE_THREAD,子进程属于父进程所在组。 if (clone_flags & CLONE_THREAD) { spin_lock(¤t->sighand->siglock); /* * Important: if an exit-all has been started then * do not create this new thread - the whole thread * group is supposed to exit anyway. */ if (current->signal->flags & SIGNAL_GROUP_EXIT) { spin_unlock(¤t->sighand->siglock); write_unlock_irq(&tasklist_lock); retval = -EAGAIN; goto bad_fork_cleanup_namespace; } p->group_leader = current->group_leader;
if (current->signal->group_stop_count > 0) { /* * There is an all-stop in progress for the group. * We ourselves will stop as soon as we check signals. * Make the new thread part of that group stop too. */ current->signal->group_stop_count++; set_tsk_thread_flag(p, TIF_SIGPENDING); }
if (!cputime_eq(current->signal->it_virt_expires, cputime_zero) || !cputime_eq(current->signal->it_prof_expires, cputime_zero) || current->signal->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY || !list_empty(¤t->signal->cpu_timers[0]) || !list_empty(¤t->signal->cpu_timers[1]) || !list_empty(¤t->signal->cpu_timers[2])) { /* * Have child wake up on its first tick to check * for process CPU timers. */ p->it_prof_expires = jiffies_to_cputime(1); }
spin_unlock(¤t->sighand->siglock); } (18) 使用SET_LINKS宏将新进程描述符插入进程队列。如果子进程需要被跟踪,设置p->parent为current->parent,并且将子进程插入调试器跟踪队列。 SET_LINKS(p); if (unlikely(p->ptrace & PT_PTRACED)) __ptrace_link(p, current->parent); SET_LINKS宏定义如下 #define SET_LINKS(p) do { \ if (thread_group_leader(p)) \ list_add_tail(&(p)->tasks,&init_task.tasks); \ add_parent(p, (p)->parent); \ } while (0)
(19) 调用attach_pid将新进程的进程描述符的PID插入到pidhash[PIDTYPE_PID]哈西表。 attach_pid(p, PIDTYPE_PID, p->pid); attach_pid(p, PIDTYPE_TGID, p->tgid); if (thread_group_leader(p)) { attach_pid(p, PIDTYPE_PGID, process_group(p)); attach_pid(p, PIDTYPE_SID, p->signal->session); if (p->pid) __get_cpu_var(process_counts)++; } (20) 新进程加入到进程组后,nr_threads++ (21) 为了跟踪创建的进程,total_forks++ 返回子进程的task_struct. |