2021SC@SDUSC TencentOS Tiny源码分析(七) 队列模块三

2021SC@SDUSC

一. TencentOS-tiny中从队列中获取消息

TencentOS-tiny中进程从队列中获取消息的机制比较简单:

给进程指定一个等待时间,当且仅当队列存在消息的时候,进程才能从队列中获取到消息,当队列中没有消息时,进程处于阻塞态,当进程处于阻塞态的时间超过给定等待时间时,便会自动由阻塞态转为就绪态。下面我们来看一下获取队列消息函数源码,以及这个等待队列的过程中的一些参数:

(一)源码及参数含义:

等待队列消息函数,在\kernel\core\tos_queue.c 第 60 行

__API__ k_err_t tos_queue_pend(k_queue_t *queue, void **msg_addr, size_t *msg_size, k_tick_t timeout)
{
    TOS_CPU_CPSR_ALLOC();
    k_err_t err;

    TOS_PTR_SANITY_CHECK(queue);
    TOS_PTR_SANITY_CHECK(msg_addr);
    TOS_PTR_SANITY_CHECK(msg_size);

#if TOS_CFG_OBJECT_VERIFY_EN > 0u
    if (!pend_object_verify(&queue->pend_obj, PEND_TYPE_QUEUE)) {
        return K_ERR_OBJ_INVALID;
    }
#endif

    TOS_CPU_INT_DISABLE();

    if (tos_msg_queue_get(&queue->msg_queue, msg_addr, msg_size) == K_ERR_NONE) {
        TOS_CPU_INT_ENABLE();
        return K_ERR_NONE;
    }

    if (timeout == TOS_TIME_NOWAIT) {
        *msg_addr = K_NULL;
        *msg_size = 0;
        TOS_CPU_INT_ENABLE();
        return K_ERR_PEND_NOWAIT;
    }

    if (knl_is_sched_locked()) {
        TOS_CPU_INT_ENABLE();
        return K_ERR_PEND_SCHED_LOCKED;
    }

    pend_task_block(k_curr_task, &queue->pend_obj, timeout);

    TOS_CPU_INT_ENABLE();
    knl_sched();

    err = pend_state2errno(k_curr_task->pend_state);

    if (err == K_ERR_NONE) {
        *msg_addr = k_curr_task->msg_addr;
        *msg_size = k_curr_task->msg_size;
        k_curr_task->msg_addr = K_NULL;
        k_curr_task->msg_size = 0;
    }

    return err;
}

将等待消息的任务添加到对应等待列表函数,在\kernel\core\tos_pend.c文件的 第 106 行

__KERNEL__ void pend_task_block(k_task_t *task, pend_obj_t *object, k_tick_t timeout)
{
    readyqueue_remove(task);
    pend_list_add(task, object);

    if (timeout != TOS_TIME_FOREVER) {
        tick_list_add(task, timeout);
    }
}

获取任务等待状态的函数,在\kernel\core\tos_pend.c文件的 第 72 行

__KERNEL__ k_err_t pend_state2errno(pend_state_t state)
{
    if (state == PEND_STATE_POST) {
        return K_ERR_NONE;
    } else if (state == PEND_STATE_TIMEOUT) {
        return K_ERR_PEND_TIMEOUT;
    } else if (state == PEND_STATE_DESTROY) {
        return K_ERR_PEND_DESTROY;
    } else if (state == PEND_STATE_OWNER_DIE) {
        return K_ERR_PEND_OWNER_DIE;
    } else {
        return K_ERR_PEND_ABNORMAL;
    }
}

这是源码中出现的一些参数及其含义:

参数说明
queue队列控制块指针
msg_addr用于保存获取到的消息(这是输出的)
msg_size用于保存获取到消息的大小(这是输出的)
timeout等待时间(以k_tick_t为单位)

(二)结合源码分析读消息过程

