NuttX的学习笔记 9
Task Control Interfaces
Task Control Interfaces
Scheduler locking interfaces
调度程序锁定接口。这种非标准接口是用于启用和禁用占先式和测试占先式目前启用。占先式优先级是32的一种中断相应方式。
sched_lock
int sched_lock(void);
这个函数禁用添加新任务。任务调用这个函数后将会是唯一任务。
sched_unlock
这个函数衰减抢占锁计数。通常这是搭配sched_lock()。解锁抢占有时需要多次调用sched_unlock()
由于之前也多次调用sched_lock()
。当lockCount递减为零,任何任务都有资格来抢占当前任务执行。这里和我理解的不一样,我以为是前一个函数将当前任务锁定为唯一任务,唯一任务是个什么意思,还有待试验中发现。
sched_lockcount
这个函数返回lockCount
的当前值。如果为零,抢占启用;如果非零,这个值表明sched_lock()
被称为执行线程。
(顺便这里说一下上一次的时间片问题,回显的tv_sec: 0
为0而tv_nsec: 200000000
值这么大的原因。时间片就是系统分给实时进程的运行时间,一旦时间片用完,那么进程将被暂停,放到FIFO队列最后面等待下一次调度。sec自然就是秒,而nsec自然就是纳秒,因为在sched_rr_get_interval
内将sec赋值的语句是一个除法,结果是一个小数,而tv_sec的类型不是一个浮点型,故为0。)
Task synchronization interfaces
等待终止子任务。
waitpid
ipid_t waitpid(pid_t pid, int *stat_loc, int options);
这个看起来还是有点复杂的。首先这个函数涉及到配置选项 CONFIG_SCHED_HAVE_PARENT
。如果定义CONFIG_SCHED_HAVE_PARENT waitpid()将更符合规范。
当该配置未开启时,waitpid()
只支持等待任何任务完成执行。
当该配置开启时,waitpid()
将使用 SIGCHLD
(这个貌似很厉害的样子)以达到等待任何进程。
pid 的情况有以下几种情况:
pid | 结果 |
---|---|
pid < (pid_t) -1 | 取绝对值 |
pid = (pid_t) -1 | 等待知道任意一个子进程返回 |
pid = 0 | 等待于调用函数的子进程组内的所有子进程 |
pid > 0 | 等待制定 pid 子进程 |
options的值有以下几种情况:
options | 在以下情况下返回或也会返回 |
---|---|
WCONTINUED | 等待的已停止但还未报道的子进程又被其他功能继续时 |
WNOHANG | 没有已经退出的子进程 |
WUNTRACED | 指定的子进程已经退出但还未被报道 |
这里比较复杂,实际使用中碰到的时候再说
waitid
#ifdef CONFIG_SCHED_HAVE_PARENT
int waitid(idtype_t idtype, id_t id, FAR siginfo_t *info, int options);
#endif
不配置CONFIG_SCHED_HAVE_PARENT
将不会有这个函数。而且作者写这个文档时这个 东西还没不完整。如果定义CONFIG_SCHED_HAVE_PARENT waitid()将更符合规范。
waitid仅仅支持等待一个特定的子任务。
这里先说明一下 pid 和 tid :
- pid(Process Identification) 进程识别号
- tid(Thread Identification) 线程识别号
总的来说,pid是指进程的。tid是指线程的。pid是唯一的,但是tid只是在在同一程下是唯一的。
idtype | 意义 |
---|---|
P_PID |
waitid()将等待pid为id 的子进程
|
P_PGID |
waitid()将等待pgid为id 的进程组内所有的子进程。
|
P_ALL |
waitid()将等待任何子进程并且忽略id
|
options
参数表:
idtype | 意义 |
---|---|
WEXITED | 等待已终止的子进程 |
WSTOPPED | 等待那些被因接收到信号而暂停的子进程 |
WCONTINUES | 等待那些已经停止了但是又被继续的子进程 |
WNOHANG | 如果没有子进程需要等待,那么立即返回 |
WNOWAIT | 返回任务的状态改变信息后,还能在之后继续获取该状态信息 |
wait
Task Exit Hooks
好长一段,懒得看了。直接翻译:atexit()和on_exit()可以使用注册回调函数,当一个任务组执行终止。任务组的功能模拟是一个过程:这是一组由主要任务线程和所有pthreads的主要任务创建的线程或者其他的任务broup pthreads。任务组的成员共享某些资源等环境变量,文件描述符,文件流、套接字、pthread键和开放的消息队列。
atexit
int atexit(void (*func)(void));
使用该函数注册结束前执行的空函数。
on_exit
int on_exit(CODE void (*func)(int, FAR void *), FAR void *arg);
使用该函数注册结束前执行的任务。
Parent and Child Tasks
任务同步接口“历史上?”依赖父进程和子进程之间的关系的任务。但默认的,NuttX不使用任何父/子进程知识。然而,有三个重要的配置选项,可以改变这种情况。
CONFIG_SCHED_HAVE_PARENT
如果这个设置定义,它将使NuttX记得父任务新创的每个子任务的任务ID。这种它能支持使一些额外的特性(如SIGCHLD
)和修改其他接口的行为。例如,它使waitpid()
更标准完成通过限制我等的那个任务的调用者。
CONFIG_SCHED_CHILD_STATUS
如果这个选项被选中时,那么子进程退出后其退出状态标志将被保留。没有这个设置 wait()
, waitpid()
或 waitid()
可能会失败。
CONFIG_PREALLOC_CHILDSTATUS
为了防止失控的子进程状态分配和提高分配性能,子任务退出状态结构在系统启动时预先分配。此设置确定子进程的数量状态结构,将预先分配。如果这个设置没有定义或定义为零值为2 X MAX_TASKS
使用。
又到了开始测试的时候了。
任务控制接口实验
按照我的理解先测试第一个。将 hello_task
锁住,再添加其他任务。代码:(顺便说一下我发现这个ifdef-endif
很好用,既能保存之前的代码,而且还能轻松地使用define启用)
这里先停一下,任务的锁定效果并不好,不知道哪里出了问题。之前有个让任务让出CPU的函数也是这样。输出结果丝毫不受影响。搞得我都不想再测试任务了。下一个解锁的也跳过。
以下的 任务不是必须配置 CONFIG_SCHED_HAVE_PARENT
,路径:-> RTOS Features -> Tasks and Scheduling -> Support parent/child task relationships
先不配置,测试后再配置进行比对。
(前天的测试一点都不理想,关于 waitpid
,昨天刚好赶上DOTA2的7.0更新,大圣简直了,出场率100%,玩了一下午,一天都没有看代码。)
今天测试有点意外, waitpid
突然又能用了。这让我喜出望外。测试代码宏定义段:(全部代码在最后面)
#define Started_print_hello
#define Wait_print_hello_with_waitpid
#define Print_Hello_World
测试结果:
nsh> hello_task 5
hello_task: Started print_hello at PID=9
print_hello: Hello 0
print_hello: Hello 1
print_hello: Hello 2
print_hello: Hello 3
print_hello: Hello 4
hello_task: Waiting print_hello success
hello_task: Hello World 0
hello_task: Hello World 1
hello_task: Hello World 2
hello_task: Hello World 3
hello_task: Hello World 4
nsh>
效果很理想,完全达到预期。
切回去测试之前的代码,果然 lock
的不工作问题也解决了。代码宏定义段改为:
#define Started_print_hello
#define Lock_hello_task_with_sched_lock
#define Print_Hello_World
结果和上次实验的结果一样。
再来一个waitid。
首先在config里启动该选项。
-> RTOS Features -> Tasks and Scheduling -> Support parent/child task relationships
宏定义段:
#define Started_print_hello
#define Wait_print_hello_with_waitid
#define Print_Hello_World
达到相同的效果。
而 wait()
寻找到该代码的定义段时发现:
pid_t wait(FAR int *stat_loc) {return waitpid((pid_t) -1, stat_loc, 0);
}
这不就是waitpid()
的等待所有子进程的功能么!
我就再开一个子进程print_world
,并且输出的的是print_hello
的两倍。之后发现结果是当其中一个 print_hello
结束后主进程就停止等待了,所以,我重新阅读了文档,发现之前有个小错误,waitpid(-1,...)
的功能是等待所有子进程直到有任意一个子进程结束,并不是等待所有。下面一个实验当然就是waitpid(0,...)
。
结果,出现了错误:
hello_task: ERROR: Failed to all child task: No child processes
看样子之前的问题又暴露出来了:由进程创建的进程是不是子进程。
查了一些资料,结果是,子进程(只能?)由vfork
创建。
之前写的例子直接搬过来。重新整理代码,结果是无论如何我都不能让父进程先于子进程。查找 vfork
的代码,并没有找到,这就很诡异了。但是在这里,我找到了task_vforkstart
这个函数。这个函数内有这样一段代码:
/* Get the assigned pid before we start the task */pid = (int) child->cmn.pid;/* Activate the task */ret = task_activate((FAR struct tcb_s *) child);if (ret < OK) {task_vforkabort(child, -ret);
return ERROR;}/* Since the child task has the same priority as the parent task, it is* now ready to run, but has not yet ran. It is a requirement that* the parent environment be stable while vfork runs; the child thread* is still dependent on things in the parent thread... like the pointers* into parent thread's stack which will still appear in the child's* registers and environment.** We do not have SIG_CHILD, so we have to do some silly things here.* The simplest way to make sure that the child thread runs to completion* is simply to yield here. Since the child can only do exit() or* execv/l(), that should be all that is needed.** Hmmm.. this is probably not sufficient. What if we are running* SCHED_RR? What if the child thread is suspended and rescheduled* after the parent thread again?*//* We can also exploit a bug in the execv() implementation: The PID* of the task exec'ed by the child will not be the same as the PID of* the child task. Therefore, waitpid() on the child task's PID will* accomplish what we need to do.*/rc = 0;(void) waitpid(pid, &rc, 0);
#endif
return pid;
这样看来,由于没有SIG_CHILD
,所以在子进程开始之前,就已经处于 waitpid
状态,而waitpid(pid, &rc, 0)
中的pid在上面就是由child->cmn.pid
赋值的,总的来说就是父进程在等待子进程先于父进程结束。难怪子进程先于父进程而不是两个进程一起运行,当然,父进程先于子进程的话,父进程一结束,那子进程不久崩溃了么,父进程先于子进程肯定是不行的。除非有这里提到的SIG_CHILD
。这个等待所有子进程的就算是完成了。当然只有一个子进程需要等待的,如果在子进程中再调用一次 vfork
便会出现嵌套的 waitpid
看样子想要让父进程等待很多子进程还远着呢。
下一个就是两个任务在执行完成前调用其他函数的函数。
使用atexit()
之前还需要在configure中开启CONFIG_SCHED_ATEXIT
,路径 -> RTOS Features -> RTOS hooks -> Enable atexit() API
然后设定数量上限。代码宏定义部分:
#define atexit_test
结果不言而喻。但是另一个出了问题。完全没有运行。其定义令我费解:
int on_exit(CODE void (*func)(int, FAR void *), FAR void *arg);
…就看在我心情不错的份上先放它一马。本章结束。最后,所有代码:
#include <sched.h>
#include <nuttx/config.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>/********************************************************** Pre-processor Definitions
*********************************************************/
//#define here
/********************************************************** Private Functions
*********************************************************/
#if defined(Started_print_hello)||defined(Started_print_world)\||defined(Print_Hello_World)||defined(on_exit_test)
int str2num(const char *s) {int rt = 0;while (*s >= '0' && *s <= '9') {rt = rt * 10 + (*s - '0');s++;}return rt;
}
#endif#ifdef Started_print_hello
int print_hello(int argc, char *argv[]) {int i;int a = str2num(argv[1]);for (i = 0; i < a; ++i) {sched_yield();printf("%s: Hello %d\n", argv[0], i);}exit(EXIT_SUCCESS);
}
#endif#ifdef Started_print_world
int print_world(int argc, char *argv[]) {int i;int a = str2num(argv[1]);for (i = 0; i < (a * 2); ++i) {printf("%s: World %d\n", argv[0], i);}exit(EXIT_SUCCESS);
}
#endif#ifdef atexit_test
void fun(void) {printf("fun\n");
}
#endif/********************************************************** Public Functions
*********************************************************//********************************************************** hello_task
*********************************************************/
int hello_task_main(int argc, char *argv[]) {int ret = 0;pid_t hello_task_pid;hello_task_pid = getpid();#ifdef Started_print_hellopid_t print_hello_pid;print_hello_pid = task_create("print_hello",SCHED_PRIORITY_DEFAULT, 2048, print_hello, &argv[1]);if (print_hello_pid < 0) {int errcode = errno;printf("%s: ERROR: Failed to start print_hello: %s\n", argv[0],strerror(errcode));exit(EXIT_FAILURE);} elseprintf("%s: Started print_hello at PID=%d\n", argv[0], print_hello_pid);
#endif#ifdef Started_print_worldpid_t print_world_pid;print_world_pid = task_create("print_world",SCHED_PRIORITY_DEFAULT, 2048, print_world, &argv[1]);if (print_world_pid < 0) {int errcode = errno;printf("%s: ERROR: Failed to start print_world: %d\n", argv[0],strerror(errcode));exit(EXIT_FAILURE);} elseprintf("%s: Started print_world at PID=%d\n", argv[0], print_world_pid);
#endif#ifdef Wait_child_task_with_waitpidret = waitpid(print_hello_pid, NULL, 0);if (ret == ((pid_t) -1)) {int errcode = errno;printf("%s: ERROR: Failed to wait print_hello: %s\n", argv[0],strerror(errcode));exit(EXIT_FAILURE);} elseprintf("%s: Waiting print_hello success\n", argv[0]);
#endif#ifdef Wait_child_task_with_waitidret = waitid(P_PID, print_hello_pid, NULL, 0);if (ret == ((pid_t) -1)) {int errcode = errno;printf("%s: ERROR: Failed to wait print_hello: %s\n", argv[0],strerror(errcode));exit(EXIT_FAILURE);} elseprintf("%s: Waiting print_hello success\n", argv[0]);
#endif#ifdef Wait_child_task_with_waitret = wait(NULL);if (ret == ((pid_t) -1)) {int errcode = errno;printf("%s: ERROR: Failed to wait child task: %s\n", argv[0],strerror(errcode));exit(EXIT_FAILURE);} elseprintf("%s: Waiting child task success\n", argv[0]);
#endif#ifdef Lock_hello_task_with_sched_lockprintf("%s: Lock Count: %d\n", argv[0], sched_lockcount());ret = sched_lock();if (ret) {printf("%s: Lock myself Fail\n", argv[0]);} else {printf("%s: Lock myself Success\n", argv[0]);printf("%s: Lock Count: %d\n", argv[0], sched_lockcount());}
#endif#ifdef Print_Hello_World{int i;int a = str2num(argv[1]);if (a == 0)printf("%s: There is nothing to say%d\n", argv[0], i);elsefor (i = 0; i < a; ++i) {printf("%s: Hello World %d\n", argv[0], i);}}
#endif#ifdef atexit_testatexit(fun);
#endifexit(EXIT_SUCCESS);
}
NuttX的学习笔记 9相关推荐
- Nuttx系统学习笔记(三)——使用Nuttx操作STM32F429外设
在上一篇,我们已经学会了如何将Nuttx进行烧录,以及学会了如何部署这个操作系统,接下来我们就要使用这个操作系统来实现我们对嵌入式设备的控制,当然也是从点灯开始的.这个基于Posix架构的操作系统使用 ...
- NuttX的学习笔记 13
2.6 Clocks and Timers 这部分是时钟与定时器,ostest里面并没有发现相关东西,在例子里找到了timer,在configure里开启这个例子. 查看这个例子的Kconfig文件, ...
- NuttX的学习笔记 12
#2.5 Counting Semaphore Interfaces Semaphores. Semaphores are the basis for synchronization and mutu ...
- NuttX的学习笔记 14
下面来分析timer这个例子吧. int timer_main(int argc, char *argv[]) {struct timer_notify_s notify;struct sigacti ...
- NuttX的学习笔记 11
板子可以跑NuttX了,继续研究ostest 之前是在讲消息队列的,那么这节就把ostest里面相关消息队列的代码分析一遍. 先从ostest_main()开始. apps\examples\oste ...
- NuttX的学习笔记 8
好了,新开一篇继续新的内容 Task Scheduling Interfaces sched_setparam sched_getparam sched_setscheduler sched_gets ...
- NuttX的学习笔记 2
README文档目录的第二部分 Configuring NuttX Instantiating "Canned" Configurations Refreshing Configu ...
- MavSDKMavros学习笔记
MavSDK&Mavros学习笔记 Introduction · MAVSDK Guide (mavlink.io) GitHub - mavlink/MAVSDK源码 C++ · MAVSD ...
- PX4/APM/飞控的学习笔记前言-Cxm
开始了 开始了 终于有时间可以学习飞控了 此文章是用来当目录,我会持续更新我的学习之旅,希望能对各位有所帮助 如果有错误的地方还请各位前辈指点. 此帖持续更新后续内容 其实从21年的一月就开始学习飞控 ...
最新文章
- cout 输出指定位数,不足补0
- java 删除二进制内容_从二进制矩阵中仅删除一个元素的行/列
- python创建数据库表空间_7.自动化监控多个Oracle表空间
- 那位标榜技术驱动的开发者去哪了?
- android中屏幕宽高显示不全,Android 获取屏幕宽度跟高度
- 软件中的1、同步调用;2、回调;3、异步调用
- Android 出现警告Exported service does not require permission
- Net设计模式实例之中介者模式(Mediator Pattern)
- python装饰器理解_如何理解Python装饰器?
- python代码评测结果tle_比比谁的代码短:TLE测试赛结束
- The field file exceeds its maximum permitted size of 1048576 bytes.
- java ppt转图片 失真_java转换ppt,ppt转成图片,获取备注,获取文本
- DID 去中心化数字身份
- 《梦幻西游》手游服务器如何实现200万玩家同时在线?(技术篇)
- 银行不良贷款很大一部分是诈骗的结果:CAG
- qq2009 好像和金山词霸屏幕取词有冲突
- 计算机自定义桌面设置在哪里设置,桌面显示日历设置方法
- Navicat Premium的下载及安装
- php notice undefined variable,解决PHP提示Notice: Undefined variable的办法
- 微距摄影,如何用单反相机拍好昆虫照片
热门文章
- excel手机版_微软开发于手机端的办公软件!
- 云服务器怎么设置成代理服务器?
- 胭脂茉莉点评推荐上海大学法院李本教授诗集《秋月曲》诗歌6首
- 2022年推土机司机(建筑特殊工种)考试题库及推土机司机(建筑特殊工种)考试技巧
- 机械手表,石英手表,智能手表怎么选最好,哪种更适合佩戴?
- 贷款违约行为的ANOVA分析——关于不同抽样方法得到不同结论的分析
- python3.6.国家政策文本分析代码
- 大学的终结,终结了什么???
- Linux下堆漏洞利用(off-by-one)
- ABAP 使用Smartforms发送HTML邮件