linux的进程组织方式

前面说从认识task_struct开始学习内核,今天又能了解里面的几个域了。

一。进程链表

每个task_struct中都有一个tasks的域来连接到进程链表上去。

struct task_struct{
  ...
  struct list_head tasks;
  ...
  char comm[TASK_COMM_LEN];//可执行程序名
  ...
};

而这个链表的头是init_task.它是0号进程的PCB,0号进程永远不会被撤销,它被静态的分配到内核数据段上。也就是Init_task的PCB是由编译器预先分配的,在程序运行的过程中一直存在,直到程序结束。

 struct task_struct init_task = INIT_TASK(init_task);


可以编写以下内核模块,得到所有进程的pid和进程名,并统计出进程总数。

#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/module.h>
#include<linux/sched.h>

MODULE_LICENSE("GPL");

static int __init print_pid_init(void)
{
	struct task_struct *task,*p;
	struct list_head *pos;
	int count = 0;
	printk("Begin to print process :\n");
	task = &init_task;
	list_for_each(pos,&task->tasks)
	{
		p = list_entry(pos,struct task_struct,tasks);
                count++;
		printk("%d ======> %s\n",p->pid,p->comm);
	}
	printk("the number of process is %d\n",count);
	return 0;
}
static void __exit print_pid_exit(void)
{
	printk("End to print process.\n");
}
module_init(print_pid_init);
module_exit(print_pid_exit);
二。哈希表

由于进程链表是将所有的进程连接到一个链表上去,所以查找一个进程的时间复杂度是O(N),是相当的低效。为此,使用哈希表来提高查找的效率。

1.哈希表的定义

我是在2.6.32.27内核中找到的,较老的版本可能是一个数组。

static struct hlist_head *pid_hash;

2.哈希函数

学过数据结构的应该知道,哈希函数对整个查找是至关重要的,它决定了发生冲突的概率。好的哈希函数能够得到减少冲突。

#define pid_hashfn(nr, ns)      \
        hash_long((unsigned long)nr + (unsigned long)ns, pidhash_shift)

 #define hash_long(val, bits) hash_32(val, bits)

static inline u32 hash_32(u32 val, unsigned int bits)
{
        /* On some cpus multiply is faster, on others gcc will do shifts */
        u32 hash = val * GOLDEN_RATIO_PRIME_32;

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

/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */
#define GOLDEN_RATIO_PRIME_32 0x9e370001UL
3.通过pid查找task_struct

内核并不能直接通过pid找到对应的task_struct,而是先通过pid找到对应的struct pid,在通过struct pid 找到对应的task_struct。

下面是详细介绍这两个的链接:

i,pid到struct pid内核函数详解

ii.struct pid 到task_struct的内核函数详解

三。就绪队列

与进程链表类似,task_struct也定义了一个连接到就绪队列的域run_list

struct sched_rt_entity {
        struct list_head run_list;
        ....
};
struct task_struct
{ 
        ....
        struct sched_rt_entity rt;
        ......
};

就绪队列头:

同样,内核中有一个就绪队列头runqueue_head

四.等待队列

1.等待队列的数据结构

typedef struct __wait_queue wait_queue_t;
struct __wait_queue {
        unsigned int flags;
#define WQ_FLAG_EXCLUSIVE       0x01
        void *private;
        wait_queue_func_t func;
        struct list_head task_list;
};
task_list链接到等待队列上去。

func是一个函数指针,指向唤醒等待队列中进程的函数。prviate是传递给func的参数,用于指定所要唤醒的进程。

typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flags, void *key);
flags标志进程是否互斥: flags为WQ_FLAG_EXCLUSIVE  时互斥,否则,非互斥。

2.等待队列头

struct __wait_queue_head {
        spinlock_t lock;
        struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;

因为等待队列是由中断处理程序和主要的内核函数修改的,因此要避免被同时访问。lock自旋锁对其进行同步,避免了双向链表被同时访问。task_list是双向链表的节点。

3.等待队列的操作

i.初始化

首先,使用了下面的宏声明并初始化了一个等待队列头

#define __WAIT_QUEUE_HEAD_INITIALIZER(name) {                           \
        .lock           = __SPIN_LOCK_UNLOCKED(name.lock),              \
        .task_list      = { &(name).task_list, &(name).task_list } }

#define DECLARE_WAIT_QUEUE_HEAD(name) \
        wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)
如果要对列中一个元素初始化,要使用这个函数:

static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p)
{
        q->flags = 0;
        q->private = p;
        q->func = default_wake_function;//default_wake_function能够唤醒睡眠的进程p,并将其从等待队列中删除。
}
ii.插入/删除

add_wait_queue()将一个非互斥进程插入到等待队列的第一个位置

void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
{
        unsigned long flags;

        wait->flags &= ~WQ_FLAG_EXCLUSIVE;
        spin_lock_irqsave(&q->lock, flags);
        __add_wait_queue(q, wait);
        spin_unlock_irqrestore(&q->lock, flags);
}

add_wait_queue_exclusive()将一个互斥进程插入到等待队列的最后一个位置

void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait)
{
        unsigned long flags;

        wait->flags |= WQ_FLAG_EXCLUSIVE;
        spin_lock_irqsave(&q->lock, flags);
        __add_wait_queue_tail(q, wait);
        spin_unlock_irqrestore(&q->lock, flags);
}

之所以将非互斥进程放在队首,而将互斥进程放在队尾,大概是因为非互斥进程不需要临界资源,将它唤醒不会影响其它进程的执行,而互斥进程得到临界资源会导致别的进程无法执行。

remove_wait_queue()将一个进程从等待队列中删除。

void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
{
        unsigned long flags;

        spin_lock_irqsave(&q->lock, flags);
        __remove_wait_queue(q, wait);
        spin_unlock_irqrestore(&q->lock, flags);
}
iii.检查是否为空队列,直接调用了list.h中的list_empty()

static inline int waitqueue_active(wait_queue_head_t *q)
{               
        return !list_empty(&q->task_list);
}
iv.睡眠

sleep_on_common(wait_queue_head_t *q, int state, long timeout)
{
        unsigned long flags;
        wait_queue_t wait; 

        init_waitqueue_entry(&wait, current);

        __set_current_state(state);

        spin_lock_irqsave(&q->lock, flags);
        __add_wait_queue(q, &wait);
        spin_unlock(&q->lock);
        timeout = schedule_timeout(timeout);
        spin_lock_irq(&q->lock);
        __remove_wait_queue(q, &wait);
        spin_unlock_irqrestore(&q->lock, flags);

        return timeout;
}

v.唤醒

static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
                        int nr_exclusive, int wake_flags, void *key)
{
        wait_queue_t *curr, *next;

        list_for_each_entry_safe(curr, next, &q->task_list, task_list) {
                unsigned flags = curr->flags;

                if (curr->func(curr, mode, wake_flags, key) &&
                                (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
                        break;
        }
}


《----------------------完结------------》





  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值