等待队列、工作队列、消息队列的区别和用法

目录

1.等待队列

2.工作队列

3.消息队列


1.等待队列

等待队列是一种实现阻塞和唤醒的内核机制,与进程调度机制紧密结合,能够用于实现内核中的异步事件通知机制。可以实现线程的软等待,当符合条件时从另一个线程实现软唤醒。
使用方法:
1.定义等待队列  static DECLARE_WAIT_QUEUE_HEAD(wq) 
2.定义唤醒标志  static int w_flag = 0; //当且仅当w_flag为真,才唤醒队列
3.在需要阻塞的地方调用wait_event_interruptible(wq,w_flag )函数,使线程进入睡眠
4.在另一个线程(或当前线程的中断函数),先w_flag=1;再wake_up_interruptible(&wq)函数,即可唤醒
扩展:
1.等待队列采用先入后出进制,类似于栈(stack)。

2.工作队列

工作队列(workqueue)是另外一种将工作推后执行的形式.工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。最重要的就是工作队列允许被重新调度甚至是睡眠。工作队列的使用又分两种情况,一种是利用系统共享的工作队列来添加自己的工作,这种情况处理函数不能消耗太多时间,这样会影响共享队列中其他任务的处理,尤其不能长时间休眠;另外一种是创建自己的工作队列并添加工作。
(一)利用系统共享的工作队列添加工作:
第一步:声明或编写一个工作处理函数
void my_func();


第二步:创建一个工作结构体变量,并将处理函数和参数的入口地址赋给这个工作结构体变量
DECLARE_WORK(my_work,my_func,&data); //编译时创建名为my_work的结构体变量并把函数入口地址和参数地址赋给它;


如果不想要在编译时就用DECLARE_WORK()创建并初始化工作结构体变量,也可以在程序运行时再用INIT_WORK()创建
struct work_struct my_work; //创建一个名为my_work的结构体变量,创建后才能使用INIT_WORK()
INIT_WORK(&my_work,my_func,&data); //初始化已经创建的my_work,其实就是往这个结构体变量中添加处理函数的入口地址和data的地址,通常在驱动的open函数中完成


第三步:将工作结构体变量添加入系统的共享工作队列
schedule_work(&my_work); //添加入队列的工作完成后会自动从队列中删除
my_work马上就会被调度,一旦其所在的处理器上的工作者线程被唤醒,它就会被执行。

有时候并不希望工作马上就被执行,而是希望它经过一段延迟以后再执行。在这种情况下,可以调度它在指定的时间执行:
schedule_delayed_work(&my_work,tick); //延时tick个滴答后再提交工作
这时,&my_work指向的work_struct直到delay指定的时钟节拍用完以后才会执行。

1,定义工作结构体类型和一个指向工作队列的指针
struct akm8976_data {
struct input_dev *input_dev;
struct work_struct work;
};//定义结构类型
static DECLARE_WAIT_QUEUE_HEAD(data_ready_wq);
init_waitqueue_head(&data_ready_wq);

2,声明或编写一个工作处理函数
static void akm_work_func(struct work_struct *work)
{
if (AKECS_GetData() < 0)
printk(KERN_ERR "AKM8976 akm_work_func: Get data failed/n");
enable_irq(this_client->irq);
}
//处理函数的实现,唤醒工作队列

3,初始化已经创建的工作结构体变量akm->work
static int akm8976_probe(
struct i2c_client *client, const struct i2c_device_id *id)
{;
struct akm8976_data *akm; //定义结构变量akm
;
INIT_WORK(&akm->work, akm_work_func); //初始化已经创建的工作结构体变量akm->work,其实就是往这个结构体变量中添加处理函数的入口
//为akm_work_func
;
}

4,将工作data->work添加入自己创建的工作队列等待系统默认的工作者线程events执行

static irqreturn_t akm8976_interrupt(int irq, void *dev_id)
{
struct akm8976_data *data = dev_id;
disable_irq(this_client->irq);
schedule_work(&data->work);
return IRQ_HANDLED;
}

//硬件中断处理函数,schedule_work(&data->work); //data->work马上就会被调度,一旦其所在的处理器上的工作者线程被唤醒,它就会被执行。

工作队列的使用和调度方式跟tasklet非常相似,都通过schedule调度。但是,tasklet不能多核执行,最好不要多进程使用。tasklet是自带同步的softirq

3.消息队列

消息队列,本质上是位于内核空间的链表,链表的每个节点都是一条消息。每一条消息都有自己的消息类型,消息类型用整数来表示,而且必须大于 0。将消息写入消息队列,然后再从消息队列中取消息,一般来说是先进先出的顺序,实际应用可选择。主要用于两个进程之间的通信。进程终止或崩溃时,消息队列及其内容并不会被删除
使用方法:
ftok函数生成键值

msgget函数创建消息队列

msgsnd函数往消息队列发送消息

