linux 2.6源代码情景分析笔记之进程3

为内核寻找新进程在cpu上运行时,必须只考虑可运行进程(TASK_RUNNING)。
提高调度程序运行速度的诀窍是建立多个可运行进程链表,每种进程优先权对应一个不同的链表。每个task_struct描述符包含一个list_head类型的字段run_list。如果进程的优先权等于k(0-139),run_list字段把该进程链入优先权为k的可运行进程的链表中。
内核必须为系统中每个运行队列保存大量的数据,不过运行队列的主要结构还是组成运行队列的进程描述符表,所有这些链表都由一个单独的prio_array_t数据结构来实现。

185 struct prio_array {
186         unsigned int nr_active;             链表中进程描述符的数量
187         unsigned long bitmap[BITMAP_SIZE];  优先权位图:当且仅当某个优先权的进程链表不为空时设置相应的位标志
188         struct list_head queue[MAX_PRIO];   140个优先权队列的头结点
189 };

static void enqueue_task(struct task_struct *p, prio_array_t *array)
{
        sched_info_queued(p);
        list_add_tail(&p->run_list, array->queue + p->prio);
        __set_bit(p->prio, array->bitmap);
        array->nr_active++;
        p->array = array;
}
此函数将进程描述符插入某个运行队列链表。于此函数对应的是dequeue_task(p,array)
进程描述符的prio字段存放进程的动态优先权,而array字段则是一个指针,指向当前运行队列的prio_array_t数据结构。

程序创建的进程具有父/子关系。如果一个进程创建多个子进程时,则子进程之间具有兄弟关系。进程0和进程1是由内核创建的。进程1(init)是所有进程的祖先。

real_parent:指向创建了p进程的描述符,如果p的父进程不再存在,就指向进程1(init)的描述符(如果用户运行一个后台进程而且退出了shell,后台进程就会成为init的子进程)
parent:指向p的当前父进程(这种进程的子进程终止时,必须向父进程发信号)。它的值通常与real_parent一致,但偶尔也可以不同。
children:链表的头部,链表中的所有元素都是p创建的子进程。
sibling:指向兄弟进程链表中的下一个元素或前一个元素的指针,这些兄弟进程的父进程都是p.

group_leader:p所在的进程组的领头进程的描述符指针
singal->pgrp:p所在进程组的领头进程的pid
tgid:p所在线程组的领头进程的pid
signal->session:p的登录绘话领头进程的pid
ptrace_children:链表的头,该链表包含所有被debugger程序跟踪的p的子进程
ptrace_list:指向所跟踪进程其实际父进程链表的前一个和下一个元素(用于p被跟踪时)

有些时候,内核必须能从进程的pid导出对应的进程描述符指针。为了查找方便,引入了四个散列表。需要四个散列表是因为进程描述符包含了表示不同类型pid的字段,而且每种类型的pid需要它自己的散列表。对应关系如下:
PIDTYPE_PID           pid 进程的pid
PIDTYPE_TGID          tgid线程组领头进程的pid
PIDTYPE_PGID          pgrp进程组领头进程的pid
PIDTYPE_SID           session会话领头进程的pid

内核初始化期间动态为四个散列表分配空间,并把它们的地址存入pid_hash数组。一个散列表的长度依赖于可用ram的容量(系统拥有512mb的ram,那么每个散列表就被存在4个页框中,可以拥有2048个表项)

#define pid_hashfn(nr) hash_long((unsigned long)nr, pidhash_shift)
static inline unsigned long hash_long(unsigned long val, unsigned int bits)
{
        unsigned long hash = val;

#if BITS_PER_LONG == 64
        /*  Sigh, gcc can't optimise this alone like it does for 32 bits. */
        unsigned long n = hash;
        n <<= 18;
        hash -= n;
        n <<= 33;
        hash -= n;
        n <<= 3;
        hash += n;
        n <<= 3;
        hash -= n;
        n <<= 4;
        hash += n;
        n <<= 2;
        hash += n;
#else
        /* On some cpus multiply is faster, on others gcc will do shifts */
        hash *= GOLDEN_RATIO_PRIME;
#endif

        /* High bits are more random, so use them. */
        return hash >> (BITS_PER_LONG - bits);
}

#if BITS_PER_LONG == 32
/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */
#define GOLDEN_RATIO_PRIME 0x9e370001UL
#elif BITS_PER_LONG == 64
/*  2^63 + 2^61 - 2^57 + 2^54 - 2^51 - 2^18 + 1 */
#define GOLDEN_RATIO_PRIME 0x9e37fffffffc0001UL
#else
#error Define GOLDEN_RATIO_PRIME for your wordsize.
#endif

pid_hashfn宏的作用是把pid转化为表索引。pidhash_shift用来存放表索引长度(以位为单位长度,在例子中是11位)如果pidhash_shift等于11,2^11,那么pid_hashfn范围就是0-2^11-1=2047.

0x9e370001UL的由来:基于表索引乘以一个适当的大数,于是结果溢出,就把留在32位变量中的值作为模数操作的结果。knuth建议,要得到满意的结果,这个大乘数就应当是接近黄金比例的2^32的一个素数(32位是80x86寄存器的大小)。
2654 404 609就是接近2^32*(5开方-1)/2的一个素数,这个数可以方便地通过加运算和位移运算得到,因为等于:2^31+2^29-2^25+2^22-2^19-2^16+1.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Linux内核版本2.6是一个较早的版本,对于SPI总线的支持相对较为简单。SPI(Serial Peripheral Interface)是一种同步的全双工串行通信总线,可用于连接微控制器与外围设备,例如传感器、存储器等。 在Linux 2.6版本中,对于SPI总线的驱动代码主要涉及以下几个方面: 1. 初始化SPI硬件:SPI总线的初始化包括选择SPI设备、配置SPI寄存器、设置传输速率、设置SPI模式等。这通常在驱动模块的probe函数中完成。 2. 定义SPI设备:在驱动代码中,需要定义SPI设备的一些基本信息,如设备名称、ID、SPI模式、位宽等。这些信息在驱动模块中进行定义,并通过spi_register_driver函数进行注册。 3. 数据传输:SPI驱动代码需要实现与SPI设备的数据传输功能,包括发送数据和接收数据。对于发送数据,驱动代码通过spi_write函数将数据发送给SPI设备;对于接收数据,驱动代码通过spi_read函数从SPI设备接收数据。 4. 中断处理:SPI驱动代码可以通过中断处理函数来处理SPI设备发生的中断事件,以便实时响应设备的数据传输情况。中断处理函数通常与硬件平台相关,需要根据具体的硬件平台进行编写。 5. 设备节点的创建和注册:驱动代码需要创建设备节点,并通过sysfs接口将设备节点注册到/sys/class/spi目录下,以提供给用户空间进行访问和控制。 需要注意的是,以上是对于Linux 2.6版本中SPI驱动代码大致的介绍,实际的驱动代码会根据具体的硬件平台和需求进行适配和实现。对于较新的Linux内核版本,SPI驱动代码的实现可能会有所不同,更加灵活和强大。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值