进程调度如何找到优先级最高的进程

以下是调度器用于判断优先级最高进程的主要代码:

  1. struct task_struct *prev, *next;
  2. struct list_head *queue;
  3. struct prio_array *array;
  4. int indx;
  5. prev = current;
  6. array = rq->active;
  7. indx = sched_find_first_bit(array->bitmap);
  8. queue = array->queue + indx;
  9. next = list_entry(queue->next,struct task_struct,run_list);

我们先看看上面提到的几个数据结构和函数。

  • struct  task_struct

 内核把进程存放在叫做任务队列的双向循环链表中。链表中的每一项都是类型为task_struct(称为进程描述符)的结构。该结构在<linux/sched.h>中定义。该结构中,包含了两个结构成员struct list_head run_list和struct prio_array *array。

      1. struct task_struct {
      2.     volatile long state;    /* -1 unrunnable, 0 runnable, >0 stopped */
      3.     struct thread_info *thread_info;
      4.     atomic_t usage;
      5.     unsigned long flags;    /* per process flags, defined below */
      6.     unsigned long ptrace;
      7.     int lock_depth;     /* BKL lock depth */
      8. #ifdef CONFIG_SMP 
      9. #ifdef __ARCH_WANT_UNLOCKED_CTXSW 
      10.     int oncpu;
      11. #endif 
      12. #endif 
      13.     int load_weight;    /* for niceness load balancing purposes */
      14.     int prio, static_prio, normal_prio;
      15.     struct list_head run_list;
      16.     struct prio_array *array;
      17.     unsigned short ioprio;
      18. #ifdef CONFIG_BLK_DEV_IO_TRACE 
      19.     unsigned int btrace_seq;
      20. #endif 
      21.     unsigned long sleep_avg;
      22.     unsigned long long timestamp, last_ran;
      23.     unsigned long long sched_time; /* sched_clock time spent running */
      24.     enum sleep_type sleep_type;
      25.     unsigned long policy;
      26.     cpumask_t cpus_allowed;
      27.     unsigned int time_slice, first_time_slice;
      28. #if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT) 
      29.     struct sched_info sched_info;
      30. #endif 
      31.     struct list_head tasks;
      32.     /*
      33.      * ptrace_list/ptrace_children forms the list of my children
      34.      * that were stolen by a ptracer.
      35.      */
      36.     struct list_head ptrace_children;
      37.     struct list_head ptrace_list;
      38.     struct mm_struct *mm, *active_mm;
      39. /* task state */
      40.     struct linux_binfmt *binfmt;
      41.     long exit_state;
      42.     int exit_code, exit_signal;
      43.     int pdeath_signal;  /*  The signal sent when the parent dies  */
      44.     /* ??? */
      45.     unsigned long personality;
      46.     unsigned did_exec:1;
      47.     pid_t pid;
      48.     pid_t tgid;
      49. #ifdef CONFIG_CC_STACKPROTECTOR 
      50.     /* Canary value for the -fstack-protector gcc feature */
      51.     unsigned long stack_canary;
      52. #endif 
      53.     /* 
      54.      * pointers to (original) parent process, youngest child, younger sibling,
      55.      * older sibling, respectively.  (p->father can be replaced with 
      56.      * p->parent->pid)
      57.      */
      58.     struct task_struct *real_parent; /* real parent process (when being debugged) */
      59.     struct task_struct *parent; /* parent process */
      60.     /*
      61.      * children/sibling forms the list of my children plus the
      62.      * tasks I'm ptracing.
      63.      */
      64.     struct list_head children;  /* list of my children */
      65.     struct list_head sibling;   /* linkage in my parent's children list */
      66.     struct task_struct *group_leader;   /* threadgroup leader */
      67.     /* PID/PID hash table linkage. */
      68.     struct pid_link pids[PIDTYPE_MAX];
      69.     struct list_head thread_group;
      70.     struct completion *vfork_done;      /* for vfork() */
      71.     int __user *set_child_tid;      /* CLONE_CHILD_SETTID */
      72.     int __user *clear_child_tid;        /* CLONE_CHILD_CLEARTID */
      73.     unsigned long rt_priority;
      74.     cputime_t utime, stime;
      75.     unsigned long nvcsw, nivcsw; /* context switch counts */
      76.     struct timespec start_time;
      77. /* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
      78.     unsigned long min_flt, maj_flt;
      79.     cputime_t it_prof_expires, it_virt_expires;
      80.     unsigned long long it_sched_expires;
      81.     struct list_head cpu_timers[3];
      82. /* process credentials */
      83.     uid_t uid,euid,suid,fsuid;
      84.     gid_t gid,egid,sgid,fsgid;
      85.     struct group_info *group_info;
      86.     kernel_cap_t   cap_effective, cap_inheritable, cap_permitted;
      87.     unsigned keep_capabilities:1;
      88.     struct user_struct *user;
      89. #ifdef CONFIG_KEYS 
      90.     struct key *request_key_auth;   /* assumed request_key authority */
      91.     struct key *thread_keyring; /* keyring private to this thread */
      92.     unsigned char jit_keyring;  /* default keyring to attach requested keys to */
      93. #endif 
      94.     /*
      95.      * fpu_counter contains the number of consecutive context switches
      96.      * that the FPU is used. If this is over a threshold, the lazy fpu
      97.      * saving becomes unlazy to save the trap. This is an unsigned char
      98.      * so that after 256 times the counter wraps and the behavior turns
      99.      * lazy again; this to deal with bursty apps that only use FPU for
      100.      * a short time
      101.      */
      102.     unsigned char fpu_counter;
      103.     int oomkilladj; /* OOM kill score adjustment (bit shift). */
      104.     char comm[TASK_COMM_LEN]; /* executable name excluding path
      105.                      - access with [gs]et_task_comm (which lock
      106.                        it with task_lock())
      107.                      - initialized normally by flush_old_exec */
      108. /* file system info */
      109.     int link_count, total_link_count;
      110. #ifdef CONFIG_SYSVIPC 
      111. /* ipc stuff */
      112.     struct sysv_sem sysvsem;
      113. #endif 
      114. /* CPU-specific state of this task */
      115.     struct thread_struct thread;
      116. /* filesystem information */
      117.     struct fs_struct *fs;
      118. /* open file information */
      119.     struct files_struct *files;
      120. /* namespaces */
      121.     struct nsproxy *nsproxy;
      122. /* signal handlers */
      123.     struct signal_struct *signal;
      124.     struct sighand_struct *sighand;
      125.     sigset_t blocked, real_blocked;
      126.     sigset_t saved_sigmask;     /* To be restored with TIF_RESTORE_SIGMASK */
      127.     struct sigpending pending;
      128.     unsigned long sas_ss_sp;
      129.     size_t sas_ss_size;
      130.     int (*notifier)(void *priv);
      131.     void *notifier_data;
      132.     sigset_t *notifier_mask;
      133.     
      134.     void *security;
      135.     struct audit_context *audit_context;
      136.     seccomp_t seccomp;
      137. /* Thread group tracking */
      138.     u32 parent_exec_id;
      139.     u32 self_exec_id;
      140. /* Protection of (de-)allocation: mm, files, fs, tty, keyrings */
      141.     spinlock_t alloc_lock;
      142.     /* Protection of the PI data structures: */
      143.     spinlock_t pi_lock;
      144. #ifdef CONFIG_RT_MUTEXES 
      145.     /* PI waiters blocked on a rt_mutex held by this task */
      146.     struct plist_head pi_waiters;
      147.     /* Deadlock detection and priority inheritance handling */
      148.     struct rt_mutex_waiter *pi_blocked_on;
      149. #endif 
      150. #ifdef CONFIG_DEBUG_MUTEXES 
      151.     /* mutex deadlock detection */
      152.     struct mutex_waiter *blocked_on;
      153. #endif 
      154. #ifdef CONFIG_TRACE_IRQFLAGS 
      155.     unsigned int irq_events;
      156.     int hardirqs_enabled;
      157.     unsigned long hardirq_enable_ip;
      158.     unsigned int hardirq_enable_event;
      159.     unsigned long hardirq_disable_ip;
      160.     unsigned int hardirq_disable_event;
      161.     int softirqs_enabled;
      162.     unsigned long softirq_disable_ip;
      163.     unsigned int softirq_disable_event;
      164.     unsigned long softirq_enable_ip;
      165.     unsigned int softirq_enable_event;
      166.     int hardirq_context;
      167.     int softirq_context;
      168. #endif 
      169. #ifdef CONFIG_LOCKDEP 
      170. # define MAX_LOCK_DEPTH 30UL 
      171.     u64 curr_chain_key;
      172.     int lockdep_depth;
      173.     struct held_lock held_locks[MAX_LOCK_DEPTH];
      174.     unsigned int lockdep_recursion;
      175. #endif 
      176. /* journalling filesystem info */
      177.     void *journal_info;
      178. /* VM state */
      179.     struct reclaim_state *reclaim_state;
      180.     struct backing_dev_info *backing_dev_info;
      181.     struct io_context *io_context;
      182.     unsigned long ptrace_message;
      183.     siginfo_t *last_siginfo; /* For ptrace use.  */
      184. /*
      185.  * current io wait handle: wait queue entry to use for io waits
      186.  * If this thread is processing aio, this points at the waitqueue
      187.  * inside the currently handled kiocb. It may be NULL (i.e. default
      188.  * to a stack based synchronous wait) if its doing sync IO.
      189.  */
      190.     wait_queue_t *io_wait;
      191. #ifdef CONFIG_TASK_XACCT 
      192. /* i/o counters(bytes read/written, #syscalls */
      193.     u64 rchar, wchar, syscr, syscw;
      194. #endif 
      195.     struct task_io_accounting ioac;
      196. #if defined(CONFIG_TASK_XACCT) 
      197.     u64 acct_rss_mem1;  /* accumulated rss usage */
      198.     u64 acct_vm_mem1;   /* accumulated virtual memory usage */
      199.     cputime_t acct_stimexpd;/* stime since last update */
      200. #endif 
      201. #ifdef CONFIG_NUMA 
      202.     struct mempolicy *mempolicy;
      203.     short il_next;
      204. #endif 
      205. #ifdef CONFIG_CPUSETS 
      206.     struct cpuset *cpuset;
      207.     nodemask_t mems_allowed;
      208.     int cpuset_mems_generation;
      209.     int cpuset_mem_spread_rotor;
      210. #endif 
      211.     struct robust_list_head __user *robust_list;
      212. #ifdef CONFIG_COMPAT 
      213.     struct compat_robust_list_head __user *compat_robust_list;
      214. #endif 
      215.     struct list_head pi_state_list;
      216.     struct futex_pi_state *pi_state_cache;
      217.     atomic_t fs_excl;   /* holding fs exclusive resources */
      218.     struct rcu_head rcu;
      219.     /*
      220.      * cache last used pipe for splice
      221.      */
      222.     struct pipe_inode_info *splice_pipe;
      223. #ifdef  CONFIG_TASK_DELAY_ACCT 
      224.     struct task_delay_info *delays;
      225. #endif 
      226. #ifdef CONFIG_FAULT_INJECTION 
      227.     int make_it_fail;
      228. #endif 
      229. };
    • struct  list_head