msgrcv函数从消息队列读取消息

msgctl函数进行删除消息队列

头文件:
#include <sys/msg.h>
#include<sys/ipc.h>

消息队列的相关函数:

// 生成键值,成功返回键值(相当于32位的int)。出错返回-1
key_t ftok(const char *path ,int id);
// 创建或打开消息队列,成功返回队列ID,失败返回-1
int msgget(key_t key, int flags);

// 将消息发送到消息队列,成功返回0,失败返回-1
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
// 从消息队列获取消息,成功返回消息数据的长度,失败返回-1
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
// 查看、设置、删除 ipc 内核对象(用法和 shmctl 一样),成功返回0,失败返回-1
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

msgget用法:
key = msgget((key_t)1234,IPC_CREAT | 0666);

在以下两种情况下,msgget将创建一个新的消息队列:
1.如果没有与键值key相对应的消息队列,并且flag中包含了IPC_CREAT标志位。
2.key参数为IPC_PRIVATE。

ftok 用法:
key_t key = ftok( "/tmp", 100);
参数 *path 为已知文件/tmp ,参数id取值0-255

msgsnd 用法:
int res = msgsnd(key, &msg, sizeof(Person), 0);
参数 key为msgget的key,&msg消息的结构体指针,Person为消息的正文

msgrcv用法:
res = msgrcv(key, &msg, sizeof(Person), type, IPC_NOWAIT);
参数 key为msgget的key,&msg消息的结构体指针,Person为消息的正文,参数type如下:
type == 0,返回队列中的第一个消息;
type > 0,返回队列中消息类型为 type 的第一个消息;
type < 0,返回队列中消息类型值小于或等于 type 绝对值的消息,如果有多个,则取类型值最小的消息。
可以看出,type值非 0 时用于以非先进先出次序读消息。也可以把 type 看做优先级的权值

msgctl用法:
res = msgctl(id,IPC_RMID,NULL);

#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>

// 用于创建一个唯一的key
//#define MSG_FILE "/etc/passwd"
#define MSG_FILE "/boot"

int bufsz = 256;   //size_t
int sendmsgtyp = 999; //long 
int revmsgtyp = 888; //long 

// 消息结构
struct msg_form {
    long mtype;
    char mtext[256];
};

int main()
{
    int msqid;
    key_t key;
    struct msg_form msg;

    // 获取key值
    if ((key = ftok(MSG_FILE, 'z')) < 0)
    {
        perror("ftok error");
        exit(1);
    }

    // 打印key值
    printf("Message Queue - Server key is: %d.\n", key);

    // 创建消息队列
    if ((msqid = msgget(key, IPC_CREAT | 0777)) == -1)
    {
        perror("msgget error");
        exit(1);
    }

    // 打印消息队列ID及进程ID
    printf("Message Queue ID  is: %d.\n", msqid);
    printf("Pid is: %d.\n", getpid());

    // 循环读取消息
    for (;;)
    {
        msgrcv(msqid, &msg, bufsz, revmsgtyp, 0);// 返回类型为888的第一个消息
        printf("Server: receive msg.mtext is: %s.\n", msg.mtext);
        printf("Server: receive msg.mtype is: %d.\n", msg.mtype);

        msg.mtype = sendmsgtyp; // 客户端接收的消息类型
        sprintf(msg.mtext, "I'm from server,PID= %d", getpid());
        msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
    }
    return 0;
}


#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>

// 用于创建一个唯一的key
//#define MSG_FILE "/etc/passwd"
#define MSG_FILE "/boot"

int bufsz = 256;   //size_t
int sendmsgtyp = 888; //long 
int revmsgtyp = 999; //long 

// 消息结构
struct msg_form {
    long mtype;
    char mtext[256];
};

int main()
{
    int msqid;
    key_t key;
    struct msg_form msg;

    // 获取key值
    if ((key = ftok(MSG_FILE, 'z')) < 0)
    {
        perror("ftok error");
        exit(1);
    }

    // 打印key值
    printf("Message Queue - Client key is: %d.\n", key);

    // 打开消息队列
    if ((msqid = msgget(key, IPC_CREAT | 0777)) == -1)
    {
        perror("msgget error");
        exit(1);
    }

    // 打印消息队列ID及进程ID
    printf("Message Queue ID  is: %d.\n", msqid);
    printf("Pid is: %d.\n", getpid());

    // 添加消息,类型为888
    msg.mtype = sendmsgtyp;
    sprintf(msg.mtext, "hello,I'm from Client,PID= %d", getpid());
    msgsnd(msqid, &msg, sizeof(msg.mtext), 0);

    // 读取类型为999的消息
    msgrcv(msqid, &msg, bufsz, revmsgtyp, 0);
    printf("Client: receive msg.mtext is: %s.\n", msg.mtext);
    printf("Client: receive msg.mtype is: %d.\n", msg.mtype);
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值