下面我们结合源码,分析一下进程等待队列消息的过程,看一下进程是如何等待队列中的消息的:

  • 首先检测传入的参数是否正确
  • 尝试调用tos_msg_queue_get()函数获取消息,如果队列存在消息则会获取成功(返回K_ERR_NONE),否则获取失败。(关于该函数在下一章讲解)
    • 当获取成功则可以直接退出函数
    • 当获取消息失败的时候,则可以根据指定的等待时间timeout进行阻塞,如果不等待(timeout =TOS_TIME_NOWAIT),则直接返回错误代码K_ERR_PEND_NOWAIT。
  • 如果调度器被锁了knl_is_sched_locked(),则无法进行等待操作,返回错误代码K_ERR_PEND_SCHED_LOCKED,毕竟需要切换任务,调度器被锁则无法切换任务。
  • 调用pend_task_block()函数将任务阻塞,该函数实际上就是将任务从就绪列表中移除k_rdyq.task_list_head[task_prio],并且插入到等待列表中object->list,如果等待的时间不是永久等待TOS_TIME_FOREVER,还会将任务插入时间列表中k_tick_list,阻塞时间为timeout,然后进行一次任务调度knl_sched()。
  • 当程序能执行到pend_state2errno()时,则表示任务等待到消息,又或者发生超时,那么就调用pend_state2errno()函数获取一下任务的等待状态,看一下是哪种情况导致任务恢复运行。
  • 如果是正常情况(等待获取到消息),则将消息从任务控制块的k_curr_task->msg_addr读取出来,并且写入msg_addr 中用于返回。同样的消息的大小也是会通过msg_size返回。

二. TencentOS-tiny中往队列中写入消息

在TencentOS-tiny中是允许进程或者中断服务程序给消息队列发送消息的,当发送消息时,TencentOS-tiny会从消息池中取出一个消息,挂载到队列的消息列表末尾-(FIFO发送方式),下面我们来大致看一下相关源码:

(一)源码及参数含义:

写入队列消息函数,在\kernel\core\tos_queue.c 第 159 、164 行

__API__ k_err_t tos_queue_post(k_queue_t *queue, void *msg_addr, size_t msg_size)
{
    TOS_PTR_SANITY_CHECK(queue);
    TOS_PTR_SANITY_CHECK(msg_addr);

    return queue_do_post(queue, msg_addr, msg_size, OPT_POST_ONE);
}

__API__ k_err_t tos_queue_post_all(k_queue_t *queue, void *msg_addr, size_t msg_size)
{
    TOS_PTR_SANITY_CHECK(queue);
    TOS_PTR_SANITY_CHECK(msg_addr);

    return queue_do_post(queue, msg_addr, msg_size, OPT_POST_ALL);
}

tos_queue_post()是唤醒一个等待队列消息任务,tos_queue_post_all()则会唤醒所有等待队列消息的任务,无论何种情况,都是调用queue_do_post将消息写入队列中。

写入队列消息函数实际调用的函数,通过opt参数进行不一样的处理,在\kernel\core\tos_queue.c 第 118 行

__STATIC__ k_err_t queue_do_post(k_queue_t *queue, void *msg_addr, size_t msg_size, opt_post_t opt)
{
    TOS_CPU_CPSR_ALLOC();
    k_list_t *curr, *next;

    TOS_PTR_SANITY_CHECK(queue);

#if TOS_CFG_OBJECT_VERIFY_EN > 0u
    if (!pend_object_verify(&queue->pend_obj, PEND_TYPE_QUEUE)) {
        return K_ERR_OBJ_INVALID;
    }
#endif

    TOS_CPU_INT_DISABLE();

    if (pend_is_nopending(&queue->pend_obj)) {
        if (tos_msg_queue_put(&queue->msg_queue, msg_addr, msg_size, TOS_OPT_MSG_PUT_FIFO) != K_ERR_NONE) {
            TOS_CPU_INT_ENABLE();
            return K_ERR_QUEUE_FULL;
        }
        TOS_CPU_INT_ENABLE();
        return K_ERR_NONE;
    }

    if (opt == OPT_POST_ONE) {
        queue_task_msg_recv(TOS_LIST_FIRST_ENTRY(&queue->pend_obj.list, k_task_t, pend_list),
                                msg_addr, msg_size);
    } else { // OPT_QUEUE_POST_ALL
        TOS_LIST_FOR_EACH_SAFE(curr, next, &queue->pend_obj.list) {
            queue_task_msg_recv(TOS_LIST_ENTRY(curr, k_task_t, pend_list),
                                msg_addr, msg_size);
        }
    }

    TOS_CPU_INT_ENABLE();
    knl_sched();

    return K_ERR_NONE;
}

唤醒等待的任务函数,在\kernel\core\tos_pend.c文件 的第 87 行

唤醒等待任务的思想就是将任务从对应的等待列表移除,然后添加到就绪列表中。

