注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

pthread_create函数对参数的向下传递方法  

2011-05-07 22:12:03|  分类: Linux内核 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

一、父进程设置子进程的优先级

父进程通过fork设置子进程的优先级,但是这里有一个问题,就是子进程如何和父进程交互的,或者说父进程如何可以修改子进程的参数,又如何保证子进程和父进程之间可能存在的竞争关系?

或者进一步说,当子进程设置了和父进程不同的调度策略和优先级的时候,子进程和父进程是如何确定API返回之后哪个线程优先运行呢?

二、Linux下NPTL库的实现

首先父进程通过pthread_create来声明要创建一个子进程,在这个调用之前,父进程已经通过一些接口设置了子进程的优先级或者调度策略等pthread_attr_setXXX属性,现在需要开始真正的创建一个子进程。

__pthread_create_2_1

 

  /* Copy the parent's scheduling parameters.  The flags will say what
     is valid and what is not.  */
  pd->schedpolicy = self->schedpolicy;
  pd->schedparam = self->schedparam;

  /* Copy the stack guard canary.  */
#ifdef THREAD_COPY_STACK_GUARD
  THREAD_COPY_STACK_GUARD (pd);
#endif

  /* Copy the pointer guard value.  */
#ifdef THREAD_COPY_POINTER_GUARD
  THREAD_COPY_POINTER_GUARD (pd);
#endif

  /* Determine scheduling parameters for the thread.  */
  if (attr != NULL
      && __builtin_expect ((iattr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0, 0)
      && (iattr->flags & (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)) != 0)
    {
      INTERNAL_SYSCALL_DECL (scerr);

      /* Use the scheduling parameters the user provided.  */
      if (iattr->flags & ATTR_FLAG_POLICY_SET)
 pd->schedpolicy = iattr->schedpolicy;
      else if ((pd->flags & ATTR_FLAG_POLICY_SET) == 0)
 {
   pd->schedpolicy = INTERNAL_SYSCALL (sched_getscheduler, scerr, 1, 0);
   pd->flags |= ATTR_FLAG_POLICY_SET;
 }

      if (iattr->flags & ATTR_FLAG_SCHED_SET)
 memcpy (&pd->schedparam, &iattr->schedparam,
  sizeof (struct sched_param));
      else if ((pd->flags & ATTR_FLAG_SCHED_SET) == 0)
 {
   INTERNAL_SYSCALL (sched_getparam, scerr, 2, 0, &pd->schedparam);
   pd->flags |= ATTR_FLAG_SCHED_SET;
 }

      /* Check for valid priorities.  */
      int minprio = INTERNAL_SYSCALL (sched_get_priority_min, scerr, 1,
          iattr->schedpolicy);
      int maxprio = INTERNAL_SYSCALL (sched_get_priority_max, scerr, 1,
          iattr->schedpolicy);
      if (pd->schedparam.sched_priority < minprio
   || pd->schedparam.sched_priority > maxprio)
 {
   err = EINVAL;
   goto errout;
 }
    }

 这里默认使用父进程的调度策略和调度参数。

pthread_create-->>create_thread

  /* Determine whether the newly created threads has to be started
     stopped since we have to set the scheduling parameters or set the
     affinity.  */
  bool stopped = false;
  if (attr != NULL && (attr->cpuset != NULL
         || (attr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0))
    stopped = true;

而在pthread_attr_setinheritsched函数中,我们是的确设置了这个属性了的:

 

int
__pthread_attr_setinheritsched (attr, inherit)
     pthread_attr_t *attr;
     int inherit;
{
  struct pthread_attr *iattr;

  assert (sizeof (*attr) >= sizeof (struct pthread_attr));
  iattr = (struct pthread_attr *) attr;

  /* Catch invalid values.  */
  if (inherit != PTHREAD_INHERIT_SCHED && inherit != PTHREAD_EXPLICIT_SCHED)
    return EINVAL;

  /* Store the new values.  */
  if (inherit != PTHREAD_INHERIT_SCHED)
    iattr->flags |= ATTR_FLAG_NOTINHERITSCHED;
  else
    iattr->flags &= ~ATTR_FLAG_NOTINHERITSCHED;

  return 0;
}
然后进入do_clone函数

  if (stopped)
    /* We Make sure the thread does not run far by forcing it to get a
       lock.  We lock it here too so that the new thread cannot continue
       until we tell it to.  */
    lll_lock (pd->lock);

父进程优先获得了这个锁,所以子进程是无法获得这个锁的,当然是如果子进程来获得这个锁的话。这里也比较有意思,它获得的是子进程控制结构里的锁,从而不同的子进程之间就不会相互干扰。

然后再ARCH_CLONE函数之后会执行子进程的属性的设置

/* Now we have the possibility to set scheduling parameters etc.  */
  if (__builtin_expect (stopped != 0, 0))
    {
      INTERNAL_SYSCALL_DECL (err);
      int res = 0;

      /* Set the affinity mask if necessary.  */
      if (attr->cpuset != NULL)
 {
   res = INTERNAL_SYSCALL (sched_setaffinity, err, 3, pd->tid,
      sizeof (cpu_set_t), attr->cpuset);

   if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0))
     {
       /* The operation failed.  We have to kill the thread.  First
   send it the cancellation signal.  */
       INTERNAL_SYSCALL_DECL (err2);
     err_out:
#if __ASSUME_TGKILL
       (void) INTERNAL_SYSCALL (tgkill, err2, 3,
           THREAD_GETMEM (THREAD_SELF, pid),
           pd->tid, SIGCANCEL);
#else
       (void) INTERNAL_SYSCALL (tkill, err2, 2, pd->tid, SIGCANCEL);
#endif

       return (INTERNAL_SYSCALL_ERROR_P (res, err)
        ? INTERNAL_SYSCALL_ERRNO (res, err)
        : 0);
     }
 }

      /* Set the scheduling parameters.  */
      if ((attr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0)
 {
   res = INTERNAL_SYSCALL (sched_setscheduler, err, 3, pd->tid,
      pd->schedpolicy, &pd->schedparam);

   if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0))
     goto err_out;
 }
    }
