歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Linux 內核等待隊列解析

Linux 內核等待隊列解析

日期:2017/3/1 10:15:14   编辑:Linux編程

在閱讀Tun驅動時看到,有一些類似 add_wait_queue 的函數,這些函數正是執行等待隊列的相關操作,要說等待隊列還得從內核進程調度說起,內核調度系統內進程,分配時間片,但是有些進程如從網卡中讀數據,在網卡有數據到達之前進程處於阻塞狀態,如果此時給相應進程分配時間片做調度,無疑是浪費系統資源,所以系統內每個進程都有自己的狀態標志 task->state,這些狀態定義於文件 include/linux/sched.h

#define TASK_RUNNING            0
#define TASK_INTERRUPTIBLE      1
#define TASK_UNINTERRUPTIBLE    2
...


內核只會調用標志是 TASK_RUNNING 的進程,如果需要等待資源,可以設置自己的進程標志為 TASK_INTERRUPTIBLE (可中斷)或者 TASK_UNINTERRUPTIBLE (不可中斷),然後調用 schedule();放棄CPU,此進程就不會被內核調度了,但隨之而來的問題是,如果進程所需資源可以使用,如何喚醒進程呢,這就要依賴於等待隊列了,進程在放棄控制權之前,把自己加入一個隊列中,當所需條件滿足,其他進程便可wakeup該隊列,修改進程狀態為 TASK_RUNNING,等待進程便可順利往下執行了。

與等待隊列相關的操作有如下幾種:

/* 等待隊列頭聲明 */
wait_queue_head_t simple_queue;

/* 初始化等待隊列頭 */
init_waitqueue_head(&simple_queue);

/* 定義等待隊列項 */
DECLARE_WAITQUEUE (name, tsk);

/* 添加刪除等待項 */
void fastcall add_wait_queue(wait_queue_head_t * q, wait_queue_t * wait);
void fastcall remove_wait_queue(wait_queue_head_t * q, wait_queue_t * wait);

/* 喚醒等待頭中的所有項 */
void wake_up(wait_queue_head_t * queue);
void wake_up_interruptible(wait_queue_head_t * queue);

結合這些函數,再往simple驅動裡添加一些簡單的機制支持等待。在 simple_read 函數中修改當前進程狀態,加入等待隊列後,放棄控制權,另加入 simple_write 函數喚醒隊列。

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

#include<linux/fs.h>
#include<linux/types.h>
#include<linux/cdev.h>
#include<linux/mm.h>
#include<linux/sched.h>
#include<asm/io.h>
#include<asm/uaccess.h>
#include<asm/system.h>

#include<linux/device.h>

dev_t devno;
struct class * simple_class;
static struct cdev cdev;

wait_queue_head_t simple_queue;

char test_data[255];
int len;

ssize_t simple_read(struct file * pfile,
    char __user * buf, size_t size, loff_t * ppos)
{
    /* 定義一個等待項,添加到等待隊列,並設置進程狀態後放棄執行權 */
    DECLARE_WAITQUEUE (simple_item, current);
    add_wait_queue(&simple_queue, &simple_item);
    current->state = TASK_INTERRUPTIBLE;
    schedule();
    /* 被喚醒以後,從隊列中移除 */
    remove_wait_queue(&simple_queue, &simple_item);

    if (copy_to_user(buf, test_data, len))
        return -EFAULT;
    else
        return len;
}

ssize_t simple_write(struct file * pfile, const char __user * buf, size_t count, loff_t * ppos)
{
    if (count > 255)
    {
        return -EFAULT;
    }

    if (!copy_from_user(test_data, buf, count))
    {
        len = count;
        /* 喚醒等待隊列中所有進程 */
        wake_up(&simple_queue);
    }
    return len;
}

int simple_open(struct inode * pnode, struct file * pfile)
{
    printk(KERN_INFO "open simple\n");
    return 0;
}

int simple_release(struct inode * pnode, struct file * pfile)
{
    printk(KERN_INFO "close simple\n");
    return 0;
}

static struct file_operations simple_op =
{
    .owner = THIS_MODULE,
    .read = simple_read,
    .open = simple_open,
    .release = simple_release,
    .write = simple_write,
};

static int __init initialization(void)
{
    int result;

    result = alloc_chrdev_region(&devno, 0, 1, "simple");
    if (result < 0)
        return result;

    cdev_init(&cdev, &simple_op);
    result = cdev_add(&cdev, devno, 1);

    simple_class = class_create(THIS_MODULE, "simple");
    device_create(simple_class, NULL, devno, NULL, "simple");

    printk(KERN_INFO " init simple\n");

    init_waitqueue_head(&simple_queue);

    return result;
}

static void __exit cleanup(void)
{
    device_destroy(simple_class, devno);
    class_destroy(simple_class);

    cdev_del(&cdev);
    unregister_chrdev_region(devno, 1);
    printk(KERN_INFO " cleanup simple\n");
}

module_init(initialization);
module_exit(cleanup);

MODULE_AUTHOR("alloc [email protected]");
MODULE_DESCRIPTION("A simple linux kernel module");
MODULE_VERSION("V0.2");
MODULE_LICENSE("Dual BSD/GPL");

加載模塊 insmod simple.ko 後,使用cat讀取數據,cat /dev/simple, 會發現進程處於等待狀態,不會輸出任何信息,新打開一個終端,輸入echo “test” > /dev/simple執行寫操作,cat便會輸出數據,緊接著再次處於等待狀態。

insmod simple.ko
cat /dev/simple        --->    進程等待
first            --->    echo "first" > /dev/simple
cool            --->    echo "cool" > /dev/simple
Copyright © Linux教程網 All Rights Reserved