__KERNEL__ void pend_task_wakeup(k_task_t *task, pend_state_t state)
{
    if (task_state_is_pending(task)) {
        // mark why we wakeup
        task->pend_state = state;
        pend_list_remove(task);
    }

    if (task_state_is_sleeping(task)) {
        tick_list_remove(task);
    }

    if (task_state_is_suspended(task)) {
        return;
    }

    readyqueue_add(task);
}

(二)结合源码分析写消息过程

下面我们结合源码,分析一下往队列中写消息的过程,看一下进程或者是中断服务程序是如何往队列中写消息的:

  • 和读消息类似,首先检测传入的参数是否正确
  • 尝试调用tos_msg_queue_get()函数获取消息,如果队列存在消息则会获取成功(返回K_ERR_NONE),否则获取失败。(关于该函数在下一章讲解)
    • 当获取成功则可以直接退出函数
    • 当获取消息失败的时候,则可以根据指定的等待时间timeout进行阻塞,如果不等待(timeout =TOS_TIME_NOWAIT),则直接返回错误代码K_ERR_PEND_NOWAIT。
  • 如果调度器被锁了,knl_is_sched_locked(),则无法进行等待操作,返回错误代码K_ERR_PEND_SCHED_LOCKED,毕竟需要切换任务,调度器被锁则无法切换任务。
  • 调用pend_task_block()函数将任务阻塞,该函数实际上就是将任务从就绪列表中移除k_rdyq.task_list_head[task_prio],并且插入到等待列表中object->list,如果等待的时间不是永久等待TOS_TIME_FOREVER,还会将任务插入时间列表中k_tick_list,阻塞时间为timeout,然后进行一次任务调度knl_sched()。
  • 当程序能执行到pend_state2errno()时,则表示任务等待到消息,又或者发生超时,那么就调用pend_state2errno()函数获取一下任务的等待状态,看一下是哪种情况导致任务恢复运行。
  • 如果是正常情况(等待获取到消息),则将消息从任务控制块的k_curr_task->msg_addr读取出来,并且写入msg_addr 中用于返回。同样的消息的大小也是会通过msg_size返回。

以上就是关于本周的所有内容了,也是TencentOS-tiny源码学习分析的队列模块所有内容,下周我将开始消息队列模块内容的源码学习和分析。

1.查看系统本身存在的版本 rpm -qa | grep yum 2.卸载centos7上存在的yum安装包 rpm -e 包 --nodeps 3.下载yum包(http://mirrors.163.com/centos/7/os/x86_64/Packages/) yum-metadata-parser-1.1.4-10.el7.x86_64 PackageKit-yum-1.0.7-6.el7.centos.x86_64 yum-utils-1.1.31-40.el7.noarch 下方两个一起装 yum-plugin-fastestmirror-1.1.31-40.el7.noarch yum-langpacks-0.4.2-7.el7.noarch yum-3.4.3-150.el7.centos.noarch yum-rhn-plugin-2.0.1-6.el7.noarch 4.安装yum包 rpm -ivh yum* 5.创建配置文件(/etc/yum.repos.d/CentOS-Base.repo) vi /etc/yum.repos.d/CentOS-Base.repo [base] name=CentOS-$releasever - Base - 163.com #mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch;=$basearch&repo=os baseurl=http://mirrors.163.com/centos/(系统版本号)7/os/$basearch/ gpgcheck=1 gpgkey=http://mirrors.163.com/centos/RPM-GPG-KEY-CentOS-7 #released updates [updates] name=CentOS-$releasever - Updates - 163.com #mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch;=$basearch&repo=updates baseurl=http://mirrors.163.com/centos/7/updates/$basearch/ gpgcheck=1 gpgkey=http://mirrors.163.com/centos/RPM-GPG-KEY-CentOS-7 #additional packages that may be useful [extras] name=CentOS-$releasever - Extras - 163.com #mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch;=$basearch&repo=extras baseurl=http://mirrors.163.com/centos/7/extras/$basearch/ gpgcheck=1 gpgkey=http://mirrors.163.com/centos/RPM-GPG-KEY-CentOS-7 #additional packages that extend functionality of existing packages [centosplus] name=$releasever - Plus - 163.com baseurl=http://mirrors.163.com/centos/7/centosplus/$basearch/ gpgcheck=1 enabled=0 gpgkey=http://mirrors.163.com/centos/RPM-GPG-KEY-CentOS-7 每一个baseurl的centos后都改成自己系统的版本号 6.执行命令 yum clean all yum makecache yum install telnet
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值