要注意的是,这里的函数都是在父进程中执行的,此时和子进程没有任何关系

三、子进程方面

在do_clone接口中,所有的线程的入口都是使用的start_thread函数,而这个函数则负责真正调用用户提供的入口函数,也可以说,start_thread函数才是在子进程山下文中执行的代码流

然后在子进程中对同步锁信号的处理函数为

  int not_first_call;
  not_first_call = setjmp ((struct __jmp_buf_tag *) unwind_buf.cancel_jmp_buf);
  if (__builtin_expect (! not_first_call, 1))
    {
      /* Store the new cleanup handler info.  */
      THREAD_SETMEM (pd, cleanup_jmp_buf, &unwind_buf);

      if (__builtin_expect (pd->stopped_start, 0))
 {
   int oldtype = CANCEL_ASYNC ();

   /* Get the lock the parent locked to force synchronization.  */
   lll_lock (pd->lock);
   /* And give it up right away.  */
   lll_unlock (pd->lock); 这里由于父进程已经通过pthread_lock获得了互斥锁,所以这里子进程将会阻塞,直到父进程设置好了子进程的属性并通过unlock将子进程唤醒,子进程获得运行之后马上会再次释放这个互斥锁

   CANCEL_RESET (oldtype);
 }
四、内核操作

sched_setscheduler-->>do_sched_setscheduler--->>sched_setscheduler--->>__sched_setscheduler--->>>>check_class_changed

check_class_changed

static void prio_changed_rt(struct rq *rq, struct task_struct *p,
       int oldprio, int running)
{

 


/*
 * Priority of the task has changed. This may cause
 * us to initiate a push or pull.
 */
static void prio_changed_rt(struct rq *rq, struct task_struct *p,
       int oldprio, int running)
{
 if (running) {
#ifdef CONFIG_SMP
  /*
   * If our priority decreases while running, we
   * may need to pull tasks to this runqueue.
   */
  if (oldprio < p->prio)
   pull_rt_task(rq);
  /*
   * If there's a higher priority task waiting to run
   * then reschedule. Note, the above pull_rt_task
   * can release the rq lock and p could migrate.
   * Only reschedule if p is still on the same runqueue.
   */
  if (p->prio > rq->rt.highest_prio.curr && rq->curr == p)
   resched_task(p);
#else
  /* For UP simply resched on drop of prio */
  if (oldprio < p->prio)
   resched_task(p);
#endif /* CONFIG_SMP */
 } else {
  /*
   * This task is not running, but if it is
   * greater than the current running task
   * then reschedule
.
   */
  if (p->prio < rq->curr->prio)
   resched_task(rq->curr);
 }
}

  评论这张
 
阅读(1640)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017