Chinaunix首页 | 论坛 | 博客
  • 博客访问: 38443
  • 博文数量: 23
  • 博客积分: 1065
  • 博客等级: 少尉
  • 技术积分: 185
  • 用 户 组: 普通用户
  • 注册时间: 2006-11-14 15:32
文章分类

全部博文(23)

文章存档

2010年(20)

2008年(3)

我的朋友
最近访客

分类: LINUX

2010-03-01 23:58:57

创建内核线程的时候,由于内核线程没有用户空间,而所有进程的内核页目录都是一样的(某些情况下可能有不同步的情况出现,主要是为了减轻同步所有进程内核 页目录的开销,而只是在各个进程要访问内核空间,如果有不同步的情况,然后才进行同步处理),所以创建的内核线程的
内核页目录总是借用进程0的内核页目录。

>>> kernel_thread以标志CLONE_VM调用clone系统调用
/*
 * Create a kernel thread
 */
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
 long retval, d0;

 __asm__ __volatile__(
  "movl %%esp,%%esi\n\t"
  "int $0x80\n\t"  /* Linux/i386 system call */
  "cmpl %%esp,%%esi\n\t" /* child or parent? */
  "je 1f\n\t"  /* parent - jump */
  /* Load the argument into eax, and push it.  That way, it does
   * not matter whether the called function is compiled with
   * -mregparm or not.  */
  "movl %4,%%eax\n\t"
  "pushl %%eax\n\t"  
  "call *%5\n\t"  /* call fn */
  "movl %3,%0\n\t" /* exit */
  "int $0x80\n"
  "1:\t"
  :"=&a" (retval), "=&S" (d0)
  :"0" (__NR_clone), "i" (__NR_exit),
   "r" (arg), "r" (fn),
   "b" (flags | CLONE_VM)
  : "memory");
 return retval;
}

>>> sys_clone->do_fork->copy_mm:
static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
{
 struct mm_struct * mm, *oldmm;
 int retval;

 。。。。。。。。

 tsk->mm = NULL;
 tsk->active_mm = NULL;

 /*
  * Are we cloning a kernel thread?
  *
  * We need to steal a active VM for that..
  */
>>> 如果是内核线程的子线程(mm=NULL),则直接退出,此时内核线程mm和active_mm均为NULL ?
 oldmm = current->mm;
 if (!oldmm)
  return 0;

>>> 内核线程,只是增加当前进程的虚拟空间的引用计数
 if (clone_flags & CLONE_VM) {
  atomic_inc(&oldmm->mm_users);
  mm = oldmm;
  goto good_mm;
 }

 。。。。。。。。。。

good_mm:
>>> 内核线程的mm和active_mm指向当前进程的mm_struct结构
 tsk->mm = mm;
 tsk->active_mm = mm;
 return 0;

 。。。。。。。
}

然后内核线程一般调用daemonize来释放对用户空间的引用:
>>> daemonize->exit_mm->_exit_mm:
/*
 * Turn us into a lazy TLB process if we
 * aren't already..
 */
static inline void __exit_mm(struct task_struct * tsk)
{
 struct mm_struct * mm = tsk->mm;

 mm_release();
 if (mm) {
  atomic_inc(&mm->mm_count);
  if (mm != tsk->active_mm) BUG();
  /* more a memory barrier than a real lock */
  task_lock(tsk);
>>> 释放用户虚拟空间的数据结构
  tsk->mm = NULL;
  task_unlock(tsk);
  enter_lazy_tlb(mm, current, smp_processor_id());

>>> 递减mm的引用计数并是否为0,是则释放mm所代表的映射
  mmput(mm);
 }
}

asmlinkage void schedule(void)
{
 。。。。。。。。。
 if (!current->active_mm) BUG();

 。。。。。。。。。

prepare_to_switch();
 {
  struct mm_struct *mm = next->mm;
  struct mm_struct *oldmm = prev->active_mm;
>>> mm = NULL,选中的为内核线程
  if (!mm) {
>>> 对内核线程,active_mm = NULL,否则一定是出错了
   if (next->active_mm) BUG();
>>> 选中的内核线程active_mm借用老进程的active_mm
   next->active_mm = oldmm;
   atomic_inc(&oldmm->mm_count);
   enter_lazy_tlb(oldmm, next, this_cpu);
  } else {
>>> mm != NULL 选中的为用户进程,active_mm必须与mm相等,否则一定是出错了
   if (next->active_mm != mm) BUG();
   switch_mm(oldmm, mm, next, this_cpu);
  }

>>> prev = NULL ,切换出去的是内核线程
  if (!prev->mm) {
>>> 设置其 active_mm = NULL 。
   prev->active_mm = NULL;
   mmdrop(oldmm);
  }
 }

}

对内核线程的虚拟空间总结一下:
1、创建的时候:
 父进程是用户进程,则mm和active_mm均共享父进程的,然后内核线程一般调用daemonize释放mm
 父进程是内核线程,则mm和active_mm均为NULL
总之,内核线程的mm = NULL;进程调度的时候以此为依据判断是用户进程还是内核线程。

2、进程调度的时候
 如果切换进来的是内核线程,则置active_mm为切换出去的进程的active_mm;
 如果切换出去的是内核线程,则置active_mm为NULL。
阅读(443) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~