在Linux中list无处不在,如果定义一个结构使之成为链表,只需添加struct list_head结构作为它的一个成员就可以了。该结构定义在include/linux/list.h中。

    1. typedef struct list_head {
    2.         struct list_head *next, *prev;         //双向链表
    3. } list_t;
    4. #define LIST_HEAD_INIT(name) { &(name), &(name) }       
    5. #define LIST_HEAD(name) /
    6.         struct list_head name = LIST_HEAD_INIT(name)     //定义一个空的链表
    7. #define INIT_LIST_HEAD(ptr) do { /               //初始化一个已定义的列表
    8.         (ptr)->next = (ptr); (ptr)->prev = (ptr); /
    9. while (0)
  • sturct rq

调度程序中最基本的数据结构是运行队列(runqueue)。可执行队列定义于kernel/sched.c中,由结构runqueue表示。可执行队列是给定处理器上的可执行进程的链表,每个处理器一个。每个可投入运行的进程都唯一的归属于一个可执行队列。此外,可执行队列中还包含每个处理器的调度信息。每个运行队列都有两个优先级数组,一个活跃的,一个过期的,如下代码中画红色下划线所示。

    1. /*
    2.  * This is the main, per-CPU runqueue data structure.
    3.  *
    4.  * Locking rule: those places that want to lock multiple runqueues
    5.  * (such as the load balancing or the thread migration code), lock
    6.  * acquire operations must be ordered by ascending &runqueue.
    7.  */
    8. struct rq {
    9.     spinlock_t lock;
    10.     /*
    11.      * nr_running and cpu_load should be in the same cacheline because
    12.      * remote CPUs use both these fields when doing load calculation.
    13.      */
    14.     unsigned long nr_running;
    15.     unsigned long raw_weighted_load;
    16. #ifdef CONFIG_SMP
    17.     unsigned long cpu_load[3];
    18. #endif
    19.     unsigned long long nr_switches;
    20.     /*
    21.      * This is part of a global counter where only the total sum
    22.      * over all CPUs matters. A task can increase this counter on
    23.      * one CPU and if it got migrated afterwards it may decrease
    24.      * it on another CPU. Always updated under the runqueue lock:
    25.      */
    26.     unsigned long nr_uninterruptible;
    27.     unsigned long expired_timestamp;
    28.     /* Cached timestamp set by update_cpu_clock() */
    29.     unsigned long long most_recent_timestamp;
    30.     struct task_struct *curr, *idle;
    31.     unsigned long next_balance;
    32.     struct mm_struct *prev_mm;
    33.     struct prio_array *active, *expired, arrays[2];
    34.     int best_expired_prio;
    35.     atomic_t nr_iowait;
    36. #ifdef CONFIG_SMP
    37.     struct sched_domain *sd;
    38.     /* For active balancing */
    39.     int active_balance;
    40.     int push_cpu;
    41.     int cpu;        /* cpu of this runqueue */
    42.     struct task_struct *migration_thread;
    43.     struct list_head migration_queue;
    44. #endif
    45. #ifdef CONFIG_SCHEDSTATS
    46.     /* latency stats */
    47.     struct sched_info rq_sched_info;
    48.     /* sys_sched_yield() stats */
    49.     unsigned long yld_exp_empty;
    50.     unsigned long yld_act_empty;
    51.     unsigned long yld_both_empty;
    52.     unsigned long yld_cnt;
    53.     /* schedule() stats */
    54.     unsigned long sched_switch;
    55.     unsigned long sched_cnt;
    56.     unsigned long sched_goidle;
    57.     /* try_to_wake_up() stats */
    58.     unsigned long ttwu_cnt;
    59.     unsigned long ttwu_local;
    60. #endif
    61.     struct lock_class_key rq_lock_key;
    62. };
  • struct  prio_array

