diff --git a/rt-thread-version/rt-thread-standard/_sidebar.md b/rt-thread-version/rt-thread-standard/_sidebar.md index 8c2a67b09ec10c32df33cedb7d87529e19805fd7..9c5889e63cb71872f7550601979acfd171dd6e6f 100644 --- a/rt-thread-version/rt-thread-standard/_sidebar.md +++ b/rt-thread-version/rt-thread-standard/_sidebar.md @@ -59,8 +59,12 @@ - [概况](/rt-thread-version/rt-thread-standard/programming-manual/libc/introduction.md) - [编译器配平层](/rt-thread-version/rt-thread-standard/programming-manual/libc/compiler.md) - POSIX - - [POSIX 简介](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/introduction.md) - - [PSE51](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/pse51.md) + - [简介](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/introduction.md) + - [FILE](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-file.md) + - [Pthread](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-pthread.md) + - [Timer](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-timer.md) + - [IPC semaphore](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-ipc-semaphore.md) + - [IPC messages](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-ipc-message.md) - [动态模块](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/dlmodule.md) - 网络组件 - [net 组件总概](/rt-thread-version/rt-thread-standard/programming-manual/net/net_introduce.md) @@ -80,7 +84,6 @@ - [虚拟文件系统](/rt-thread-version/rt-thread-standard/programming-manual/filesystem/filesystem.md) - [ulog 日志](/rt-thread-version/rt-thread-standard/programming-manual/ulog/ulog.md) - [utest 测试框架](/rt-thread-version/rt-thread-standard/programming-manual/utest/utest.md) - - [POSIX 接口](/rt-thread-version/rt-thread-standard/programming-manual/posix/posix.md) - [电源管理](/rt-thread-version/rt-thread-standard/programming-manual/pm/pm.md) - [RT-Link 组件](/rt-thread-version/rt-thread-standard/programming-manual/rtlink/rtlink.md) - 软件包 diff --git a/rt-thread-version/rt-thread-standard/programming-manual/libc/figures/51_52_53_54.png b/rt-thread-version/rt-thread-standard/programming-manual/libc/figures/51_52_53_54.png new file mode 100644 index 0000000000000000000000000000000000000000..96efd9417ecea85354b3dc966cfa9ea7e7eddda2 Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/libc/figures/51_52_53_54.png differ diff --git a/rt-thread-version/rt-thread-standard/programming-manual/libc/figures/libc_structure.png b/rt-thread-version/rt-thread-standard/programming-manual/libc/figures/libc_structure.png index c93d1572df779d795f98737bab698c5dbef6b41c..93c8985afb8a5717e0566821a4eeb9d83e386401 100644 Binary files a/rt-thread-version/rt-thread-standard/programming-manual/libc/figures/libc_structure.png and b/rt-thread-version/rt-thread-standard/programming-manual/libc/figures/libc_structure.png differ diff --git a/rt-thread-version/rt-thread-standard/programming-manual/libc/figures/menuconfig_info.png b/rt-thread-version/rt-thread-standard/programming-manual/libc/figures/menuconfig_info.png new file mode 100644 index 0000000000000000000000000000000000000000..0176bfb6116c876d3ac4d779c12aea291df26ddc Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/libc/figures/menuconfig_info.png differ diff --git a/rt-thread-version/rt-thread-standard/programming-manual/libc/figures/menuconfig_pthread.png b/rt-thread-version/rt-thread-standard/programming-manual/libc/figures/menuconfig_pthread.png new file mode 100644 index 0000000000000000000000000000000000000000..c7422e209b944dec436c2ef97c535896a1a88dda Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/libc/figures/menuconfig_pthread.png differ diff --git a/rt-thread-version/rt-thread-standard/programming-manual/libc/figures/menuconfig_timer.png b/rt-thread-version/rt-thread-standard/programming-manual/libc/figures/menuconfig_timer.png new file mode 100644 index 0000000000000000000000000000000000000000..5e4c3dde251d391dc53e74c4fcc463a349eaaaf8 Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/libc/figures/menuconfig_timer.png differ diff --git a/rt-thread-version/rt-thread-standard/programming-manual/libc/introduction.md b/rt-thread-version/rt-thread-standard/programming-manual/libc/introduction.md index d3288c43b6f3c1d93e700b49e85f8829fc754bd4..ac8e148fecad08e225f4dfa1e6f4a9d01f130bbf 100644 --- a/rt-thread-version/rt-thread-standard/programming-manual/libc/introduction.md +++ b/rt-thread-version/rt-thread-standard/programming-manual/libc/introduction.md @@ -4,11 +4,25 @@ RT-Thread 提供的 LIBC (C library, C库),包含编译器配平层和 POSIX ![libc_structure](figures/libc_structure.png) +由于在不同的平台开发所使用的编译工具链不一致;所以在实现时,RT-Thread 区分了三个概念:**广义的 POSIX**,**狭隘的 POSIX**,**编译器配平层**;下面结合关系图,描述一下对应的概念: + +* 广义的 POSIX ,即 POSIX 接口;这里面包含了一些关于 pthread, signal, IPC 等函数以及 C 库的相关内容; +* 狭隘的 POSIX,即不是 C 库标准提供以外的 POSIX 函数,例如 pthread 这些内容; +* 编译器配平层,由 C 库相关提供的函数。由于不同编译平台对该内容的实现参差不齐,因此需要针对不同的编译平台增补这些函数。这也就是 “ 配平 ” 的由来。这块内容较多,且不同的编译工具链所支持的操作不一,因此在 POSIX 代码迁移时需要修改这里来解决对应的编译错误。 + ### 注意事项 -为保证跨不同编译器、不同工具链的兼容性,建议用户应用层代码: +##### 不同编译平台的兼容 + +在上一节,编译器配平层的存在解决了多组编译工具链不一致造成的问题;同时,也引入了一些在编写代码时需要注意的事项。这些注意事项是为了解决对应的编译错误。因为编译工具链有的允许头文件替换,有的不支持这些问题。RT-Thread 提供了一个相对轻便的解决办法: + +* 为保证跨不同编译器、不同工具链的兼容性,建议用户应用层代码: + * 使用 代替 + * 使用 代替 + * 使用 代替 + +有使用 的引用逻辑的,修改为 的方式来避免编译错误;其他两个也是类似的处理逻辑,拟合不同编译工具链的处理逻辑。 -- 使用 代替 -- 使用 代替 -- 使用 代替 +##### Net 接口 +启用网络功能后,并不是所有的 BSD Socket 接口都支持,在 RT-Thread 中只实现了一部分函数。因此如果对应函数缺失,需要按照代码逻辑替换为基础的 Socket 接口来兼容原逻辑。完整的 BSD Socket 功能将持续进行来解决这个问题。 diff --git a/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/introduction.md b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/introduction.md index 2c8302e89012a1c24782b10df0047e60b0aa08e0..8559e04b6db49c8c916cdafd38e87f6f4e8ff607 100644 --- a/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/introduction.md +++ b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/introduction.md @@ -1,26 +1,44 @@ # POSIX 简介 -## 标准输入输出函数 - -如果需要读写标准输入输出,调用以下函数/功能,请开启 `RT_USING_POSIX_FS` 和 `RT_USING_POSIX_STDIO` 宏。若读写文件系统中的文件,则仅需开启 `RT_USING_POSIX_FS`,无需开启 `RT_USING_POSIX_STDIO`。 - -```c -/* 标准输出 */ -write(STDOUT_FILENO 或 STDERR_FILENO); -printf(...); /* 该函数仅需在gcc下使能上述两个宏,在其他编译器下,可以直接使用 */ -fprintf(stdout 或 stderr); -fwrite(stdout 或 stderr); -fputs(stdout 或 stderr); -fputc(stdout 或 stderr); -puts(); - -/* 标准输入 */ -getchar(); -read(STDIN_FILENO); -fread(stdin); -fgetc(stdin); -fgets(stdin); -``` - -> 注:如果已经开启 FinSH 功能的话,可以在 FinSH 线程下,使用 finsh_getchar 代替 getchar,来获取从终端键入的字符。 +可移植操作系统接口,POSIX 标准定义了操作系统(很多时候针对的是类 Unix 操作系统)应该为应用程序提供的接口标准,从而保证了应用程序在源码层次的可移植性,如今主流的 Linux 系统都做到了兼容 POSIX 标准。由此可见,可移植性是 POSIX 的一大特性,如果一个操作系统拟合了POSIX系统,就将可以将自己的 POSIX 应用轻松迁移到其他同样符合POSIX标准的系统平台,并且可以获得大量的 POSIX 应用,丰富自己系统的应用生态。 +POSIX 为了提供不同层级的RTOS能力,划定了 PSE51, PSE52, PSE53, PSE54 共四个级别;分别对应了四种操作系统。 + +1. Minimal Real-time System Profile IEEE Std 1003.13 PSE51,基础 RTOS +2. Real-time Controller Profile IEEE Std 1003.13 PSE52,带有简单的文件系统...等 +3. Dedicated Real-time Profile IEEE Std 1003.13 PSE53,拥有网络功能...等 +4. Multi-Purpose Real-time Profile IEEE Std 1003.13 PSE54,完整的文件系统,带有 Shell 组件...等 + +![image-20220421115323880](../figures/51_52_53_54.png) + +由于标准的制定是随着时间发展而不断更新的,PSE51, 52, 53, 54 的标准也随着 MCU,MPU 芯片产业的发展,不断增强的芯片功能和性能使得这些标准所对应的四种操作系统的界限而越显模糊。在 RT-Thread 中也依靠自身的组件内容,提供了多种标准的部分内容,为不同的 POSIX 应用提供可供在 RTOS 系统上使用的软件。 + +## POSIX 在 RT-Thread 中支持情况 + +RT-Thread 针对 PSE51 的标准规定的头文件及对应接口,提供了大部分接口,其 PSE51 接口数目占比如下饼状图所示。除此之外,我们还提供了更加完整的文件系统的接口,以及基于我们 SAL 组件的网络接口。这为 POSIX 的应用迁移打下了基础,可以使得较复杂的 POSIX 应用也可以在 RT-Thread 的平台上经过小部分修改就可以在 RT-Thread 上应用。 + +![image-20220419112531014](../figures/menuconfig_info.png) + +* POSIX FILE 相关接口,包含了文件系统的内容,接口比较丰富;标准 I/O 接口,Poll / Select 等较复杂的功能也已经支持 +* Delay 相关接口 +* Clock 相关接口,对拥有 RTC 硬件,或者没有使用 RTC 硬件的设备都有对应的功能实现 +* Timer 相关接口,已经对接到 RT-Thread 的定时器接口,实现对应的函数 +* Pthread 相关接口,在多任务编程中使用很广泛 +* Dynamic 动态模块 +* POSIX 的 标准 IPC 接口 :pipe ,message,semaphore 等函数 + +通过上面灵活的配置项,开发者可以启用对应的功能来添加该类函数的支持;这对与同样使用功能宏来标识一些函数功能的 POSIX 接口来说十分类似,在该界面可以配置对应的 POSIX 函数支持。 + +## Net 接口 + +1. 在 menuconfig 中打开 ```Enable BSD Socket I/O ``` 就可以使用标准 Socket 进行编程 + +更加详细的 Socket 编程内容,可以到**网络组件**下查看。 + +[函数介绍](../../sal/sal.md) +[简单例程](../../../tutorial/qemu-network/tcpclient/tcpclient) + +## Filesystem 接口 + +1. 在 menuconfig 中打开```Enable POSIX file system and I/O``` 配置具体的 POSIX 函数接口 +2. 按照需求打开需要的功能,例如 设备I/O,AIO,MMAN 之类的功能 diff --git a/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-clock.md b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-clock.md new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-delay.md b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-delay.md new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-file.md b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-file.md new file mode 100644 index 0000000000000000000000000000000000000000..07efce27071400ceb0e0bd20fd637e09235a6a49 --- /dev/null +++ b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-file.md @@ -0,0 +1,112 @@ +## 文件系统函数 + +本节介绍对文件进行操作的相关函数,对文件的操作一般都要基于文件描述符 fd,如下图所示: + +![文件管理常用函数](../../filesystem/figures/fs-mg.png) + +具体的文件系统函数已经在[虚拟文件系统](../../filesystem/filesystem.md)中实现,可以点此[跳转](../../filesystem/filesystem.md) + +## select 函数 + +### select 函数原型: + +```c +int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,struct timeval *timeout); +``` + +#### 【参数说明】 + +* **nfds**:select监视的文件句柄数,一般设为要监视各文件中的最大文件描述符值加1。 +* **readfds**:文件描述符集合监视文件集中的任何文件是否有数据可读,当select函数返回的时候,readfds将清除其中不可读的文件描述符,只留下可读的文件描述符。 +* **writefds**:文件描述符集合监视文件集中的任何文件是否有数据可写,当select函数返回的时候,writefds将清除其中不可写的文件描述符,只留下可写的文件描述符。 +* **exceptfds**:文件集将监视文件集中的任何文件是否发生错误,可用于其他的用途,例如,监视带外数据OOB,带外数据使用MSG\_OOB标志发送到套接字上。当select函数返回的时候,exceptfds将清除其中的其他文件描述符,只留下标记有OOB数据的文件描述符。 +* **timeout** 参数是一个指向 struct timeval 类型的指针,它可以使 select\(\)在等待 timeout 时间后若没有文件描述符准备好则返回。其timeval结构用于指定这段时间的秒数和微秒数。它可以使select处于三种状态: + +> \(1\) 若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止; +> \(2\) 若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值; +> \(3\) timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。 + +timeval 结构体定义 + +```c +struct timeval +{ + int tv_sec; /* 秒 */ + int tv_usec; /* 微妙 */ +}; +``` + +#### 【返回值】 + +* **int**:若有就绪描述符返回其数目,若超时则为0,若出错则为-1 + +下列操作用来设置、清除、判断文件描述符集合。 + +```c +FD_ZERO(fd_set *set); // 清除一个文件描述符集。 +FD_SET(int fd,fd_set *set); // 将一个文件描述符加入文件描述符集中。 +FD_CLR(int fd,fd_set *set); // 将一个文件描述符从文件描述符集中清除。 +FD_ISSET(int fd,fd_set *set); // 判断文件描述符是否被置位 +``` + +fd\_set可以理解为一个集合,这个集合中存放的是文件描述符\(file descriptor\),即文件句柄。中间的三个参数指定我们要让内核测试读、写和异常条件的文件描述符集合。如果对某一个的条件不感兴趣,就可以把它设为空指针。 + +**select\(\)的机制中提供一种fd\_set的数据结构**,实际上是一个long类型的数组,每一个数组元素都能与打开的文件句柄(不管是Socket句柄,还是其他文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成,当调用select\(\)时,由内核根据IO状态修改fd\_set的内容,由此来通知执行了select\(\)的进程哪一Socket或文件可读。 + +## poll 函数 + +### poll 的函数原型: + +```c +int poll(struct pollfd *fds, nfds_t nfds, int timeout); +``` + +#### 【参数说明】 + +* **fds**:fds是一个struct pollfd类型的数组,用于存放需要检测其状态的socket描述符,并且调用poll函数之后fds数组不会被清空;一个pollfd结构体表示一个被监视的文件描述符,通过传递fds指示 poll\(\) 监视多个文件描述符。 + +struct pollfd原型如下: + +```c +typedef struct pollfd { + int fd; // 需要被检测或选择的文件描述符 + short events; // 对文件描述符fd上感兴趣的事件 + short revents; // 文件描述符fd上当前实际发生的事件 +} pollfd_t; +``` + +其中,结构体的events域是监视该文件描述符的事件掩码,由用户来设置这个域,结构体的revents域是文件描述符的操作结果事件掩码,内核在调用返回时设置这个域。 + +* **nfds**:记录数组fds中描述符的总数量。 +* **timeout**:指定等待的毫秒数,无论 I/O 是否准备好,poll\(\) 都会返回,和select函数是类似的。 + +#### 【返回值】 + +* **int**:函数返回fds集合中就绪的读、写,或出错的描述符数量,返回0表示超时,返回-1表示出错; + +poll改变了文件描述符集合的描述方式,使用了pollfd结构而不是select的fd\_set结构,使得poll支持的文件描述符集合限制远大于select的1024。这也是和select不同的地方。 + +## 标准 I/O 接口函数 + +如果需要读写标准输入输出(标准 I/O 接口),调用以下函数/功能,请开启 `RT_USING_POSIX_FS` 和 `RT_USING_POSIX_STDIO` 宏。若读写文件系统中的文件,则仅需开启 `RT_USING_POSIX_FS`,无需开启 `RT_USING_POSIX_STDIO`。 + +```c +/* 标准输出 */ +write(STDOUT_FILENO 或 STDERR_FILENO); +printf(...); /* 该函数仅需在gcc下使能上述两个宏,在其他编译器下,可以直接使用 */ +fprintf(stdout 或 stderr); +fwrite(stdout 或 stderr); +fputs(stdout 或 stderr); +fputc(stdout 或 stderr); +puts(); + +/* 标准输入 */ +getchar(); +read(STDIN_FILENO); +fread(stdin); +fgetc(stdin); +fgets(stdin); +``` + +> 注:如果已经开启 FinSH 功能的话,可以在 FinSH 线程下,使用 finsh_getchar 代替 getchar,来获取从终端键入的字符。 + diff --git a/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-ipc-message.md b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-ipc-message.md new file mode 100644 index 0000000000000000000000000000000000000000..f4e64c37f89108a951e626fe9683eda776861192 --- /dev/null +++ b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-ipc-message.md @@ -0,0 +1,291 @@ + +## 消息队列 + +消息队列是另一种常用的线程间通讯方式,它能够接收来自线程或中断服务例程中不固定长度的消息,并把消息缓存在自己的内存空间中。其他线程也能够从消息队列中读取相应的消息,而当消息队列是空的时候,可以挂起读取线程。当有新的消息到达时,挂起的线程将被唤醒以接收并处理消息。 + +消息队列主要操作包括:通过函数 mq_open() 创建或者打开,调用 mq_send() 发送一条消息到消息队列,调用 mq_receive() 从消息队列获取一条消息,当消息队列不在使用时,可以调用 mq_unlink() 删除消息队列。 + +POSIX 消息队列主要用于进程间通信,RT-Thread 操作系统的 POSIX 消息队列主要是基于 RT-Thread 内核消息队列的一个封装,主要还是用于系统内线程间的通讯。使用方式和 RT-Thread 内核的消息队列差不多。 + +### 消息队列控制块 + +每个消息队列对应一个消息队列控制块,创建消息队列前需要先定义一个消息队列控制块。消息队列控制块定义在 mqueue.h 头文件里。 + +```c +struct mqdes +{ + rt_uint16_t refcount; /* 引用计数 */ + rt_uint16_t unlinked; /* 消息队列的分离状态,值为 1 表示消息队列已经分离 */ + rt_mq_t mq; /* RT-Thread 消息队列控制块 */ + struct mqdes* next; /* 指向下一个消息队列控制块 */ +}; +typedef struct mqdes* mqd_t; /* 消息队列控制块指针类型重定义 */ +``` + +### 创建或打开消息队列 + +```c +mqd_t mq_open(const char *name, int oflag, ...); +``` + +| **参数** | **描述** | +|----------|----------------| +| name | 消息队列名称 | +| oflag | 消息队列打开方式 | +|**返回**| —— | +| 消息队列句柄 | 成功 | +| NULL | 失败 | + +此函数会根据消息队列的名字 name 创建一个新的消息队列或者打开一个已经存在的消息队列。Oflag 的可选值有 0、O_CREAT 或 O_CREAT\|O_EXCL。如果 Oflag 设置为 O_CREAT 则会创建一个新的消息队列。如果 Oflag 设置 O_CREAT\|O_EXCL,如果消息队列已经存在则会返回 NULL,如果不存在则会创建一个新的消息队列。如果 Oflag 设置为 0,消息队列不存在则会返回 NULL。 + +### 分离消息队列 + +```c +int mq_unlink(const char *name); +``` + +| **参数** | **描述** | +|----|------------| +| name | 消息队列名称 | +|**返回**| —— | +| 0 | 成功 | +| -1 | 失败 | + +此函数会根据消息队列名称 name 查找消息队列,若找到,则将消息队列置为分离状态,之后若持有计数为 0,则删除消息队列,并释放消息队列占有的资源。 + +### 关闭消息队列 + +```c +int mq_close(mqd_t mqdes); +``` + +| **参数** | **描述** | +|----------|------------| +| mqdes | 消息队列句柄 | +|**返回**| —— | +| 0 | 成功 | +| -1 | 失败 | + +当一个线程终止时,会对其占用的消息队列执行此关闭操作。不论线程是自愿终止还是非自愿终止都会执行这个关闭操作,相当于是消息队列的持有计数减 1,若减 1 后持有计数为 0,且消息队列处于分离状态,则会删除 mqdes 消息队列并释放其占有的资源。 + +### 阻塞方式发送消息 + +```c +int mq_send(mqd_t mqdes, + const char *msg_ptr, + size_t msg_len, + unsigned msg_prio); +``` + +| **参数** | **描述** | +|---------|----------------------------------| +| mqdes | 消息队列句柄, 不能为 NULL | +| sg_ptr | 指向要发送的消息的指针,不能为 NULL | +| msg_len | 发送的消息的长度 | +| msg_prio | RT-Thread 未实现参数 | +|**返回**| —— | +| 0 | 成功 | +| -1 | 失败 | + +此函数用来向 mqdes 消息队列发送一条消息,是 rt_mq_send() 函数的封装。此函数把 msg_ptr 指向的消息添加到 mqdes 消息队列中,发送的消息长度 msg_len 必须小于或者等于创建消息队列时设置的最大消息长度。 + +如果消息队列已经满,即消息队列中的消息数量等于最大消息数,发送消息的的线程或者中断程序会收到一个错误码(-RT_EFULL)。 + +### 指定阻塞时间发送消息 + +```c +int mq_timedsend(mqd_t mqdes, + const char *msg_ptr, + size_t msg_len, + unsigned msg_prio, + const struct timespec *abs_timeout); +``` + +| **参数** | **描述** | +|------------|-------------------------------------------------| +| mqdes | 消息队列句柄, 不能为 NULL | +| msg_ptr | 指向要发送的消息的指针,不能为 NULL | +| msg_len | 发送的消息的长度 | +| msg_prio | RT-Thread 未实现参数 | +| abs_timeout | 指定的等待时间,单位是操作系统时钟节拍(OS Tick) | +|**返回**| —— | +| 0 | 成功 | +| -1 | 失败 | + +目前 RT-Thread 不支持指定阻塞时间发送消息,但是函数接口已经实现,相当于调用 mq_send()。 + +### 阻塞方式接受消息 + +```c +ssize_t mq_receive(mqd_t mqdes, + char *msg_ptr, + size_t msg_len, + unsigned *msg_prio); +``` + +| **参数** | **描述** | +|---------|----------------------------------| +| mqdes | 消息队列句柄, 不能为 NULL | +| msg_ptr | 指向要发送的消息的指针,不能为 NULL | +| msg_len | 发送的消息的长度 | +| msg_prio | RT-Thread 未实现参数 | +|**返回**| —— | +| 消息长度 | 成功 | +| -1 | 失败 | + +此函数会把 mqdes 消息队列里面最老的消息移除消息队列,并把消息放到 msg_ptr 指向的内存里。如果消息队列为空,调用 mq_receive() 函数的线程将会阻塞,直到消息队列中消息可用。 + +### 指定阻塞时间接受消息 + +```c +ssize_t mq_timedreceive(mqd_t mqdes, + char *msg_ptr, + size_t msg_len, + unsigned *msg_prio, + const struct timespec *abs_timeout); +``` + +| **参数** | **描述** | +|------------|-------------------------------------------------| +| mqdes | 消息队列句柄, 不能为 NULL | +| msg_ptr | 指向要发送的消息的指针,不能为 NULL | +| msg_len | 发送的消息的长度 | +| msg_prio | RT-Thread 未实现参数 | +| abs_timeout | 指定的等待时间,单位是操作系统时钟节拍(OS Tick) | +|**返回**| —— | +| 消息长度 | 成功 | +| -1 | 失败 | + +此函数和 mq_receive() 函数的区别在于,若消息队列为空,线程将阻塞 abs_timeout 时长,超时后函数直接返回 - 1,线程将被唤醒由阻塞态进入就绪态。 + +### 消息队列示例代码 + +这个程序会创建 3 个线程,线程 2 从消息队列接受消息,线程 2 和线程 3 往消息队列发送消息。 + +```c +#include +#include + +/* 线程控制块 */ +static pthread_t tid1; +static pthread_t tid2; +static pthread_t tid3; +/* 消息队列句柄 */ +static mqd_t mqueue; + +/* 函数返回值检查函数 */ +static void check_result(char* str,int result) +{ + if (0 == result) + { + printf("%s successfully!\n",str); + } + else + { + printf("%s failed! error code is %d\n",str,result); + } +} +/* 线程 1 入口函数 */ +static void* thread1_entry(void* parameter) +{ + char buf[128]; + int result; + + while (1) + { + /* 从消息队列中接收消息 */ + result = mq_receive(mqueue, &buf[0], sizeof(buf), 0); + if (result != -1) + { + /* 输出内容 */ + printf("thread1 recv [%s]\n", buf); + } + + /* 休眠 1 秒 */ + // sleep(1); + } +} +/* 线程 2 入口函数 */ +static void* thread2_entry(void* parameter) +{ + int i, result; + char buf[] = "message2 No.x"; + + while (1) + { + for (i = 0; i < 10; i++) + { + buf[sizeof(buf) - 2] = '0' + i; + + printf("thread2 send [%s]\n", buf); + /* 发送消息到消息队列中 */ + result = mq_send(mqueue, &buf[0], sizeof(buf), 0); + if (result == -1) + { + /* 消息队列满, 延迟 1s 时间 */ + printf("thread2:message queue is full, delay 1s\n"); + sleep(1); + } + } + + /* 休眠 2 秒 */ + sleep(2); + } +} +/* 线程 3 入口函数 */ +static void* thread3_entry(void* parameter) +{ + int i, result; + char buf[] = "message3 No.x"; + + while (1) + { + for (i = 0; i < 10; i++) + { + buf[sizeof(buf) - 2] = '0' + i; + + printf("thread3 send [%s]\n", buf); + /* 发送消息到消息队列中 */ + result = mq_send(mqueue, &buf[0], sizeof(buf), 0); + if (result == -1) + { + /* 消息队列满, 延迟 1s 时间 */ + printf("thread3:message queue is full, delay 1s\n"); + sleep(1); + } + } + + /* 休眠 2 秒 */ + sleep(2); + } +} +/* 用户应用入口 */ +int rt_application_init() +{ + int result; + struct mq_attr mqstat; + int oflag = O_CREAT|O_RDWR; +#define MSG_SIZE 128 +#define MAX_MSG 128 + memset(&mqstat, 0, sizeof(mqstat)); + mqstat.mq_maxmsg = MAX_MSG; + mqstat.mq_msgsize = MSG_SIZE; + mqstat.mq_flags = 0; + mqueue = mq_open("mqueue1",O_CREAT,0777,&mqstat); + + /* 创建线程 1, 线程入口是 thread1_entry, 属性参数设为 NULL 选择默认值,入口参数为 NULL*/ + result = pthread_create(&tid1,NULL,thread1_entry,NULL); + check_result("thread1 created",result); + + /* 创建线程 2, 线程入口是 thread2_entry, 属性参数设为 NULL 选择默认值,入口参数为 NULL*/ + result = pthread_create(&tid2,NULL,thread2_entry,NULL); + check_result("thread2 created",result); + + /* 创建线程 3, 线程入口是 thread3_entry, 属性参数设为 NULL 选择默认值,入口参数为 NULL*/ + result = pthread_create(&tid3,NULL,thread3_entry,NULL); + check_result("thread3 created",result); + + + return 0; +} +``` diff --git a/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-ipc-pipe.md b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-ipc-pipe.md new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-ipc-semaphore.md b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-ipc-semaphore.md new file mode 100644 index 0000000000000000000000000000000000000000..3387d7a8048069bc91071d0408281759b605848d --- /dev/null +++ b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-ipc-semaphore.md @@ -0,0 +1,326 @@ +## 信号量 + +信号量可以用于进程与进程之间,或者进程内线程之间的通信。每个信号量都有一个不会小于 0 的信号量值,对应信号量的可用数量。调用 sem_init() 或者 sem_open() 给信号量值赋初值,调用 sem_post() 函数可以让信号量值加 1,调用 sem_wait() 可以让信号量值减 1,如果当前信号量为 0,调用 sem_wait() 的线程被挂起在该信号量的等待队列上,直到信号量值大于 0,处于可用状态。 + +根据信号量的值(代表可用资源的数目)的不同,POSIX 信号量可以分为: + +- 二值信号量:信号量的值只有 0 和 1,初始值指定为 1。这和互斥锁一样,若资源被锁住,信号量的值为 0,若资源可用,则信号量的值为 1。相当于只有一把钥匙,线程拿到钥匙后,完成了对共享资源的访问后需要解锁,把钥匙再放回去,给其他需要此钥匙的线程使用。使用方法和互斥锁一样,等待信号量函数必须和发送信号量函数成对使用,不能单独使用,必须先等待后发送。 + +- 计数信号量:信号量的值在 0 到一个大于 1 的限制值(POSIX 指出系统的最大限制值至少要为 32767)。该计数表示可用信号量个数。此时,发送信号量函数可以被单独调用发送信号量,相当于有多把钥匙,线程拿到一把钥匙就消耗了一把,使用过的钥匙不必在放回去。 + +POSIX 信号量又分为有名信号量和无名信号量: + +- 有名信号量:其值保存在文件中,一般用于进程间同步或互斥。 + +- 无名信号量:其值保存在内存中,一般用于线程间同步或互斥。 + +RT-Thread 操作系统的 POSIX 信号量主要是基于 RT-Thread 内核信号量的一个封装,主要还是用于系统内线程间的通讯。使用方式和 RT-Thread 内核的信号量差不多。 + +### 信号量控制块 + +每个信号量对应一个信号量控制块,创建一个信号量前需要先定义一个 sem_t 信号量控制块。sem_t 是 posix_sem 结构体类型的重定义,定义在 semaphore.h 头文件里。 + +```c +struct posix_sem +{ + rt_uint16_t refcount; + rt_uint8_t unlinked; + rt_uint8_t unamed; + rt_sem_t sem; /* RT-Thread 信号量 */ + struct posix_sem* next; /* 指向下一个信号量控制块 */ +}; +typedef struct posix_sem sem_t; + +rt_sem_t 是 RT-Thread 信号量控制块,定义在 rtdef.h 头文件里。 + +struct rt_semaphore +{ + struct rt_ipc_object parent;/* 继承自 ipc_object 类 */ + rt_uint16_t value; /* 信号量的值 */ +}; +/* rt_sem_t 是指向 semaphore 结构体的指针类型 */ +typedef struct rt_semaphore* rt_sem_t; + +``` + +### 无名信号量 + +无名信号量的值保存在内存中,一般用于线程间同步或互斥。在使用之前,必须先调用 sem_init() 初始化。 + +#### 初始化无名信号量 + +```c +int sem_init(sem_t *sem, int pshared, unsigned int value); +``` + +| **参数** | **描述** | +|-------|--------------------------------------| +| sem | 信号量句柄 | +| value | 信号量初始值,表示信号量资源的可用数量 | +| pshared | RT-Thread 未实现参数 | +|**返回**| —— | +| 0 | 成功 | +| -1 | 失败 | + +此函数初始化一个无名信号量 sem,根据给定的或默认的参数对信号量相关数据结构进行初始化,并把信号量放入信号量链表里。初始化后信号量值为给定的初始值 value。此函数是对 rt_sem_create() 函数的封装。 + +#### 销毁无名信号量 + +```c +int sem_destroy(sem_t *sem); +``` + +| **参数** | **描述** | +|----|----------| +| sem | 信号量句柄 | +|**返回**| —— | +| 0 | 成功 | +| -1 | 失败 | + +此函数会销毁一个无名信号量 sem,并释放信号量占用的资源。 + +### 有名信号量 + +有名信号量,其值保存在文件中,一般用于进程间同步或互斥。两个进程可以操作相同名称的有名信号量。RT-Thread 操作系统中的有名信号量实现和无名信号量差不多,都是设计用于线程间的通信,使用方法也类似。 + +#### 创建或打开有名信号量 + +```c +sem_t *sem_open(const char *name, int oflag, ...); +``` + +| **参数** | **描述** | +|----------|----------------| +| name | 信号量名称 | +| oflag | 信号量的打开方式 | +|**返回**| —— | +| 信号量句柄 | 成功 | +| NULL | 失败 | + +此函数会根据信号量名字 name 创建一个新的信号量或者打开一个已经存在的信号量。Oflag 的可选值有 0、O_CREAT 或 O_CREAT\|O_EXCL。如果 Oflag 设置为 O_CREAT 则会创建一个新的信号量。如果 Oflag 设置 O_CREAT\|O_EXCL,如果信号量已经存在则会返回 NULL,如果不存在则会创建一个新的信号量。如果 Oflag 设置为 0,信号量不存在则会返回 NULL。 + +#### 分离有名信号量 + +```c +int sem_unlink(const char *name); +``` + +| **参数** | **描述** | +|----|----------| +| name | 信号量名称 | +|**返回**| —— | +| 0 | 成功 | +| -1 | 失败,信号量不存在 | + +此函数会根据信号量名称 name 查找该信号量,若信号量存在,则将该信号量标记为分离状态。之后检查引用计数,若值为 0,则立即删除信号量,若值不为 0,则等到所有持有该信号量的线程关闭信号量之后才会删除。 + +#### 关闭有名信号量 + +```c +int sem_close(sem_t *sem); +``` + +| **参数** | **描述** | +|----|----------| +| sem | 信号量句柄 | +|**返回**| —— | +| 0 | 成功 | +| -1 | 失败 | + +当一个线程终止时,会对其占用的信号量执行此关闭操作。不论线程是自愿终止还是非自愿终止都会执行这个关闭操作,相当于是信号量的持有计数减 1。若减 1 后持有计数为 0 且信号量已经处于分离状态,则会删除 sem 信号量并释放其占有的资源。 + +### 获取信号量值 + +```c +int sem_getvalue(sem_t *sem, int *sval); +``` + +| **参数** | **描述** | +|----|---------------------------------| +| sem | 信号量句柄,不能为 NULL | +| sval | 保存获取的信号量值地址, 不能为 NULL | +|**返回**| —— | +| 0 | 成功 | +| -1 | 失败 | + +此函数可以获取 sem 信号量的值,并保存在 sval 指向的内存里,可以知道信号量的资源数量。 + +### 阻塞方式等待信号量 + +```c +int sem_wait(sem_t *sem); +``` + +| **参数** | **描述** | +|----|----------------------| +| sem | 信号量句柄,不能为 NULL | +|**返回**| —— | +| 0 | 成功 | +| -1 | 失败 | + +线程调用此函数获取信号量,是 rt_sem_take(sem,RT_WAITING_FOREVER) 函数的封装。若信号量值大于零,表明信号量可用,线程获得信号量,信号量值减 1。若信号量值等于 0,表明信号量不可用,线程阻塞进入挂起状态,并按照先进先出的方式排队等待,直到信号量可用。 + +### 非阻塞方式获取信号量 + +```c +int sem_trywait(sem_t *sem); +``` + +| **参数** | **描述** | +|----|----------------------| +| sem | 信号量句柄,不能为 NULL | +|**返回**| —— | +| 0 | 成功 | +| -1 | 失败 | + +此函数是 sem_wait() 函数的非阻塞版,是 rt_sem_take(sem,0) 函数的封装。当信号量不可用时,线程不会阻塞,而是直接返回。 + +### 指定阻塞时间等待信号量 + +```c +int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); +``` + +| **参数** | **描述** | +|------------|-------------------------------------------------| +| sem | 信号量句柄,不能为 NULL | +| abs_timeout | 指定的等待时间,单位是操作系统时钟节拍(OS Tick) | +|**返回**| —— | +| 0 | 成功 | +| -1 | 失败 | + +此函数和 sem_wait() 函数的区别在于,若信号量不可用,线程将阻塞 abs_timeout 时长,超时后函数返回 - 1,线程将被唤醒由阻塞态进入就绪态。 + +### 发送信号量 + +```c +int sem_post(sem_t *sem); +``` + +| **参数** | **描述** | +|----|----------------------| +| sem | 信号量句柄,不能为 NULL | +|**返回**| —— | +| 0 | 成功 | +| -1 | 失败 | + +此函数将释放一个 sem 信号量,是 rt_sem_release() 函数的封装。若等待该信号量的线程队列不为空,表明有线程在等待该信号量,第一个等待该信号量的线程将由挂起状态切换到就绪状态,等待系统调度。若没有线程等待该信号量,该信号量值将加 1。 + +### 无名信号量使用示例代码 + +信号量使用的典型案例是生产者消费者模型。一个生产者线程和一个消费者线程对同一块内存进行操作,生产者往共享内存填充数据,消费者从共享内存读取数据。 + +此程序会创建 2 个线程,2 个信号量,一个信号量表示共享数据为空状态,一个信号量表示共享数据不为空状态,一个互斥锁用于保护共享资源。生产者线程生产好数据后会给消费者发送一个 full_sem 信号量,通知消费者线程有数据可用,休眠 2 秒后会等待消费者线程发送的 empty_sem 信号量。消费者线程等到生产者发送的 full_sem 后会处理共享数据,处理完后会给生产者线程发送 empty_sem 信号量。程序会这样一直循环。 + +```c +#include +#include +#include +#include +#include + +/* 静态方式初始化一个互斥锁用于保护共享资源 */ +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +/* 2 个信号量控制块,一个表示资源空信号,一个表示资源满信号 */ +static sem_t empty_sem,full_sem; + +/* 指向线程控制块的指针 */ +static pthread_t tid1; +static pthread_t tid2; + +/* 函数返回值检查 */ +static void check_result(char* str,int result) +{ + if (0 == result) + { + printf("%s successfully!\n",str); + } + else + { + printf("%s failed! error code is %d\n",str,result); + } +} + +/* 生产者生产的结构体数据,存放在链表里 */ +struct node +{ + int n_number; + struct node* n_next; +}; +struct node* head = NULL; /* 链表头, 是共享资源 */ + +/* 消费者线程入口函数 */ +static void* consumer(void* parameter) +{ + struct node* p_node = NULL; + + while (1) + { + sem_wait(&full_sem); + pthread_mutex_lock(&mutex); /* 对互斥锁上锁, */ + + while (head != NULL) /* 判断链表里是否有元素 */ + { + p_node = head; /* 拿到资源 */ + head = head->n_next; /* 头指针指向下一个资源 */ + /* 打印输出 */ + printf("consume %d\n",p_node->n_number); + + free(p_node); /* 拿到资源后释放节点占用的内存 */ + } + + pthread_mutex_unlock(&mutex); /* 临界区数据操作完毕,释放互斥锁 */ + + sem_post(&empty_sem); /* 发送一个空信号量给生产者 */ + } +} +/* 生产者线程入口函数 */ +static void* product(void* patameter) +{ + int count = 0; + struct node *p_node; + + while(1) + { + /* 动态分配一块结构体内存 */ + p_node = (struct node*)malloc(sizeof(struct node)); + if (p_node != NULL) + { + p_node->n_number = count++; + pthread_mutex_lock(&mutex); /* 需要操作 head 这个临界资源,先加锁 */ + + p_node->n_next = head; + head = p_node; /* 往链表头插入数据 */ + + pthread_mutex_unlock(&mutex); /* 解锁 */ + printf("produce %d\n",p_node->n_number); + + sem_post(&full_sem); /* 发送一个满信号量给消费者 */ + } + else + { + printf("product malloc node failed!\n"); + break; + } + sleep(2); /* 休眠 2 秒 */ + sem_wait(&empty_sem); /* 等待消费者发送空信号量 */ + } +} + +int rt_application_init() +{ + int result; + + sem_init(&empty_sem,NULL,0); + sem_init(&full_sem,NULL,0); + /* 创建生产者线程, 属性为默认值,入口函数是 product,入口函数参数为 NULL*/ + result = pthread_create(&tid1,NULL,product,NULL); + check_result("product thread created",result); + + /* 创建消费者线程, 属性为默认值,入口函数是 consumer,入口函数参数是 NULL */ + result = pthread_create(&tid2,NULL,consumer,NULL); + check_result("consumer thread created",result); + + return 0; +} +``` diff --git a/rt-thread-version/rt-thread-standard/programming-manual/posix/posix.md b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-pthread.md similarity index 72% rename from rt-thread-version/rt-thread-standard/programming-manual/posix/posix.md rename to rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-pthread.md index bdc65608eb2ad65bb751b6381c830cbf1ae5d5fe..94aafac00e65e563f5b52b7d5b5ca46a9525b828 100644 --- a/rt-thread-version/rt-thread-standard/programming-manual/posix/posix.md +++ b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-pthread.md @@ -1,5 +1,3 @@ -# POSIX 接口 - ## Pthreads 简介 POSIX Threads 简称 Pthreads,POSIX 是 “Portable Operating System @@ -49,17 +47,16 @@ Std1003.1c-1995)标准里,该标准定义了一套 C 程序语言的类型、 ### 在 RT-Thread 中使用 POSIX -在 RT-Thread 中使用 POSIX API 接口包括几个部分:libc(例如 newlib),filesystem,pthread 等。需要在 rtconfig.h 中打开相关的选项: +在 RT-Thread 中使用 POSIX API 接口包括几个部分:libc(例如 newlib),filesystem,pthread 等。 -```c -#define RT_USING_LIBC -#define RT_USING_DFS -#define RT_USING_DFS_DEVFS -#define RT_USING_PTHREADS -``` +![image-20220419112531014](../figures/menuconfig_pthread.png) + +1. 在 menuconfig 中打开 pthread ,设定支持得最大 pthread 数目 +2. 在用户代码中,即可使用 ```pthread.h, sche.h ``` 这些 pthread 提供的头文件来编程 RT-Thread 实现了 Pthreads 的大部分函数和常量,按照 POSIX 标准定义在 pthread.h、mqueue.h、semaphore.h 和 sched.h 头文件里。Pthreads 是 libc 的一个子库,RT-Thread 中的 Pthreads 是基于 RT-Thread 内核函数的封装,使其符合 POSIX 标准。后续章节会详细介绍 RT-Thread 中实现的 Pthreads 函数及相关功能。 + ## 线程 ### 线程句柄 @@ -1359,625 +1356,6 @@ int rt_application_init() } ``` - -## 信号量 - -信号量可以用于进程与进程之间,或者进程内线程之间的通信。每个信号量都有一个不会小于 0 的信号量值,对应信号量的可用数量。调用 sem_init() 或者 sem_open() 给信号量值赋初值,调用 sem_post() 函数可以让信号量值加 1,调用 sem_wait() 可以让信号量值减 1,如果当前信号量为 0,调用 sem_wait() 的线程被挂起在该信号量的等待队列上,直到信号量值大于 0,处于可用状态。 - -根据信号量的值(代表可用资源的数目)的不同,POSIX 信号量可以分为: - -- 二值信号量:信号量的值只有 0 和 1,初始值指定为 1。这和互斥锁一样,若资源被锁住,信号量的值为 0,若资源可用,则信号量的值为 1。相当于只有一把钥匙,线程拿到钥匙后,完成了对共享资源的访问后需要解锁,把钥匙再放回去,给其他需要此钥匙的线程使用。使用方法和互斥锁一样,等待信号量函数必须和发送信号量函数成对使用,不能单独使用,必须先等待后发送。 - -- 计数信号量:信号量的值在 0 到一个大于 1 的限制值(POSIX 指出系统的最大限制值至少要为 32767)。该计数表示可用信号量个数。此时,发送信号量函数可以被单独调用发送信号量,相当于有多把钥匙,线程拿到一把钥匙就消耗了一把,使用过的钥匙不必在放回去。 - -POSIX 信号量又分为有名信号量和无名信号量: - -- 有名信号量:其值保存在文件中,一般用于进程间同步或互斥。 - -- 无名信号量:其值保存在内存中,一般用于线程间同步或互斥。 - -RT-Thread 操作系统的 POSIX 信号量主要是基于 RT-Thread 内核信号量的一个封装,主要还是用于系统内线程间的通讯。使用方式和 RT-Thread 内核的信号量差不多。 - -### 信号量控制块 - -每个信号量对应一个信号量控制块,创建一个信号量前需要先定义一个 sem_t 信号量控制块。sem_t 是 posix_sem 结构体类型的重定义,定义在 semaphore.h 头文件里。 - -```c -struct posix_sem -{ - rt_uint16_t refcount; - rt_uint8_t unlinked; - rt_uint8_t unamed; - rt_sem_t sem; /* RT-Thread 信号量 */ - struct posix_sem* next; /* 指向下一个信号量控制块 */ -}; -typedef struct posix_sem sem_t; - -rt_sem_t 是 RT-Thread 信号量控制块,定义在 rtdef.h 头文件里。 - -struct rt_semaphore -{ - struct rt_ipc_object parent;/* 继承自 ipc_object 类 */ - rt_uint16_t value; /* 信号量的值 */ -}; -/* rt_sem_t 是指向 semaphore 结构体的指针类型 */ -typedef struct rt_semaphore* rt_sem_t; - -``` - -### 无名信号量 - -无名信号量的值保存在内存中,一般用于线程间同步或互斥。在使用之前,必须先调用 sem_init() 初始化。 - -#### 初始化无名信号量 - -```c -int sem_init(sem_t *sem, int pshared, unsigned int value); -``` - -| **参数** | **描述** | -|-------|--------------------------------------| -| sem | 信号量句柄 | -| value | 信号量初始值,表示信号量资源的可用数量 | -| pshared | RT-Thread 未实现参数 | -|**返回**| —— | -| 0 | 成功 | -| -1 | 失败 | - -此函数初始化一个无名信号量 sem,根据给定的或默认的参数对信号量相关数据结构进行初始化,并把信号量放入信号量链表里。初始化后信号量值为给定的初始值 value。此函数是对 rt_sem_create() 函数的封装。 - -#### 销毁无名信号量 - -```c -int sem_destroy(sem_t *sem); -``` - -| **参数** | **描述** | -|----|----------| -| sem | 信号量句柄 | -|**返回**| —— | -| 0 | 成功 | -| -1 | 失败 | - -此函数会销毁一个无名信号量 sem,并释放信号量占用的资源。 - -### 有名信号量 - -有名信号量,其值保存在文件中,一般用于进程间同步或互斥。两个进程可以操作相同名称的有名信号量。RT-Thread 操作系统中的有名信号量实现和无名信号量差不多,都是设计用于线程间的通信,使用方法也类似。 - -#### 创建或打开有名信号量 - -```c -sem_t *sem_open(const char *name, int oflag, ...); -``` - -| **参数** | **描述** | -|----------|----------------| -| name | 信号量名称 | -| oflag | 信号量的打开方式 | -|**返回**| —— | -| 信号量句柄 | 成功 | -| NULL | 失败 | - -此函数会根据信号量名字 name 创建一个新的信号量或者打开一个已经存在的信号量。Oflag 的可选值有 0、O_CREAT 或 O_CREAT\|O_EXCL。如果 Oflag 设置为 O_CREAT 则会创建一个新的信号量。如果 Oflag 设置 O_CREAT\|O_EXCL,如果信号量已经存在则会返回 NULL,如果不存在则会创建一个新的信号量。如果 Oflag 设置为 0,信号量不存在则会返回 NULL。 - -#### 分离有名信号量 - -```c -int sem_unlink(const char *name); -``` - -| **参数** | **描述** | -|----|----------| -| name | 信号量名称 | -|**返回**| —— | -| 0 | 成功 | -| -1 | 失败,信号量不存在 | - -此函数会根据信号量名称 name 查找该信号量,若信号量存在,则将该信号量标记为分离状态。之后检查引用计数,若值为 0,则立即删除信号量,若值不为 0,则等到所有持有该信号量的线程关闭信号量之后才会删除。 - -#### 关闭有名信号量 - -```c -int sem_close(sem_t *sem); -``` - -| **参数** | **描述** | -|----|----------| -| sem | 信号量句柄 | -|**返回**| —— | -| 0 | 成功 | -| -1 | 失败 | - -当一个线程终止时,会对其占用的信号量执行此关闭操作。不论线程是自愿终止还是非自愿终止都会执行这个关闭操作,相当于是信号量的持有计数减 1。若减 1 后持有计数为 0 且信号量已经处于分离状态,则会删除 sem 信号量并释放其占有的资源。 - -### 获取信号量值 - -```c -int sem_getvalue(sem_t *sem, int *sval); -``` - -| **参数** | **描述** | -|----|---------------------------------| -| sem | 信号量句柄,不能为 NULL | -| sval | 保存获取的信号量值地址, 不能为 NULL | -|**返回**| —— | -| 0 | 成功 | -| -1 | 失败 | - -此函数可以获取 sem 信号量的值,并保存在 sval 指向的内存里,可以知道信号量的资源数量。 - -### 阻塞方式等待信号量 - -```c -int sem_wait(sem_t *sem); -``` - -| **参数** | **描述** | -|----|----------------------| -| sem | 信号量句柄,不能为 NULL | -|**返回**| —— | -| 0 | 成功 | -| -1 | 失败 | - -线程调用此函数获取信号量,是 rt_sem_take(sem,RT_WAITING_FOREVER) 函数的封装。若信号量值大于零,表明信号量可用,线程获得信号量,信号量值减 1。若信号量值等于 0,表明信号量不可用,线程阻塞进入挂起状态,并按照先进先出的方式排队等待,直到信号量可用。 - -### 非阻塞方式获取信号量 - -```c -int sem_trywait(sem_t *sem); -``` - -| **参数** | **描述** | -|----|----------------------| -| sem | 信号量句柄,不能为 NULL | -|**返回**| —— | -| 0 | 成功 | -| -1 | 失败 | - -此函数是 sem_wait() 函数的非阻塞版,是 rt_sem_take(sem,0) 函数的封装。当信号量不可用时,线程不会阻塞,而是直接返回。 - -### 指定阻塞时间等待信号量 - -```c -int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); -``` - -| **参数** | **描述** | -|------------|-------------------------------------------------| -| sem | 信号量句柄,不能为 NULL | -| abs_timeout | 指定的等待时间,单位是操作系统时钟节拍(OS Tick) | -|**返回**| —— | -| 0 | 成功 | -| -1 | 失败 | - -此函数和 sem_wait() 函数的区别在于,若信号量不可用,线程将阻塞 abs_timeout 时长,超时后函数返回 - 1,线程将被唤醒由阻塞态进入就绪态。 - -### 发送信号量 - -```c -int sem_post(sem_t *sem); -``` - -| **参数** | **描述** | -|----|----------------------| -| sem | 信号量句柄,不能为 NULL | -|**返回**| —— | -| 0 | 成功 | -| -1 | 失败 | - -此函数将释放一个 sem 信号量,是 rt_sem_release() 函数的封装。若等待该信号量的线程队列不为空,表明有线程在等待该信号量,第一个等待该信号量的线程将由挂起状态切换到就绪状态,等待系统调度。若没有线程等待该信号量,该信号量值将加 1。 - -### 无名信号量使用示例代码 - -信号量使用的典型案例是生产者消费者模型。一个生产者线程和一个消费者线程对同一块内存进行操作,生产者往共享内存填充数据,消费者从共享内存读取数据。 - -此程序会创建 2 个线程,2 个信号量,一个信号量表示共享数据为空状态,一个信号量表示共享数据不为空状态,一个互斥锁用于保护共享资源。生产者线程生产好数据后会给消费者发送一个 full_sem 信号量,通知消费者线程有数据可用,休眠 2 秒后会等待消费者线程发送的 empty_sem 信号量。消费者线程等到生产者发送的 full_sem 后会处理共享数据,处理完后会给生产者线程发送 empty_sem 信号量。程序会这样一直循环。 - -```c -#include -#include -#include -#include -#include - -/* 静态方式初始化一个互斥锁用于保护共享资源 */ -static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; -/* 2 个信号量控制块,一个表示资源空信号,一个表示资源满信号 */ -static sem_t empty_sem,full_sem; - -/* 指向线程控制块的指针 */ -static pthread_t tid1; -static pthread_t tid2; - -/* 函数返回值检查 */ -static void check_result(char* str,int result) -{ - if (0 == result) - { - printf("%s successfully!\n",str); - } - else - { - printf("%s failed! error code is %d\n",str,result); - } -} - -/* 生产者生产的结构体数据,存放在链表里 */ -struct node -{ - int n_number; - struct node* n_next; -}; -struct node* head = NULL; /* 链表头, 是共享资源 */ - -/* 消费者线程入口函数 */ -static void* consumer(void* parameter) -{ - struct node* p_node = NULL; - - while (1) - { - sem_wait(&full_sem); - pthread_mutex_lock(&mutex); /* 对互斥锁上锁, */ - - while (head != NULL) /* 判断链表里是否有元素 */ - { - p_node = head; /* 拿到资源 */ - head = head->n_next; /* 头指针指向下一个资源 */ - /* 打印输出 */ - printf("consume %d\n",p_node->n_number); - - free(p_node); /* 拿到资源后释放节点占用的内存 */ - } - - pthread_mutex_unlock(&mutex); /* 临界区数据操作完毕,释放互斥锁 */ - - sem_post(&empty_sem); /* 发送一个空信号量给生产者 */ - } -} -/* 生产者线程入口函数 */ -static void* product(void* patameter) -{ - int count = 0; - struct node *p_node; - - while(1) - { - /* 动态分配一块结构体内存 */ - p_node = (struct node*)malloc(sizeof(struct node)); - if (p_node != NULL) - { - p_node->n_number = count++; - pthread_mutex_lock(&mutex); /* 需要操作 head 这个临界资源,先加锁 */ - - p_node->n_next = head; - head = p_node; /* 往链表头插入数据 */ - - pthread_mutex_unlock(&mutex); /* 解锁 */ - printf("produce %d\n",p_node->n_number); - - sem_post(&full_sem); /* 发送一个满信号量给消费者 */ - } - else - { - printf("product malloc node failed!\n"); - break; - } - sleep(2); /* 休眠 2 秒 */ - sem_wait(&empty_sem); /* 等待消费者发送空信号量 */ - } -} - -int rt_application_init() -{ - int result; - - sem_init(&empty_sem,NULL,0); - sem_init(&full_sem,NULL,0); - /* 创建生产者线程, 属性为默认值,入口函数是 product,入口函数参数为 NULL*/ - result = pthread_create(&tid1,NULL,product,NULL); - check_result("product thread created",result); - - /* 创建消费者线程, 属性为默认值,入口函数是 consumer,入口函数参数是 NULL */ - result = pthread_create(&tid2,NULL,consumer,NULL); - check_result("consumer thread created",result); - - return 0; -} -``` - -## 消息队列 - -消息队列是另一种常用的线程间通讯方式,它能够接收来自线程或中断服务例程中不固定长度的消息,并把消息缓存在自己的内存空间中。其他线程也能够从消息队列中读取相应的消息,而当消息队列是空的时候,可以挂起读取线程。当有新的消息到达时,挂起的线程将被唤醒以接收并处理消息。 - -消息队列主要操作包括:通过函数 mq_open() 创建或者打开,调用 mq_send() 发送一条消息到消息队列,调用 mq_receive() 从消息队列获取一条消息,当消息队列不在使用时,可以调用 mq_unlink() 删除消息队列。 - -POSIX 消息队列主要用于进程间通信,RT-Thread 操作系统的 POSIX 消息队列主要是基于 RT-Thread 内核消息队列的一个封装,主要还是用于系统内线程间的通讯。使用方式和 RT-Thread 内核的消息队列差不多。 - -### 消息队列控制块 - -每个消息队列对应一个消息队列控制块,创建消息队列前需要先定义一个消息队列控制块。消息队列控制块定义在 mqueue.h 头文件里。 - -```c -struct mqdes -{ - rt_uint16_t refcount; /* 引用计数 */ - rt_uint16_t unlinked; /* 消息队列的分离状态,值为 1 表示消息队列已经分离 */ - rt_mq_t mq; /* RT-Thread 消息队列控制块 */ - struct mqdes* next; /* 指向下一个消息队列控制块 */ -}; -typedef struct mqdes* mqd_t; /* 消息队列控制块指针类型重定义 */ -``` - -### 创建或打开消息队列 - -```c -mqd_t mq_open(const char *name, int oflag, ...); -``` - -| **参数** | **描述** | -|----------|----------------| -| name | 消息队列名称 | -| oflag | 消息队列打开方式 | -|**返回**| —— | -| 消息队列句柄 | 成功 | -| NULL | 失败 | - -此函数会根据消息队列的名字 name 创建一个新的消息队列或者打开一个已经存在的消息队列。Oflag 的可选值有 0、O_CREAT 或 O_CREAT\|O_EXCL。如果 Oflag 设置为 O_CREAT 则会创建一个新的消息队列。如果 Oflag 设置 O_CREAT\|O_EXCL,如果消息队列已经存在则会返回 NULL,如果不存在则会创建一个新的消息队列。如果 Oflag 设置为 0,消息队列不存在则会返回 NULL。 - -### 分离消息队列 - -```c -int mq_unlink(const char *name); -``` - -| **参数** | **描述** | -|----|------------| -| name | 消息队列名称 | -|**返回**| —— | -| 0 | 成功 | -| -1 | 失败 | - -此函数会根据消息队列名称 name 查找消息队列,若找到,则将消息队列置为分离状态,之后若持有计数为 0,则删除消息队列,并释放消息队列占有的资源。 - -### 关闭消息队列 - -```c -int mq_close(mqd_t mqdes); -``` - -| **参数** | **描述** | -|----------|------------| -| mqdes | 消息队列句柄 | -|**返回**| —— | -| 0 | 成功 | -| -1 | 失败 | - -当一个线程终止时,会对其占用的消息队列执行此关闭操作。不论线程是自愿终止还是非自愿终止都会执行这个关闭操作,相当于是消息队列的持有计数减 1,若减 1 后持有计数为 0,且消息队列处于分离状态,则会删除 mqdes 消息队列并释放其占有的资源。 - -### 阻塞方式发送消息 - -```c -int mq_send(mqd_t mqdes, - const char *msg_ptr, - size_t msg_len, - unsigned msg_prio); -``` - -| **参数** | **描述** | -|---------|----------------------------------| -| mqdes | 消息队列句柄, 不能为 NULL | -| sg_ptr | 指向要发送的消息的指针,不能为 NULL | -| msg_len | 发送的消息的长度 | -| msg_prio | RT-Thread 未实现参数 | -|**返回**| —— | -| 0 | 成功 | -| -1 | 失败 | - -此函数用来向 mqdes 消息队列发送一条消息,是 rt_mq_send() 函数的封装。此函数把 msg_ptr 指向的消息添加到 mqdes 消息队列中,发送的消息长度 msg_len 必须小于或者等于创建消息队列时设置的最大消息长度。 - -如果消息队列已经满,即消息队列中的消息数量等于最大消息数,发送消息的的线程或者中断程序会收到一个错误码(-RT_EFULL)。 - -### 指定阻塞时间发送消息 - -```c -int mq_timedsend(mqd_t mqdes, - const char *msg_ptr, - size_t msg_len, - unsigned msg_prio, - const struct timespec *abs_timeout); -``` - -| **参数** | **描述** | -|------------|-------------------------------------------------| -| mqdes | 消息队列句柄, 不能为 NULL | -| msg_ptr | 指向要发送的消息的指针,不能为 NULL | -| msg_len | 发送的消息的长度 | -| msg_prio | RT-Thread 未实现参数 | -| abs_timeout | 指定的等待时间,单位是操作系统时钟节拍(OS Tick) | -|**返回**| —— | -| 0 | 成功 | -| -1 | 失败 | - -目前 RT-Thread 不支持指定阻塞时间发送消息,但是函数接口已经实现,相当于调用 mq_send()。 - -### 阻塞方式接受消息 - -```c -ssize_t mq_receive(mqd_t mqdes, - char *msg_ptr, - size_t msg_len, - unsigned *msg_prio); -``` - -| **参数** | **描述** | -|---------|----------------------------------| -| mqdes | 消息队列句柄, 不能为 NULL | -| msg_ptr | 指向要发送的消息的指针,不能为 NULL | -| msg_len | 发送的消息的长度 | -| msg_prio | RT-Thread 未实现参数 | -|**返回**| —— | -| 消息长度 | 成功 | -| -1 | 失败 | - -此函数会把 mqdes 消息队列里面最老的消息移除消息队列,并把消息放到 msg_ptr 指向的内存里。如果消息队列为空,调用 mq_receive() 函数的线程将会阻塞,直到消息队列中消息可用。 - -### 指定阻塞时间接受消息 - -```c -ssize_t mq_timedreceive(mqd_t mqdes, - char *msg_ptr, - size_t msg_len, - unsigned *msg_prio, - const struct timespec *abs_timeout); -``` - -| **参数** | **描述** | -|------------|-------------------------------------------------| -| mqdes | 消息队列句柄, 不能为 NULL | -| msg_ptr | 指向要发送的消息的指针,不能为 NULL | -| msg_len | 发送的消息的长度 | -| msg_prio | RT-Thread 未实现参数 | -| abs_timeout | 指定的等待时间,单位是操作系统时钟节拍(OS Tick) | -|**返回**| —— | -| 消息长度 | 成功 | -| -1 | 失败 | - -此函数和 mq_receive() 函数的区别在于,若消息队列为空,线程将阻塞 abs_timeout 时长,超时后函数直接返回 - 1,线程将被唤醒由阻塞态进入就绪态。 - -### 消息队列示例代码 - -这个程序会创建 3 个线程,线程 2 从消息队列接受消息,线程 2 和线程 3 往消息队列发送消息。 - -```c -#include -#include - -/* 线程控制块 */ -static pthread_t tid1; -static pthread_t tid2; -static pthread_t tid3; -/* 消息队列句柄 */ -static mqd_t mqueue; - -/* 函数返回值检查函数 */ -static void check_result(char* str,int result) -{ - if (0 == result) - { - printf("%s successfully!\n",str); - } - else - { - printf("%s failed! error code is %d\n",str,result); - } -} -/* 线程 1 入口函数 */ -static void* thread1_entry(void* parameter) -{ - char buf[128]; - int result; - - while (1) - { - /* 从消息队列中接收消息 */ - result = mq_receive(mqueue, &buf[0], sizeof(buf), 0); - if (result != -1) - { - /* 输出内容 */ - printf("thread1 recv [%s]\n", buf); - } - - /* 休眠 1 秒 */ - // sleep(1); - } -} -/* 线程 2 入口函数 */ -static void* thread2_entry(void* parameter) -{ - int i, result; - char buf[] = "message2 No.x"; - - while (1) - { - for (i = 0; i < 10; i++) - { - buf[sizeof(buf) - 2] = '0' + i; - - printf("thread2 send [%s]\n", buf); - /* 发送消息到消息队列中 */ - result = mq_send(mqueue, &buf[0], sizeof(buf), 0); - if (result == -1) - { - /* 消息队列满, 延迟 1s 时间 */ - printf("thread2:message queue is full, delay 1s\n"); - sleep(1); - } - } - - /* 休眠 2 秒 */ - sleep(2); - } -} -/* 线程 3 入口函数 */ -static void* thread3_entry(void* parameter) -{ - int i, result; - char buf[] = "message3 No.x"; - - while (1) - { - for (i = 0; i < 10; i++) - { - buf[sizeof(buf) - 2] = '0' + i; - - printf("thread3 send [%s]\n", buf); - /* 发送消息到消息队列中 */ - result = mq_send(mqueue, &buf[0], sizeof(buf), 0); - if (result == -1) - { - /* 消息队列满, 延迟 1s 时间 */ - printf("thread3:message queue is full, delay 1s\n"); - sleep(1); - } - } - - /* 休眠 2 秒 */ - sleep(2); - } -} -/* 用户应用入口 */ -int rt_application_init() -{ - int result; - struct mq_attr mqstat; - int oflag = O_CREAT|O_RDWR; -#define MSG_SIZE 128 -#define MAX_MSG 128 - memset(&mqstat, 0, sizeof(mqstat)); - mqstat.mq_maxmsg = MAX_MSG; - mqstat.mq_msgsize = MSG_SIZE; - mqstat.mq_flags = 0; - mqueue = mq_open("mqueue1",O_CREAT,0777,&mqstat); - - /* 创建线程 1, 线程入口是 thread1_entry, 属性参数设为 NULL 选择默认值,入口参数为 NULL*/ - result = pthread_create(&tid1,NULL,thread1_entry,NULL); - check_result("thread1 created",result); - - /* 创建线程 2, 线程入口是 thread2_entry, 属性参数设为 NULL 选择默认值,入口参数为 NULL*/ - result = pthread_create(&tid2,NULL,thread2_entry,NULL); - check_result("thread2 created",result); - - /* 创建线程 3, 线程入口是 thread3_entry, 属性参数设为 NULL 选择默认值,入口参数为 NULL*/ - result = pthread_create(&tid3,NULL,thread3_entry,NULL); - check_result("thread3 created",result); - - - return 0; -} -``` - ## 线程高级编程 本章节会对一些很少使用的属性对象及相关函数做详细介绍。 @@ -2630,83 +2008,3 @@ int mq_getattr(mqd_t mqdes, struct mq_attr *mqstat); |**返回**| —— | | 0 | 成功 | |-1 | 参数无效 | - -## select 函数 - -### select 函数原型: - -```c -int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,struct timeval *timeout); -``` - -#### 【参数说明】 - -* **nfds**:select监视的文件句柄数,一般设为要监视各文件中的最大文件描述符值加1。 -* **readfds**:文件描述符集合监视文件集中的任何文件是否有数据可读,当select函数返回的时候,readfds将清除其中不可读的文件描述符,只留下可读的文件描述符。 -* **writefds**:文件描述符集合监视文件集中的任何文件是否有数据可写,当select函数返回的时候,writefds将清除其中不可写的文件描述符,只留下可写的文件描述符。 -* **exceptfds**:文件集将监视文件集中的任何文件是否发生错误,可用于其他的用途,例如,监视带外数据OOB,带外数据使用MSG\_OOB标志发送到套接字上。当select函数返回的时候,exceptfds将清除其中的其他文件描述符,只留下标记有OOB数据的文件描述符。 -* **timeout** 参数是一个指向 struct timeval 类型的指针,它可以使 select\(\)在等待 timeout 时间后若没有文件描述符准备好则返回。其timeval结构用于指定这段时间的秒数和微秒数。它可以使select处于三种状态: - -> \(1\) 若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止; -> \(2\) 若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值; -> \(3\) timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。 - -timeval 结构体定义 - -```c -struct timeval -{ - int tv_sec; /* 秒 */ - int tv_usec; /* 微妙 */ -}; -``` - -#### 【返回值】 - -* **int**:若有就绪描述符返回其数目,若超时则为0,若出错则为-1 - -下列操作用来设置、清除、判断文件描述符集合。 - -```c -FD_ZERO(fd_set *set); // 清除一个文件描述符集。 -FD_SET(int fd,fd_set *set); // 将一个文件描述符加入文件描述符集中。 -FD_CLR(int fd,fd_set *set); // 将一个文件描述符从文件描述符集中清除。 -FD_ISSET(int fd,fd_set *set); // 判断文件描述符是否被置位 -``` - -fd\_set可以理解为一个集合,这个集合中存放的是文件描述符\(file descriptor\),即文件句柄。中间的三个参数指定我们要让内核测试读、写和异常条件的文件描述符集合。如果对某一个的条件不感兴趣,就可以把它设为空指针。 - -**select\(\)的机制中提供一种fd\_set的数据结构**,实际上是一个long类型的数组,每一个数组元素都能与打开的文件句柄(不管是Socket句柄,还是其他文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成,当调用select\(\)时,由内核根据IO状态修改fd\_set的内容,由此来通知执行了select\(\)的进程哪一Socket或文件可读。 - -## poll 函数 - -### poll 的函数原型: - -```c -int poll(struct pollfd *fds, nfds_t nfds, int timeout); -``` - -#### 【参数说明】 - -* **fds**:fds是一个struct pollfd类型的数组,用于存放需要检测其状态的socket描述符,并且调用poll函数之后fds数组不会被清空;一个pollfd结构体表示一个被监视的文件描述符,通过传递fds指示 poll\(\) 监视多个文件描述符。 - -struct pollfd原型如下: - -```c -typedef struct pollfd { - int fd; // 需要被检测或选择的文件描述符 - short events; // 对文件描述符fd上感兴趣的事件 - short revents; // 文件描述符fd上当前实际发生的事件 -} pollfd_t; -``` - -其中,结构体的events域是监视该文件描述符的事件掩码,由用户来设置这个域,结构体的revents域是文件描述符的操作结果事件掩码,内核在调用返回时设置这个域。 - -* **nfds**:记录数组fds中描述符的总数量。 -* **timeout**:指定等待的毫秒数,无论 I/O 是否准备好,poll\(\) 都会返回,和select函数是类似的。 - -#### 【返回值】 - -* **int**:函数返回fds集合中就绪的读、写,或出错的描述符数量,返回0表示超时,返回-1表示出错; - -poll改变了文件描述符集合的描述方式,使用了pollfd结构而不是select的fd\_set结构,使得poll支持的文件描述符集合限制远大于select的1024。这也是和select不同的地方。 diff --git a/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-timer.md b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-timer.md new file mode 100644 index 0000000000000000000000000000000000000000..94d04e9843506fa53d576573ca61ffeffbc27e00 --- /dev/null +++ b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-timer.md @@ -0,0 +1,29 @@ +## timer 函数 + +![image-20220419112531014](../figures/menuconfig_timer.png) + +1. 在 menuconfig 中打开 timer 即可 +2. 在用户代码中,即可使用 ``` ``` 这些 timer 提供的头文件来编程 + +```c +#include +#include + +int main(void) +{ + time_t sec; + time_t ret; + time_t timestamp = 1609459200; + ret = time(&sec); + + if(ret != sec) + { + return -1; + } + if(ret != timestamp) + { + return -1; + } + return 0; +} +``` \ No newline at end of file diff --git a/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/pse51.md b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/pse51.md deleted file mode 100644 index 8818118008935913199c94f4795a93c526658fda..0000000000000000000000000000000000000000 --- a/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/pse51.md +++ /dev/null @@ -1 +0,0 @@ -# PSE51 \ No newline at end of file