linux 内核中,工作队列和线程有什么区别

linux 内核中,工作队列和线程有什么区别,第1张

一句话概括:根本就是两个不同的概念,差别很大。

简单的说:

一般来说,线程是windows上的概念,windows区分进程和线程。而在linux上,统一叫进程,进程是完成某项任务所需资源的集合,同时也是linux基本的执行单元。

工作队列是一个等待被执行的任务链表,由专有的线程来调度、执行。很多linux驱动的中断下半部的实现,都是采用工作队列的方式。

作者:陈彬

链接:https://www.zhihu.com/question/20085207/answer/13927301

来源:知乎

著作权归作者所有,转载请联系作者获得授权。

struct prio_array {

    unsigned intnr_active//优先级队列数据中task个数

    unsignedlong bitmap[BITMAP_SIZE]

    structlist_head queue[MAX_PRIO]

}

#define BITMAP_SIZE ((((MAX_PRIO+1+7)/8)+sizeof(long)-1)/sizeof(long))

//内核中long型占4个字节,一个字节8位,BITMAP_SIZE的值为5

场景:将任务加入到对应优先级队列的尾部

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

}

场景:将任务从优先级队列中移除

static void dequeue_task(struct task_struct *p, prio_array_t *array)

{

    array->nr_active--

    list_del(&p->run_list)

    if (list_empty(array->queue + p->prio))

    __clear_bit(p->prio, array->bitmap)

}

场景:将任务移动到对应优先级队列的尾部

/*

* Put task to the end of the run list without the overhead of dequeue

* followed by enqueue.

*/

static void requeue_task(struct task_struct *p, prio_array_t *array)

{

    list_move_tail(&p->run_list, array->queue + p->prio)

}

场景:将任务加入到对应优先级队列的首部

static inline void enqueue_task_head(struct task_struct *p, prio_array_t *array)

{

    list_add(&p->run_list, array->queue + p->prio)

    __set_bit(p->prio, array->bitmap)

    array->nr_active++

    p->array = array

}

消息队列本质上是位于内核空间的链表,链表的每个节点都是一条消息。每一条消息都有自己的消息类型,消息类型用整数来表示,而且必须大于 0。每种类型的消息都被对应的链表所维护:

其中数字 1 表示类型为 1 的消息,数字2、3、4 类似。彩色块表示消息数据,它们被挂在对应类型的链表上。

值得注意的是,刚刚说过没有消息类型为 0 的消息,实际上,消息类型为 0 的链表记录了所有消息加入队列的顺序,其中红色箭头表示消息加入的顺序。

无论你是发送还是接收消息,消息的格式都必须按照规范来。简单的说,它一般长成下面这个样子:

所以,只要你保证首4字节(32 位 linux 下的 long)是一个整数就行了。

举个例子:

从上面可以看出,正文部分是什么数据类型都没关系,因为消息队列传递的是 2 进制数据,不一定非得是文本。

msgsnd 函数用于将数据发送到消息队列。如果该函数被信号打断,会设置 errno 为 EINTR。

参数 msqid:ipc 内核对象 id

参数 msgp:消息数据地址

参数 msgsz:消息正文部分的大小(不包含消息类型)

参数 msgflg:可选项

该值为 0:如果消息队列空间不够,msgsnd 会阻塞。

IPC_NOWAIT:直接返回,如果空间不够,会设置 errno 为 EAGIN.

返回值:0 表示成功,-1 失败并设置 errno。

msgrcv 函数从消息队列取出消息后,并将其从消息队列里删除。

参数 msqid:ipc 内核对象 id

参数 msgp:用来接收消息数据地址

参数 msgsz:消息正文部分的大小(不包含消息类型)

参数 msgtyp:指定获取哪种类型的消息

msgtyp = 0:获取消息队列中的第一条消息

msgtyp >0:获取类型为 msgtyp 的第一条消息,除非指定了 msgflg 为MSG_EXCEPT,这表示获取除了 msgtyp 类型以外的第一条消息。

msgtyp <0:获取类型 ≤|msgtyp|≤|msgtyp| 的第一条消息。

参数 msgflg:可选项。

如果为 0 表示没有消息就阻塞。

IPC_NOWAIT:如果指定类型的消息不存在就立即返回,同时设置 errno 为 ENOMSG

MSG_EXCEPT:仅用于 msgtyp >0 的情况。表示获取类型不为 msgtyp 的消息

MSG_NOERROR:如果消息数据正文内容大于 msgsz,就将消息数据截断为 msgsz

程序 msg_send 和 msg_recv 分别用于向消息队列发送数据和接收数据。

msg_send 程序定义了一个结构体 Msg,消息正文部分是结构体 Person。该程序向消息队列发送了 10 条消息。

msg_send.c

程序 msg_send 第一次运行完后,内核中的消息队列大概像下面这样:

msg_recv 程序接收一个参数,表示接收哪种类型的消息。比如./msg_recv 4 表示接收类型为 4 的消息,并打印在屏幕。

先运行 msg_send,再运行 msg_recv。

接收所有消息

接收类型为 4 的消息

获取和设置消息队列的属性

msqid:消息队列标识符

cmd:控制指令

IPC_STAT:获得msgid的消息队列头数据到buf中

IPC_SET:设置消息队列的属性,要设置的属性需先存储在buf中,可设置的属性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes

buf:消息队列管理结构体。

返回值:

成功:0

出错:-1,错误原因存于error中

EACCESS:参数cmd为IPC_STAT,确无权限读取该消息队列

EFAULT:参数buf指向无效的内存地址

EIDRM:标识符为msqid的消息队列已被删除

EINVAL:无效的参数cmd或msqid

EPERM:参数cmd为IPC_SET或IPC_RMID,却无足够的权限执行


欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/yw/5913519.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-03-08
下一篇2023-03-08

发表评论

登录后才能评论

评论列表(0条)

    保存