优先级数组在kernel/sched.c中定义,它是prio_array类型的结构体。优先级数组使可运行处理器的每一种优先级都包含一个相应的队列,这些队列又包含对应优先级上的可执行进程链表。优先级数组拥有一个优先级位图,当需要查找当前系统内优先级最高的可执行程序时,它可提高效率。MAX_PRIO定义了系统拥有的优先级个数,默认值是140。优先级位图数组由宏DECLARE_BITMAP产生。类型为unsigned long长整型,长32位,如果每一个代表一个优先级的话,140个优先级需要5个长整型才能表示,因此bitmap含有5个数组项,总共160位
      1. /*
      2.  * These are the runqueue data structures:
      3.  */
      4. struct prio_array {
      5.     unsigned int nr_active;
      6.     DECLARE_BITMAP(bitmap, MAX_PRIO+1); /* include 1 bit for delimiter */
      7.     struct list_head queue[MAX_PRIO];
      8. };
      9. #define DECLARE_BITMAP(name,bits) /
         unsigned long name[BITS_TO_LONGS(bits)]

      10. #define BITS_TO_LONGS(bits) /
         (((bits)+BITS_PER_LONG-1)/BITS_PER_LONG)
  • sched_find_first_bit(array->bitmap)

该函数的功能是在活动优先级数组中找到第一个被设置的位。由下面的代码可以知道,最终实现使用了汇编代码。

    1. /*
    2.  * Every architecture must define this function. It's the fastest
    3.  * way of searching a 140-bit bitmap where the first 100 bits are
    4.  * unlikely to be set. It's guaranteed that at least one of the 140
    5.  * bits is cleared.
    6.  */
    7. static inline int sched_find_first_bit(unsigned long *b)
    8. {
    9.     return find_first_bit(b, 140);
    10. }
    11. /**
    12.  * find_first_bit - find the first set bit in a memory region
    13.  * @addr: The address to start the search at
    14.  * @size: The maximum size to search
    15.  *
    16.  * Returns the bit-number of the first set bit, not the number of the byte
    17.  * containing a bit.
    18.  */
    19. long find_first_bit(const unsigned long * addr, unsigned long size)
    20. {
    21.     return __find_first_bit(addr,size);
    22. }
    23. static inline long
    24. __find_first_bit(const unsigned long * addr, unsigned long size)
    25. {
    26.     long d0, d1;
    27.     long res;
    28.     /*
    29.      * We must test the size in words, not in bits, because
    30.      * otherwise incoming sizes in the range -63..-1 will not run
    31.      * any scasq instructions, and then the flags used by the jz
    32.      * instruction will have whatever random value was in place
    33.      * before.  Nobody should call us like that, but
    34.      * find_next_bit() does when offset and size are at the same
    35.      * word and it fails to find a one itself.
    36.      */
    37.     size += 63;
    38.     size >>= 6;
    39.     if (!size)
    40.         return 0;
    41.     asm volatile(
    42.         "   repe; scasq/n"
    43.         "   jz 1f/n"
    44.         "   subq $8,%%rdi/n"
    45.         "   bsfq (%%rdi),%%rax/n"
    46.         "1: subq %[addr],%%rdi/n"
    47.         "   shlq $3,%%rdi/n"
    48.         "   addq %%rdi,%%rax"
    49.         :"=a" (res), "=&c" (d0), "=&D" (d1)
    50.         :"0" (0ULL), "1" (size), "2" (addr),
    51.          [addr] "r" (addr) : "memory");
    52.     return res;
    53. }
  • list_entry(ptr,type,member)

list_entry作用就是通过list_head型指针ptr换算成其宿主结构的起始地址,该宿主结构是type型的,

而ptr在其宿主结构中定义为member成员。定义在内核源文件include/linux/list.h中。

list_entry(queue->next,struct task_struct,run_list)分析:queue和queue->next都是list_head结构,其宿主结构是struct task_struct,在宿主结构中,包含了一个list_head结构类型变量run_list。这句代码的主要作用是通过queue->next的地址求出它所在的sturct task_struct结构的起始地址。

  1. #define hlist_entry(ptr, type, member) container_of(ptr,type,member)

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值