From 1d5f1eb5bac2c26bce9db95179acf9c6b744fad6 Mon Sep 17 00:00:00 2001 From: liuxianliang Date: Thu, 28 Apr 2022 20:50:22 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E3=80=90=E6=B7=BB=E5=8A=A0=E3=80=91POSIX?= =?UTF-8?q?=20=E6=96=87=E6=A1=A3=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rt-thread-standard/_sidebar.md | 1 - .../libc/figures/51_52_53_54.png | Bin 0 -> 186276 bytes .../libc/figures/libc_structure.png | Bin 14947 -> 29593 bytes .../libc/figures/menuconfig_info.png | Bin 0 -> 51136 bytes .../libc/figures/menuconfig_pthread.png | Bin 0 -> 14706 bytes .../libc/figures/menuconfig_timer.png | Bin 0 -> 12754 bytes .../programming-manual/libc/introduction.md | 22 +- .../libc/posix/introduction.md | 44 +- .../programming-manual/libc/posix/pse51.md | 2743 ++++++++++++++++- .../programming-manual/posix/posix.md | 2712 ---------------- 10 files changed, 2803 insertions(+), 2719 deletions(-) create mode 100644 rt-thread-version/rt-thread-standard/programming-manual/libc/figures/51_52_53_54.png create mode 100644 rt-thread-version/rt-thread-standard/programming-manual/libc/figures/menuconfig_info.png create mode 100644 rt-thread-version/rt-thread-standard/programming-manual/libc/figures/menuconfig_pthread.png create mode 100644 rt-thread-version/rt-thread-standard/programming-manual/libc/figures/menuconfig_timer.png delete mode 100644 rt-thread-version/rt-thread-standard/programming-manual/posix/posix.md diff --git a/rt-thread-version/rt-thread-standard/_sidebar.md b/rt-thread-version/rt-thread-standard/_sidebar.md index 8c2a67b..e2913a3 100644 --- a/rt-thread-version/rt-thread-standard/_sidebar.md +++ b/rt-thread-version/rt-thread-standard/_sidebar.md @@ -80,7 +80,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 GIT binary patch literal 186276 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 z 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 e?KqdeusMFBgHQ5Fjkh}_ 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 literal 0 HcmV?d00001 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 GIT binary patch literal 29593 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 z zX}E7AVHigCk!_4lu+}=VnU{e;%J(%!MGuLag|`%WZ0^6~=k$2vO%G=XEd$ynKtHC{ zVgn2(z@0`tLdE)Su59~3IQ%%H0Zb)%LZxiI7;%fG`JeE5h5d==TwY8BLnOhJ!@hy3 z24}x%hK+R`@3ay+StY<0Ocq3Oiugsc;H}V|!NPDcw&5`GJnec@LrQlCO$f0Q@ zUval(esBq;X-%Fo1w8VVP^}S|=6wXF-DJv@j zL%eF-pBp;ktA~R+QV3*Z=Gk#G@R{IERTy-?ig9m$WvE&{dx!>kvyyo7beFL`Pu_n( zjTNkhgi_(ia&oQFbsGxZ%k_8m+zqQk{Rc+sa1^h~m$-k;72Bf(;yG>K@>_XM^wegM zR~>Rwjk8uzPm%JWoEH0X}`2h&)x?mmjeEjMfln=(9==q@IIj9Z)1E$S+is?MB| zxZ5@GD+i@*pA`aOf98W9-`kRr7{=QE2dT^9XzX!mr0$mr*Bh5`y%O3LA3Ep!29Ivu%9t(8HrNEX| zHYa`(vhNM=W61johtJUSE(7XwmxK_oX_*aoBf1kpHQpc0FVZhz^K0fD{#J=1@g!u$ ze$EHtXFjBRxHtYxW4;6}wxVV!qejIl^i5ytI|6cY1jN!91ll5vl1Yg=MX;Pm7x)Yq ztib@)k_1i2r+L@I(V!tCw7Adj&O(3|>YC$-j|W+rX=@vqp)#aZQf>*K_`MKP@`DN4 z6O+J9>{RIjO|RQ-WrNag;Qq%UJeBWA{4bs8)q=)nF^a(@yb2zSYhxYBaM7vzt$mA4 zwL?v!SPM(cCIHbx5a2+_c&e;CLcQwz`E%DXsEITM-bp!F?*xD8m=Vyb=i?+y^F1Y!f6Hu4Otz1Zn+d#RYk=ogUHFbNft9o8<9 zKy;AM^(R$4UD3*wQ$II(gUb{RRYL7i?GPus+G9~0sSud6F-;Iw> zhlKVb$Q>i|(RAjWlb7D`U|G3_=x1r7z3203hib>XJia;j1&S%b6yn~&^$#R|NfF#J z?8#C~k@*-9NSzu+W_zDQb8|BXSl2a)o^0mE2r`hYhilSe?M8K`|IopH*qkxJ(XH9B z(x+N)dyo}hiP@-RwF1A6`1R-cU(UlsS&s1IL+r@M92N(2Wi()&OpYSD-&VNr_Cu$* z*YUJt`olfuw%dfn!T#N!63@2gs_uye)Gw1+2P>ToIvCMM!OW%VVS0gdRyB-5J+!ax^n|by%cFd1I0T z`#2=WV>$7pT%*L=_#q7aq~q2X0@0MtVE8#<<5&@m#x2^Ne?or*E@hONzx25@?V#Mgtr&2L4u65aRzr(E56buhBjbkHWnIxYiC<|^5>9?>s%hP52e)Xx zo+PujZdBfUcYD(*V547!i^?aZS9eh~1zBCtFP1CKv&|WgC`le!mOj=vB^D+r-=DCJ zux^L-rwH+)(8nC~e10V5-d)hj35`_EJ+?eCMYXv*reZT_^&_uLtMpkd)UM|tyU##= zKIi&0(^K>NIx#QbR;jI#tHOudm%`g-06yF+>~Ivii6@A=xC?~;Zq1su)mtPeYP_>b zj#ht-OWra&%*b`in)}{6kV2NWbtnF%P4ddG z`oZb3oD;^CuY6!vE+5(wxyZe_a)C+r#T&x`4fT1|nfxiqX5@^)84j*6<|9Klw0)*Q zqA7Tl*zx7M2elIUTx1F1@Oned4&%co3sQJS_^iKy+wx<;#g*CPaO&7#G(^m#?D

Wce1?%6oa@k4@bu8T;1Zir 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 UG)q0fVCc4MvNvS1F5h$if1w2c#Q*>R 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 GIT binary patch literal 51136 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 literal 0 HcmV?d00001 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 GIT binary patch literal 14706 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 YYhoUZO+vSBU9w*_6)oj5rB|Q+4@j6blK=n! literal 0 HcmV?d00001 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 d3288c4..db67597 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 2c8302e..34ce895 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,6 +1,48 @@ # POSIX 简介 -## 标准输入输出函数 + 可移植操作系统接口,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](../figure/51_52_53_54.png) + + 由于标准的制定是随着时间发展而不断更新的,PSE51, 52, 53, 54 的标准也随着 MCU,MPU 芯片产业的发展,不断增强的芯片功能和性能使得这些标准所对应的四种操作系统的界限而越显模糊。在 RT-Thread 中也依靠自身的组件内容,提供了多种标准的部分内容,为不同的 POSIX 应用提供可供在 RTOS 系统上使用的软件。 + +### 2. POSIX 在 RT-Thread 中支持情况 + + RT-Thread 针对 PSE51 的标准规定的头文件及对应接口,提供了大部分接口,其 PSE51 接口数目占比如下饼状图所示。除此之外,我们还提供了更加完整的文件系统的接口,以及基于我们 SAL 组件的网络接口。这为 POSIX 的应用迁移打下了基础,可以使得较复杂的 POSIX 应用也可以在 RT-Thread 的平台上经过小部分修改就可以在 RT-Thread 上应用。 + +![image-20220419112531014](../figure/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 函数支持。 + +## 3 Net 接口 + +1. 在 menuconfig 中打开 ```Enable BSD Socket I/O ``` 就可以使用标准 Socket 进行编程 + +### 4 Filesystem 接口 + +1. 在 menuconfig 中打开```Enable POSIX file system and I/O``` 配置具体的 POSIX 函数接口 +2. 按照需求打开需要的功能,例如 设备I/O,AIO,MMAN 之类的功能 + +### 5 其他接口 + + 其他接口也是一样,开打对应的 menuconfig 选项,即可使用对应头文件,及提供的函数来编程。整个过程不需要 rt-thread 的头文件参与,使编译出的代码运行在 RT-Thread 操作系统之上。 + +## 特别注明:标准输入输出函数 如果需要读写标准输入输出,调用以下函数/功能,请开启 `RT_USING_POSIX_FS` 和 `RT_USING_POSIX_STDIO` 宏。若读写文件系统中的文件,则仅需开启 `RT_USING_POSIX_FS`,无需开启 `RT_USING_POSIX_STDIO`。 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 index 8818118..a39a925 100644 --- 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 @@ -1 +1,2742 @@ -# PSE51 \ No newline at end of file +# POSIX-PSE51 接口 + +## Pthreads 简介 + +POSIX Threads 简称 Pthreads,POSIX 是 “Portable Operating System +Interface”(可移植操作系统接口) 的缩写,POSIX 是 IEEE Computer +Society 为了提高不同操作系统的兼容性和应用程序的可移植性而制定的一套标准。Pthreads 是线程的 POSIX 标准,被定义在 POSIX.1c, +Threads extensions (IEEE +Std1003.1c-1995)标准里,该标准定义了一套 C 程序语言的类型、函数和常量。定义在 pthread.h 头文件和一个线程库里,大约有 100 个 API,所有 API 都带有 “pthread_” 前缀,可以分为 4 大类: + +- 线程管理(Thread + management):包括线程创建(creating)、分离(detaching)、连接(joining)及设置和查询线程属性的函数等。 + +- 互斥锁(Mutex):“mutual + exclusion” 的缩写,用了限制线程对共享数据的访问,保护共享数据的完整性。包括创建、销毁、锁定和解锁互斥锁及一些用于设置或修改互斥量属性等函数。 + +- 条件变量(Condition + variable):用于共享一个互斥量的线程间的通信。包括条件变量的创建、销毁、等待和发送信号(signal)等函数。 + +- 读写锁(read/write + lock)和屏障(barrier):包括读写锁和屏障的创建、销毁、等待及相关属性设置等函数。 + +- POSIX 信号量(semaphore)和 Pthreads 一起使用,但不是 Pthreads 标准定义的一部分,被定义在 POSIX.1b, + Real-time extensions (IEEE + Std1003.1b-1993)标准里。因此信号量相关函数的前缀是 “sem_” 而不是“pthread_”。 + +- 消息队列(Message + queue)和信号量一样,和 Pthreads 一起使用,也不是 Pthreads 标准定义的一部分,被定义在 IEEE + Std 1003.1-2001 标准里。消息队列相关函数的前缀是 “mq_”。 + +| 函数前缀 | 函数组 | +|----------------------|----------------------| +| `pthread_ ` | 线程本身和各种相关函数 | +| `pthread_attr_` | 线程属性对象 | +| `Pthread_mutex_` | 互斥锁 | +| `pthread_mutexattr_` | 互斥锁属性对象 | +| `pthread_cond_ ` | 条件变量 | +| `pthread_condattr_` | 条件变量属性对象 | +| `pthread_rwlock_` | 读写锁 | +| `pthread_rwlockattr_` | 读写锁属性对象 | +| `pthread_spin_` | 自旋锁 | +| `pthread_barrier_ ` | 屏障 | +| `pthread_barrierattr_` | 屏障属性对象 | +| `sem_` | 信号量 | +| `mq_ ` | 消息队列 | + +绝大部分 Pthreads 的函数执行成功则返回 0 值,不成功则返回一个包含在 `errno.h` 头文件中的错误代码。很多操作系统都支持 Pthreads,比如 Linux、MacOSX、 Android +和 Solaris,因此使用 Pthreads 的函数编写的应用程序有很好的可移植性,可以在很多支持 Pthreads 的平台上直接编译运行。 + +### 在 RT-Thread 中使用 POSIX + +在 RT-Thread 中使用 POSIX API 接口包括几个部分:libc(例如 newlib),filesystem,pthread 等。 + +![image-20220419112531014](../figure/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 函数及相关功能。 + + +## 线程 + +### 线程句柄 + +```c +typedef rt_thread_t pthread_t; +``` + +pthread_t 是 rt_thread_t 类型的重定义,定义在 pthread.h 头文件里。rt_thread_t 是 RT-Thread 的线程句柄(或线程标识符),是指向线程控制块的指针。在创建线程前需要先定义一个 pthread_t 类型的变量。每个线程都对应了自己的线程控制块,线程控制块是操作系统用于控制线程的一个数据结构,它存放了线程的一些信息,例如优先级,线程名称和线程堆栈地址等。线程控制块及线程具体信息在 RT-Thread 编程手册的线程调度与管理一章有详细的介绍。 + +### 创建线程 + +```c +int pthread_create (pthread_t *tid, + const pthread_attr_t *attr, + void *(*start) (void *), void *arg); +``` + +| **参数** | **描述** | +|----------|------------------------------------------------------| +| tid | 指向线程句柄 (线程标识符) 的指针,不能为 NULL | +| attr | 指向线程属性的指针,如果使用 NULL,则使用默认的线程属性 | +| start | 线程入口函数地址 | +| arg | 传递给线程入口函数的参数 | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | +| ENOMEM | 动态分配内存失败 | + +此函数创建一个 pthread 线程。此函数会动态分配 POSIX 线程数据块和 RT-Thread 线程控制块,并把线程控制块的起始地址(线程 ID)保存在参数 tid 指向的内存里,此线程标识符可用于在其他线程中操作此线程;并把 attr 指向的线程属性、start 指向的线程入口函数及入口函数参数 arg 保存在线程数据块和线程控制块里。如果线程创建成功,线程立刻进入就绪态,参与系统的调度,如果线程创建失败,则会释放之前线程占有的资源。 + +关于线程属性及相关函数会在线程高级编程一章里有详细介绍,一般情况下采用默认属性就可以。 + +> [!NOTE] +> 注:创建出 pthread 线程后,如果线程需要重复创建使用,需要设置 pthread 线程为 detach 模式,或者使用 pthread_join 等待创建后的 pthread 线程结束。 + +#### 创建线程示例代码 + +以下程序会初始化 2 个线程,它们拥有共同的入口函数,但是它们的入口参数不相同。其他的,它们具备相同的优先级,并以时间片进行轮转调度。 + +```c +#include +#include +#include + +/* 线程控制块 */ +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); + } +} + +/* 线程入口函数 */ +static void* thread_entry(void* parameter) +{ + int count = 0; + int no = (int) parameter; /* 获得线程的入口参数 */ + + while (1) + { + /* 打印输出线程计数值 */ + printf("thread%d count: %d\n", no, count ++); + + sleep(2); /* 休眠 2 秒 */ + } +} + +/* 用户应用入口 */ +int rt_application_init() +{ + int result; + + /* 创建线程 1, 属性为默认值,入口函数是 thread_entry,入口函数参数是 1 */ + result = pthread_create(&tid1,NULL,thread_entry,(void*)1); + check_result("thread1 created", result); + + /* 创建线程 2, 属性为默认值,入口函数是 thread_entry,入口函数参数是 2 */ + result = pthread_create(&tid2,NULL,thread_entry,(void*)2); + check_result("thread2 created", result); + + return 0; +} +``` + +### 脱离线程 + +```c +int pthread_detach (pthread_t thread); +``` + +| **参数** | **描述** | +|------|----------------------| +| thread | 线程句柄(线程标识符) | +|**返回**| —— | +| 0 | 成功 | + +调用此函数,如果 pthread 线程没有结束,则将 thread 线程属性的分离状态设置为 detached;当 thread 线程已经结束时,系统将回收 pthread 线程占用的资源。 + +使用方法:子线程调用 pthread_detach(pthread_self())(pthread_self() 返回当前调用线程的线程句柄),或者其他线程调用 pthread_detach(thread_id)。关于线程属性的分离状态会在后面详细介绍。 + +> [!NOTE] +> 注:一旦线程属性的分离状态设置为 detached,该线程不能被 pthread_join() 函数等待或者重新被设置为 detached。 + +#### 脱离线程示例代码 + +以下程序会初始化 2 个线程,它们拥有相同的优先级,并按照时间片轮转调度。2 个线程都会被设置为脱离状态,2 个线程循环打印 3 次信息后自动退出,退出后系统将会自动回收其资源。 + +```c +#include +#include +#include + +/* 线程控制块 */ +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); + } +} + +/* 线程 1 入口函数 */ +static void* thread1_entry(void* parameter) +{ + int i; + + printf("i'm thread1 and i will detach myself!\n"); + pthread_detach(pthread_self()); /* 线程 1 脱离自己 */ + + for (i = 0;i < 3;i++) /* 循环打印 3 次信息 */ + { + printf("thread1 run count: %d\n",i); + sleep(2); /* 休眠 2 秒 */ + } + + printf("thread1 exited!\n"); + return NULL; +} + +/* 线程 2 入口函数 */ +static void* thread2_entry(void* parameter) +{ + int i; + + for (i = 0;i < 3;i++) /* 循环打印 3 次信息 */ + { + printf("thread2 run count: %d\n",i); + sleep(2); /* 休眠 2 秒 */ + } + + printf("thread2 exited!\n"); + return NULL; +} +/* 用户应用入口 */ +int rt_application_init() +{ + int result; + + /* 创建线程 1, 属性为默认值,分离状态为默认值 joinable, + * 入口函数是 thread1_entry,入口函数参数为 NULL */ + result = pthread_create(&tid1,NULL,thread1_entry,NULL); + check_result("thread1 created",result); + + /* 创建线程 2, 属性为默认值,分离状态为默认值 joinable, + * 入口函数是 thread2_entry,入口函数参数为 NULL */ + result = pthread_create(&tid2,NULL,thread2_entry,NULL); + check_result("thread2 created",result); + + pthread_detach(tid2); /* 脱离线程 2 */ + + return 0; +} +``` + +### 等待线程结束 + +```c +int pthread_join (pthread_t thread, void**value_ptr); +``` + +| **参数** | **描述** | +|----------|----------------------| +| thread | 线程句柄(线程标识符) | +| value_ptr | 用户定义的指针,用来存储被等待线程的返回值地址,可由函数 pthread_join() 获取 | +|**返回**| —— | +| 0 | 成功 | +| EDEADLK | 线程 join 自己 | +| EINVAL | join 一个分离状态为 detached 的线程 | +| ESRCH | 找不到 thread 线程 | + +此函数会使调用该函数的线程以阻塞的方式等待线程分离属性为 joinable 的 thread 线程运行结束,并获得 thread 线程的返回值,返回值的地址保存在 value_ptr 里,并释放 thread 线程占用的资源。 + +pthread_join() 和 pthread_detach() 函数功能类似,都是在线程结束后用来回收线程占用的资源。线程不能等待自己结束,thread 线程的分离状态必须是 joinable,一个线程只对应一次 `pthread_join()` 调用。分离状态为 joinable 的线程仅当有其他线程对其执行了 `pthread_join()` 后,它所占用的资源才会释放。因此为了避免内存泄漏,所有会结束运行的线程,分离状态要么已设为 detached,要么使用 pthread_join() 来回收其占用的资源。 + +#### 等待线程结束示例代码 + +以下程序代码会初始化 2 个线程,它们拥有相同的优先级,相同优先级的线程是按照时间片轮转调度。2 个线程属性的分离状态为默认值 joinable,线程 1 先开始运行,循环打印 3 次信息后结束。线程 2 调用 pthread_join() 阻塞等待线程 1 结束,并回收线程 1 占用的资源,然后线程 2 每隔 2 秒钟会打印一次信息。 + +```c +#include +#include +#include + +/* 线程控制块 */ +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); + } +} + +/* 线程 1 入口函数 */ +static void* thread1_entry(void* parameter) +{ + int i; + + for (int i = 0;i < 3;i++) /* 循环打印 3 次信息 */ + { + printf("thread1 run count: %d\n",i); + sleep(2); /* 休眠 2 秒 */ + } + + printf("thread1 exited!\n"); + return NULL; +} + +/* 线程 2 入口函数 */ +static void* thread2_entry(void* parameter) +{ + int count = 0; + void* thread1_return_value; + + /* 阻塞等待线程 1 运行结束 */ + pthread_join(tid1, NULL); + + /* 线程 2 打印信息开始输出 */ + while(1) + { + /* 打印线程计数值输出 */ + printf("thread2 run count: %d\n",count ++); + sleep(2); /* 休眠 2 秒 */ + } + + return NULL; +} + +/* 用户应用入口 */ +int rt_application_init() +{ + int result; + /* 创建线程 1, 属性为默认值,分离状态为默认值 joinable, + * 入口函数是 thread1_entry,入口函数参数为 NULL */ + result = pthread_create(&tid1,NULL,thread1_entry,NULL); + check_result("thread1 created",result); + + /* 创建线程 2, 属性为默认值,分离状态为默认值 joinable, + * 入口函数是 thread2_entry,入口函数参数为 NULL */ + result = pthread_create(&tid2,NULL,thread2_entry,NULL); + check_result("thread2 created",result); + + return 0; +} +``` + +### 退出线程 + +```c +void pthread_exit(void *value_ptr); +``` + +| **参数** | **描述** | +|----------|---------------------------| +| value_ptr | 用户定义的指针,用来存储被等待线程的返回值地址,可由函数 pthread_join() 获取 | + +pthread 线程调用此函数会终止执行,如同进程调用 exit() 函数一样,并返回一个指向线程返回值的指针。线程退出由线程自身发起。 + +> [!NOTE] +> 注:若线程的分离状态为 joinable,线程退出后该线程占用的资源并不会被释放,必须调用 pthread_join() 函数释放线程占用的资源。 + +#### 退出线程示例代码 + +这个程序会初始化 2 个线程,它们拥有相同的优先级,相同优先级的线程是按照时间片轮转调度。2 个线程属性的分离状态为默认值 joinable,线程 1 先开始运行,打印一次信息后休眠 2 秒,之后打印退出信息然后结束运行。线程 2 调用 pthread_join() 阻塞等待线程 1 结束,并回收线程 1 占用的资源,然后线程 2 每隔 2 秒钟会打印一次信息。 + +```c +#include +#include +#include + +/* 线程控制块 */ +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); + } +} + +/* 线程 1 入口函数 */ +static void* thread1_entry(void* parameter) +{ + int count = 0; + while(1) + { + /* 打印线程计数值输出 */ + printf("thread1 run count: %d\n",count ++); + sleep(2); /* 休眠 2 秒 */ + printf("thread1 will exit!\n"); + + pthread_exit(0); /* 线程 1 主动退出 */ + } +} + +/* 线程 2 入口函数 */ +static void* thread2_entry(void* parameter) +{ + int count = 0; + + /* 阻塞等待线程 1 运行结束 */ + pthread_join(tid1,NULL); + /* 线程 2 开始输出打印信息 */ + while(1) + { + /* 打印线程计数值输出 */ + printf("thread2 run count: %d\n",count ++); + sleep(2); /* 休眠 2 秒 */ + } +} + +/* 用户应用入口 */ +int rt_application_init() +{ + int result; + + /* 创建线程 1, 属性为默认值,分离状态为默认值 joinable, + * 入口函数是 thread1_entry,入口函数参数为 NULL */ + result = pthread_create(&tid1,NULL,thread1_entry,NULL); + check_result("thread1 created",result); + + /* 创建线程 2, 属性为默认值,分离状态为默认值 joinable, + * 入口函数是 thread2_entry,入口函数参数为 NULL */ + result = pthread_create(&tid2,NULL,thread2_entry,NULL); + check_result("thread2 created",result); + + return 0; +} +``` + +## 互斥锁 + +互斥锁又叫相互排斥的信号量,是一种特殊的二值信号量。互斥锁用来保证共享资源的完整性,保证在任一时刻,只能有一个线程访问该共享资源,线程要访问共享资源,必须先拿到互斥锁,访问完成后需要释放互斥锁。嵌入式的共享资源包括内存、IO、SCI、SPI 等,如果两个线程同时访问共享资源可能会出现问题,因为一个线程可能在另一个线程修改共享资源的过程中使用了该资源,并认为共享资源没有变化。 + +互斥锁的操作只有两种上锁或解锁,同一时刻只会有一个线程持有某个互斥锁。当有线程持有它时,互斥量处于闭锁状态,由这个线程获得它的所有权。相反,当这个线程释放它时,将对互斥量进行开锁,失去它的所有权。当一个线程持有互斥量时,其他线程将不能够对它进行解锁或持有它。 + +对互斥锁的主要操作包括:调用 pthread_mutex_init() 初始化一个互斥锁,调用 pthread_mutex_destroy() 销毁互斥锁,调用 pthread_mutex_lock() 对互斥锁上锁,调 pthread_mutex_unlock() 对互斥锁解锁。 + +使用互斥锁会导致一个潜在问题是线程优先级翻转。在 RT-Thread 操作系统中实现的是优先级继承算法。优先级继承是指,提高某个占有某种资源的低优先级线程的优先级,使之与所有等待该资源的线程中优先级最高的那个线程的优先级相等,然后执行,而当这个低优先级线程释放该资源时,优先级重新回到初始设定。因此,继承优先级的线程避免了系统资源被任何中间优先级的线程抢占。 + +有关优先级反转的详细介绍请参考[《线程间同步》](../ipc1/ipc1.md) 互斥量小节。 + +### 互斥锁控制块 + +每个互斥锁对应一个互斥锁控制块,包含对互斥锁进行的控制的一些信息。创建互斥锁前必须先定义一个 pthread_mutex_t 类型的变量,pthread_mutex_t 是 pthread_mutex 的重定义,pthread_mutex 数据结构定义在 pthread.h 头文件里,数据结构如下: + +```c +struct pthread_mutex +{ + pthread_mutexattr_t attr; /* 互斥锁属性 */ + struct rt_mutex lock; /* RT-Thread 互斥锁控制块 */ +}; +typedef struct pthread_mutex pthread_mutex_t; + +rt_mutex 是 RT-Thread 内核里定义的一个数据结构,定义在 rtdef.h 头文件里,数据结构如下: + +struct rt_mutex +{ + struct rt_ipc_object parent; /* 继承自 ipc_object 类 */ + rt_uint16_t value; /* 互斥锁的值 */ + rt_uint8_t original_priority; /* 持有线程的原始优先级 */ + rt_uint8_t hold; /* 互斥锁持有计数 */ + struct rt_thread *owner; /* 当前拥有互斥锁的线程 */ +}; +typedef struct rt_mutex* rt_mutex_t; /* rt_mutext_t 为指向互斥锁结构体的指针 */ +``` + +### 初始化互斥锁 + +```c +int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); +``` + +| **参数** | **描述** | +|----------|------------------------------------------------------| +| mutex | 互斥锁句柄,不能为 NULL | +| attr | 指向互斥锁属性的指针,若该指针 NULL,则使用默认的属性。 | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | + +此函数会初始化 mutex 互斥锁,并根据 attr 指向的互斥锁属性对象设置 mutex 属性,成功初始化后互斥锁处于未上锁状态,线程可以获取,此函数是对 rt_mutex_init() 函数的封装。 + +除了调用 pthread_mutex_init() 函数创建一个互斥锁,还可以用宏 PTHREAD_MUTEX_INITIALIZER 来静态初始化互斥锁,方法:`pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER`(结构体常量),等同于调用 pthread_mutex_init() 时 attr 指定为 NULL。 + +关于互斥锁属性及相关函数会在线程高级编程一章里有详细介绍,一般情况下采用默认属性就可以。 + +### 销毁互斥锁 + +```c +int pthread_mutex_destroy(pthread_mutex_t *mutex); +``` + +| **参数** | **描述** | +|----------|----------------------| +| mutex | 互斥锁句柄,不能为 NULL | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | mutex 为空或者 mutex 已经被销毁过 | +| EBUSY | 互斥锁正在被使用 | + +此函数会销毁 mutex 互斥锁。销毁后互斥锁 mutex 处于未初始化状态。销毁以后互斥锁的属性和控制块参数将不在有效,但可以调用 pthread_mutex_init() 对销毁后的互斥锁重新初始化。但不需要销毁使用宏 PTHREAD_MUTEX_INITIALIZER 静态初始化的互斥锁。 + +当确定互斥锁没有被锁住,且没有线程阻塞在该互斥锁上,才可以销毁该互斥锁。 + +### 阻塞方式对互斥锁上锁 + +```c +int pthread_mutex_lock(pthread_mutex_t *mutex); +``` + +| **参数** | **描述** | +|----------|----------------------| +| mutex | 互斥锁句柄,不能为 NULL | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | +| EDEADLK | 互斥锁 mutex 不为嵌套锁的情况下线程重复调用此函数 | + +此函数对 mutex 互斥锁上锁,此函数是对 rt_mutex_take() 函数的封装。如果互斥锁 mutex 还没有被上锁,那么申请该互斥锁的线程将成功对该互斥锁上锁。如果互斥锁 mutex 已经被当前线程上锁,且互斥锁类型为嵌套锁,则该互斥锁的持有计数加 1,当前线程也不会挂起等待(死锁),但线程必须对应相同次数的解锁。如果互斥锁 mutex 被其他线程上锁持有,则当前线程将被阻塞,一直到其他线程对该互斥锁解锁后,等待该互斥锁的线程将按照先进先出的原则获取互斥锁。 + +### 非阻塞方式对互斥锁上锁 + +```c +int pthread_mutex_trylock(pthread_mutex_t *mutex); +``` + +| **参数** | **描述** | +|----------|----------------------| +| mutex | 互斥锁句柄,不能为 NULL | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | +| EDEADLK | 互斥锁 mutex 不为嵌套锁的情况下线程重复调用此函数 | +| EBUSY | 互斥锁 mutex 已经被其他线程上锁 | + +此函数是 pthread_mutex_lock() 函数的非阻塞版本。区别在于如果互斥锁 mutex 已经被上锁,线程不会被阻塞,而是马上返回错误码。 + +### 解锁互斥锁 + +```c +int pthread_mutex_unlock(pthread_mutex_t *mutex); +``` + +| **参数** | **描述** | +|----------|----------------------| +| mutex | 互斥锁句柄,不能为 NULL | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | +| EPERM | 互斥锁 mutex 不为嵌套锁的情况下线程重复调用此函数 | +| EBUSY | 解锁其他线程持有的类型为检错锁的互斥锁 | + +调用此函数给 mutex 互斥锁解锁,是对 rt_mutex_release() 函数的封装。当线程完成共享资源的访问后,应尽快释放占有的互斥锁,使得其他线程能及时获取该互斥锁。只有已经拥有互斥锁的线程才能释放它,每释放一次该互斥锁,它的持有计数就减 1。当该互斥量的持有计数为零时(即持有线程已经释放所有的持有操作),互斥锁才变为可用,等待在该互斥锁上的线程将按先进先出方式被唤醒。如果线程的运行优先级被互斥锁提升,那么当互斥锁被释放后,线程恢复为持有互斥锁前的优先级。 + +### 互斥锁示例代码 + +这个程序会初始化 2 个线程,它们拥有相同的优先级,2 个线程都会调用同一个 printer() 函数输出自己的字符串,printer() 函数每次只输出一个字符,之后休眠 1 秒,调用 printer() 函数的线程同样也休眠。如果不使用互斥锁,线程 1 打印了一个字符,休眠后执行线程 2,线程 2 打印一个字符,这样就不能完整的打印线程 1 和线程 2 的字符串,打印出的字符串是混乱的。如果使用了互斥锁保护 2 个线程共享的打印函数 printer(),线程 1 拿到互斥锁后执行 printer() 打印函数打印一个字符,之后休眠 1 秒,这是切换到线程 2,因为互斥锁已经被线程 1 上锁,线程 2 将阻塞,直到线程 1 的字符串打印完整后主动释放互斥锁后线程 2 才会被唤醒。 + +```c +#include +#include +#include + +/* 线程控制块 */ +static pthread_t tid1; +static pthread_t tid2; +/* 互斥锁控制块 */ +static pthread_mutex_t mutex; +/* 线程共享的打印函数 */ +static void printer(char* str) +{ + while(*str != 0) + { + putchar(*str); /* 输出一个字符 */ + str++; + sleep(1); /* 休眠 1 秒 */ + } + printf("\n"); +} +/* 函数返回值检查 */ +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); + } +} +/* 线程入口 */ +static void* thread1_entry(void* parameter) +{ + char* str = "thread1 hello RT-Thread"; + while (1) + { + pthread_mutex_lock(&mutex); /* 互斥锁上锁 */ + + printer(str); /* 访问共享打印函数 */ + + pthread_mutex_unlock(&mutex); /* 访问完成后解锁 */ + + sleep(2); /* 休眠 2 秒 */ + } +} +static void* thread2_entry(void* parameter) +{ + char* str = "thread2 hi world"; + while (1) + { + pthread_mutex_lock(&mutex); /* 互斥锁上锁 */ + + printer(str); /* 访问共享打印函数 */ + + pthread_mutex_unlock(&mutex); /* 访问完成后解锁 */ + + sleep(2); /* 休眠 2 秒 */ + } +} +/* 用户应用入口 */ +int rt_application_init() +{ + int result; + /* 初始化一个互斥锁 */ + pthread_mutex_init(&mutex,NULL); + + /* 创建线程 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); + + return 0; +} +``` + +## 条件变量 + +条件变量其实就是一个信号量,用于线程间同步。条件变量用来阻塞一个线程,当条件满足时向阻塞的线程发送一个条件,阻塞线程就被唤醒,条件变量需要和互斥锁配合使用,互斥锁用来保护共享数据。 + +条件变量可以用来通知共享数据状态。比如一个处理共享资源队列的线程发现队列为空,则此线程只能等待,直到有一个节点被添加到队列中,添加后在发一个条件变量信号激活等待线程。 + +条件变量的主要操作包括:调用 pthread_cond_init() 对条件变量初始化,调用 +pthread_cond_destroy() 销毁一个条件变量,调用 pthread_cond_wait() 等待一个条件变量,调用 pthread_cond_signal() 发送一个条件变量。 + +### 条件变量控制块 + +每个条件变量对应一个条件变量控制块,包括对条件变量进行操作的一些信息。初始化一个条件变量前需要先定义一个 pthread_cond_t 条件变量控制块。pthread_cond_t 是 pthread_cond 结构体类型的重定义,定义在 pthread.h 头文件里。 + +```c +struct pthread_cond +{ + pthread_condattr_t attr; /* 条件变量属性 */ + struct rt_semaphore sem; /* RT-Thread 信号量控制块 */ +}; +typedef struct pthread_cond pthread_cond_t; + +rt_semaphore 是 RT-Thread 内核里定义的一个数据结构,是信号量控制块, +定义在 rtdef.h 头文件里 + +struct rt_semaphore +{ + struct rt_ipc_object parent;/* 继承自 ipc_object 类 */ + rt_uint16_t value; /* 信号量的值 */ +}; +``` + +### 初始化条件变量 + +```c +int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); +``` + +| **参数** | **描述** | +|----|------------------------------------------------| +| cond | 条件变量句柄,不能为 NULL | +| attr | 指向条件变量属性的指针,若为 NULL 则使用默认属性值 | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | + +此函数会初始化 cond 条件变量,并根据 attr 指向的条件变量属性设置其属性,此函数是对 rt_sem_init() 函数的一个封装,基于信号量实现。初始化成功后条件变量处于不可用状态。 + +还可以用宏 PTHREAD_COND_INITIALIZER 静态初始化一个条件变量,方法: +`pthread_cond_t cond = PTHREAD_COND_INITIALIZER`(结构体常量),等同于调用 +pthread_cond_init() 时 attr 指定为 NULL。 + +attr 一般设置 NULL 使用默认值即可,具体会在线程高级编程一章介绍。 + +### 销毁条件变量 + +```c +int pthread_cond_destroy(pthread_cond_t *cond); +``` + +| **参数** | **描述** | +|----|------------------------| +| cond | 条件变量句柄,不能为 NULL | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | +| EPERM | 互斥锁 mutex 不为嵌套锁的情况下线程重复调用此函数 | +| EBUSY | 条件变量正在被使用 | + +此函数会销毁 cond 条件变量,销毁后 cond 处于未初始化状态。销毁之后条件变量的属性及控制块参数将不在有效,但可以调用 pthread_cond_init() 或者静态方式重新初始化。 + +销毁条件变量前需要确定没有线程被阻塞在该条件变量上,也不会等待获取、发信号或者广播。 + +### 阻塞方式获取条件变量 + +```c +int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); +``` + +| **参数** | **描述** | +|----------|----------------------------------| +| cond | 条件变量句柄,不能为 NULL | +| mutex | 指向互斥锁控制块的指针,不能为 NULL | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | + +此函数会以阻塞方式获取 cond 条件变量。线程等待条件变量前需要先将 mutex 互斥锁锁住,此函数首先判断条件变量是否可用,如果不可用则初始化一个条件变量,之后解锁 mutex 互斥锁,然后尝试获取一个信号量,当信号量值大于零时,表明信号量可用,线程将获得信号量,也就获得该条件变量,相应的信号量值会减 1。如果信号量的值等于零,表明信号量不可用,线程将阻塞直到信号量可用,之后将对 mutex 互斥锁再次上锁。 + +### 指定阻塞时间获取条件变量 + +```c +int pthread_cond_timedwait(pthread_cond_t *cond, + pthread_mutex_t *mutex, + const struct timespec *abstime); +``` + +| **参数** | **描述** | +|-------|-------------------------------------------------| +| cond | 条件变量句柄,不能为 NULL | +| mutex | 指向互斥锁控制块的指针,不能为 NULL | +| abstime | 指定的等待时间,单位是操作系统时钟节拍(OS Tick) | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | +| EPERM | 互斥锁 mutex 不为嵌套锁的情况下线程重复调用此函数 | +| ETIMEDOUT | 超时 | + +此函数和 pthread_cond_wait() 函数唯一的差别在于,如果条件变量不可用,线程将被阻塞 abstime 时长,超时后函数将直接返回 ETIMEDOUT 错误码,线程将会被唤醒进入就绪态。 + +### 发送满足条件信号量 + +```c +int pthread_cond_signal(pthread_cond_t *cond); +``` + +| **参数** | **描述** | +|----|------------------------| +| cond | 条件变量句柄,不能为 NULL | +|**返回**| —— | +| 0 | 成功 | + +此函数会发送一个信号且只唤醒一个等待 cond 条件变量的线程,是对 rt_sem_release() 函数的封装,也就是发送一个信号量。当信号量的值等于零,并且有线程等待这个信号量时,将唤醒等待在该信号量线程队列中的第一个线程,由它获取信号量。否则将把信号量的值加 1。 + +### 广播 + +```c +int pthread_cond_broadcast(pthread_cond_t *cond); +``` + +| **参数** | **描述** | +|----|------------------------| +| cond | 条件变量句柄,不能为 NULL | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | + +调用此函数将唤醒所有等待 cond 条件变量的线程。 + +### 条件变量示例代码 + +这个程序是一个生产者消费者模型,有一个生产者线程,一个消费者线程,它们拥有相同的优先级。生产者每隔 2 秒会生产一个数字,放到 head 指向的链表里面,之后调用 pthread_cond_signal() 给消费者线程发信号,通知消费者线程链表里面有数据。消费者线程会调用 pthread_cond_wait() 等待生产者线程发送信号。 + +```c +#include +#include +#include +#include + +/* 静态方式初始化一个互斥锁和一个条件变量 */ +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; + +/* 指向线程控制块的指针 */ +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; + + pthread_mutex_lock(&mutex); /* 对互斥锁上锁 */ + + while (1) + { + while (head == NULL) /* 判断链表里是否有元素 */ + { + pthread_cond_wait(&cond,&mutex); /* 尝试获取条件变量 */ + } + /* + pthread_cond_wait() 会先对 mutex 解锁, + 然后阻塞在等待队列,直到获取条件变量被唤醒, + 被唤醒后,该线程会再次对 mutex 上锁,成功进入临界区。 + */ + + p_node = head; /* 拿到资源 */ + head = head->n_next; /* 头指针指向下一个资源 */ + /* 打印输出 */ + printf("consume %d\n",p_node->n_number); + + free(p_node); /* 拿到资源后释放节点占用的内存 */ + } + pthread_mutex_unlock(&mutex); /* 释放互斥锁 */ + return 0; +} +/* 生产者线程入口函数 */ +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); + + pthread_cond_signal(&cond); /* 发信号唤醒一个线程 */ + + sleep(2); /* 休眠 2 秒 */ + } + else + { + printf("product malloc node failed!\n"); + break; + } + } +} + +int rt_application_init() +{ + int result; + + /* 创建生产者线程, 属性为默认值,入口函数是 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; +} +``` + +## 读写锁 + +读写锁也称为多读者单写者锁。读写锁把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。同一时间只能有一个线程可以占有写模式的读写锁, +但是可以有多个线程同时占有读模式的读写锁。读写锁适合于对数据结构的读次数比写次数多得多的情况,因为读模式锁定时可以共享, +写模式锁定时意味着独占。 + +读写锁通常是基于互斥锁和条件变量实现的。一个线程可以对一个读写锁进行多次读写锁定,同样必须有对应次数的解锁。 + +读写锁的主要操作包括:调用 pthread_rwlock_init() 初始化一个读写锁,写线程调用 pthread_rwlock_wrlock() 对读写锁写锁定,读线程调用 pthread_rwlock_rdlock() 对读写锁读锁定,当不需要使用此读写锁时调用 pthread_rwlock_destroy() 销毁读写锁。 + +### 读写锁控制块 + +每个读写锁对应一个读写锁控制块,包括对读写锁进行操作的一些信息。pthread_rwlock_t 是 pthread_rwlock 数据结构的重定义,定义在 pthread.h 头文件里。在创建一个读写锁之前需要先定义一个 pthread_rwlock_t 类型的数据结构。 + +```c +struct pthread_rwlock +{ + pthread_rwlockattr_t attr; /* 读写锁属性 */ + pthread_mutex_t rw_mutex; /* 互斥锁 */ + pthread_cond_t rw_condreaders; /* 条件变量,供读者线程使用 */ + pthread_cond_t rw_condwriters; /* 条件变量,供写者线程使用 */ + int rw_nwaitreaders; /* 读者线程等待计数 */ + int rw_nwaitwriters; /* 写者线程等待计数 */ + /* 读写锁值,值为 0:未上锁, 值为 - 1:被写者线程锁定, 大于 0 值:被读者线程锁定数量 */ + int rw_refcount; +}; +typedef struct pthread_rwlock pthread_rwlock_t; /* 类型重定义 */ +``` + +### 初始化读写锁初始化 + +```c +int pthread_rwlock_init (pthread_rwlock_t *rwlock, + const pthread_rwlockattr_t *attr); +``` + +| **参数** | **描述** | +|------|-------------------------------------------| +| rwlock | 读写锁句柄,不能为 NULL | +| attr | 指向读写锁属性的指针,RT-Thread 不使用此变量 | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | + +此函数会初始化一个 rwlock 读写锁。此函数使用默认值初始化读写锁控制块的信号量和条件变量,相关计数参数初始为 0 值。初始化后的读写锁处于未上锁状态。 + +还可以使用宏 PTHREAD_RWLOCK_INITIALIZER 来静态初始化读写锁,方法: +pthread_rwlock_t mutex = PTHREAD_RWLOCK_INITIALIZER(结构体常量),等同于调用pthread_rwlock_init() 时 attr 指定为 NULL。 + +attr 一般设置 NULL 使用默认值即可,具体会在线程高级编程一章介绍。 + +### 销毁读写锁 + +```c +int pthread_rwlock_destroy (pthread_rwlock_t *rwlock); +``` + +| **参数** | **描述** | +|------|----------------------| +| rwlock | 读写锁句柄,不能为 NULL | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | +| EBUSY | 读写锁目前正在被使用或者有线程等待该读写锁 | +| EDEADLK | 死锁 | + +此函数会销毁一个 rwlock 读写锁,对应的会销毁读写锁里的互斥锁和条件变量。销毁之后读写锁的属性及控制块参数将不在有效,但可以调用 pthread_rwlock_init() 或者静态方式重新初始化读写锁。 + +### 读写锁读锁定 + +#### 阻塞方式对读写锁读锁定 + +```c +int pthread_rwlock_rdlock (pthread_rwlock_t *rwlock); +``` + +| **参数** | **描述** | +|------|----------------------| +| rwlock | 读写锁句柄,不能为 NULL | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | +| EDEADLK | 死锁 | + +读者线程可以调用此函数来对 rwlock 读写锁进行读锁定。如果读写锁没有被写锁定并且没有写者线程阻塞在该读写锁上,读写线程将成功获取该读写锁。如果读写锁已经被写锁定,读者线程将会阻塞,直到写锁定该读写锁的线程解锁。 + +#### 非阻塞方式对读写锁读锁定 + +```c +int pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock); +``` + +| **参数** | **描述** | +|------|----------------------| +| rwlock | 读写锁句柄,不能为 NULL | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | +| EBUSY | 读写锁目前正在被使用或者有线程等待该读写锁 | +| EDEADLK | 死锁 | + +此函数和 pthread_rwlock_rdlock() 函数的不同在于,如果读写锁已经被写锁定,读者线程不会被阻塞,而是返回一个错误码 EBUSY。 + +#### 指定阻塞时间对读写锁读锁定 + +```c +int pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock, + const struct timespec *abstime); +``` + +| **参数** | **描述** | +|-------|-------------------------------------------------| +| rwlock | 读写锁句柄,不能为 NULL | +| abstime | 指定的等待时间,单位是操作系统时钟节拍(OS Tick) | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | +| ETIMEDOUT | 超时 | +| EDEADLK | 死锁 | + +此函数和 pthread_rwlock_rdlock() 函数的不同在于,如果读写锁已经被写锁定,读者线程将会阻塞指定的 abstime 时长,超时后函数将返回错误码 ETIMEDOUT,线程将会被唤醒进入就绪态。 + +### 读写锁写锁定 + +#### 阻塞方式对读写锁写锁定 + +```c +int pthread_rwlock_wrlock (pthread_rwlock_t *rwlock); +``` + +| **参数** | **描述** | +|------|----------------------| +| rwlock | 读写锁句柄,不能为 NULL | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | +| EDEADLK | 死锁 | + +写者线程调用此函数对 rwlock 读写锁进行写锁定。写锁定读写锁类似互斥量,同一时刻只能有一个线程写锁定读写锁。如果没有线程锁定该读写锁,即读写锁值为 0,调用此函数的写者线程将会写锁定读写锁,其他线程此时都不能获取读写锁,如果已经有线程锁定该读写锁,即读写锁值不为 0,则写线程将被阻塞,直到读写锁解锁。 + +#### 非阻塞方式写锁定读写锁 + +```c +int pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock); +``` + +| **参数** | **描述** | +|------|----------------------| +| rwlock | 读写锁句柄,不能为 NULL | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | +| EBUSY | 读写锁目前被写锁定或者有写着线程阻塞在该读写锁上 | +| EDEADLK | 死锁 | + +此函数和 pthread_rwlock_wrlock() 函数唯一的不同在于,如果已经有线程锁定该读写锁,即读写锁值不为 0,则调用该函数的写者线程会直接返回一个错误代码,线程不会被阻塞。 + +#### 指定阻塞时长写锁定读写锁 + +```c +int pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock, + const struct timespec *abstime); +``` + +| **参数** | **描述** | +|--------------|---------------------| +| rwlock abstime | 读写锁句柄,不能为 NULL 指定的等待时间,单位是操作系统时钟节拍(OS Tick) | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | +| ETIMEDOUT | 超时 | +| EDEADLK | 死锁 | + +此函数和 pthread_rwlock_wrlock() 函数唯一的不同在于,如果已经有线程锁定该读写锁,即读写锁值不为 0,调用线程阻塞指定的 abstime 时长,超时后函数将返回错误码 ETIMEDOUT,线程将会被唤醒进入就绪态。 + +### 读写锁解锁 + +```c +int pthread_rwlock_unlock (pthread_rwlock_t *rwlock); +``` + +| **参数** | **描述** | +|------|----------------------| +| rwlock | 读写锁句柄,不能为 NULL | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | +| EDEADLK | 死锁 | + +此函数可以对 rwlock 读写锁解锁。线程对同一个读写锁加锁多次,必须有同样次数的解锁,若解锁后有多个线程等待对读写锁进行锁定,系统将按照先进先出的规则激活等待的线程。 + +### 读写锁示例代码 + +这个程序有 2 个读者线程,一个写着线程。2 个读者线程先对读写锁读锁定,之后休眠 2 秒,这是其他的读者线程还是可以对该读写锁读锁定,然后读取共享数据。 + +```c +#include +#include +#include + +/* 线程控制块 */ +static pthread_t reader1; +static pthread_t reader2; +static pthread_t writer1; +/* 共享数据 book */ +static int book = 0; +/* 读写锁 */ +static pthread_rwlock_t rwlock; +/* 函数结果检查 */ +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); + } +} +/* 线程入口 */ +static void* reader1_entry(void* parameter) +{ + while (1) + { + + pthread_rwlock_rdlock(&rwlock); /* 尝试读锁定该读写锁 */ + + printf("reader1 read book value is %d\n",book); + sleep(2); /* 线程休眠 2 秒,切换到其他线程运行 */ + + pthread_rwlock_unlock(&rwlock); /* 线程运行后对读写锁解锁 */ + } +} +static void* reader2_entry(void* parameter) +{ + while (1) + { + pthread_rwlock_rdlock(&rwlock); /* 尝试读锁定该读写锁 */ + + printf("reader2 read book value is %d\n",book); + sleep(2); /* 线程休眠 2 秒,切换到其他线程运行 */ + + pthread_rwlock_unlock(&rwlock); /* 线程运行后对读写锁解锁 */ + } +} +static void* writer1_entry(void* parameter) +{ + while (1) + { + pthread_rwlock_wrlock(&rwlock); /* 尝试写锁定该读写锁 */ + + book++; + printf("writer1 write book value is %d\n",book); + + pthread_rwlock_unlock(&rwlock); /* 对读写锁解锁 */ + + sleep(2); /* 线程休眠 2 秒,切换到其他线程运行 */ + } +} +/* 用户应用入口 */ +int rt_application_init() +{ + int result; + /* 默认属性初始化读写锁 */ + pthread_rwlock_init(&rwlock,NULL); + + /* 创建 reader1 线程, 线程入口是 reader1_entry, 线程属性为默认值,入口参数为 NULL*/ + result = pthread_create(&reader1,NULL,reader1_entry,NULL); + check_result("reader1 created",result); + + /* 创建 reader2 线程, 线程入口是 reader2_entry, 线程属性为默认值,入口参数为 NULL*/ + result = pthread_create(&reader2,NULL,reader2_entry,NULL); + check_result("reader2 created",result); + + /* 创建 writer1 线程, 线程入口是 writer1_entry, 线程属性为,入口参数为 NULL*/ + result = pthread_create(&writer1,NULL,writer1_entry,NULL); + check_result("writer1 created",result); + + return 0; +} +``` + +## 屏障 + +屏障是多线程同步的一种方法。barrier 意为屏障或者栏杆,把先后到达的多个线程挡在同一栏杆前,直到所有线程到齐,然后撤下栏杆同时放行。先到达的线程将会阻塞,等到所有调用 pthread_barrier_wait() 函数的线程(数量等于屏障初始化时指定的 count)都到达后,这些线程才会由阻塞状态进入就绪状态再次参与系统调度。 + +屏障是基于条件变量和互斥锁实现的。主要操作包括:调用 pthread_barrier_init() 初始化一个屏障,其他线程调用 pthread_barrier_wait(),所有线程到期后线程唤醒进入准备状态,屏障不在使用调用 pthread_barrier_destroy() 销毁一个屏障。 + +### 屏障控制块 + +创建一个屏障前需要先定义一个 pthread_barrier_t 屏障控制块。pthread_barrier_t 是 pthread_barrier 结构体类型的重定义,定义在 pthread.h 头文件里。 + +```c +struct pthread_barrier +{ + int count; /* 指定的等待线程个数 */ + pthread_cond_t cond; /* 条件变量 */ + pthread_mutex_t mutex; /* 互斥锁 */ +}; +typedef struct pthread_barrier pthread_barrier_t; +``` + +### 创建屏障 + +```c +int pthread_barrier_init(pthread_barrier_t *barrier, + const pthread_barrierattr_t *attr, + unsigned count); +``` + +| **参数** | **描述** | +|-------|-------------------------------| +| attr | 指向屏障属性的指针,传入 NULL,则使用默认值,非 NULL 必须使用 PTHREAD_PROCESS_PRIVATE | +| barrier | 屏障句柄 | +| count | 指定的等待线程个数 | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | + +此函数会创建一个 barrier 屏障,并根据默认的参数对屏障控制块的条件变量和互斥锁初始化,初始化后指定的等待线程个数为 count 个,必须对应 count 个线程调用 pthread_barrier_wait()。 + +attr 一般设置 NULL 使用默认值即可,具体会在线程高级编程一章介绍。 + +### 销毁屏障 + +```c +int pthread_barrier_destroy(pthread_barrier_t *barrier); +``` + +| **参数** | **描述** | +|-------|--------| +| barrier | 屏障句柄 | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | + +此函数会销毁一个 barrier 屏障。销毁之后屏障的属性及控制块参数将不在有效,但可以调用 pthread_barrier_init() 重新初始化。 + +### 等待屏障 + +```c +int pthread_barrier_wait(pthread_barrier_t *barrier); +``` + +| **参数** | **描述** | +|-------|--------| +| barrier | 屏障句柄 | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | + +此函数同步等待在 barrier 前的线程,由每个线程主动调用,若屏障等待线程个数 count 不为 0,count 将减 1,若减 1 后 count 为 0,表明所有线程都已经到达栏杆前,所有到达的线程将被唤醒重新进入就绪状态,参与系统调度。若减一后 count 不为 0,表明还有线程没有到达屏障,调用的线程将阻塞直到所有线程到达屏障。 + +### 屏障示例代码 + +此程序会创建 3 个线程,初始化一个屏障,屏障等待线程数初始化为 3。3 个线程都会调用 pthread_barrier_wait() 等待在屏障前,当 3 个线程都到齐后,3 个线程进入就绪态,之后会每隔 2 秒打印输出计数信息。 + +```c +#include +#include +#include + +/* 线程控制块 */ +static pthread_t tid1; +static pthread_t tid2; +static pthread_t tid3; +/* 屏障控制块 */ +static pthread_barrier_t barrier; +/* 函数返回值检查函数 */ +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) +{ + int count = 0; + + printf("thread1 have arrived the barrier!\n"); + pthread_barrier_wait(&barrier); /* 到达屏障,并等待其他线程到达 */ + + while (1) + { + /* 打印线程计数值输出 */ + printf("thread1 count: %d\n",count ++); + + /* 休眠 2 秒 */ + sleep(2); + } +} +/* 线程 2 入口函数 */ +static void* thread2_entry(void* parameter) +{ + int count = 0; + + printf("thread2 have arrived the barrier!\n"); + pthread_barrier_wait(&barrier); + + while (1) + { + /* 打印线程计数值输出 */ + printf("thread2 count: %d\n",count ++); + + /* 休眠 2 秒 */ + sleep(2); + } +} +/* 线程 3 入口函数 */ +static void* thread3_entry(void* parameter) +{ + int count = 0; + + printf("thread3 have arrived the barrier!\n"); + pthread_barrier_wait(&barrier); + + while (1) + { + /* 打印线程计数值输出 */ + printf("thread3 count: %d\n",count ++); + + /* 休眠 2 秒 */ + sleep(2); + } +} +/* 用户应用入口 */ +int rt_application_init() +{ + int result; + pthread_barrier_init(&barrier,NULL,3); + + /* 创建线程 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); + +} +``` + +## 信号量 + +信号量可以用于进程与进程之间,或者进程内线程之间的通信。每个信号量都有一个不会小于 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; +} +``` + +## 线程高级编程 + +本章节会对一些很少使用的属性对象及相关函数做详细介绍。 + +RT-Thread 实现的线程属性包括线程栈大小、线程优先级、线程分离状态、线程调度策略。pthread_create() 使用属性对象前必须先对属性对象进行初始化。设置线程属性之类的 API 函数应在创建线程之前就调用。线程属性的变更不会影响到已创建的线程。 + +线程属性结构 pthread_attr_t 定义在 pthread.h 头文件里。线程属性结构如下: + +```c +/* pthread_attr_t 类型重定义 */ +typedef struct pthread_attr pthread_attr_t; +/* 线程属性结构体 */ +struct pthread_attr +{ + void* stack_base; /* 线程栈的地址 */ + rt_uint32_t stack_size; /* 线程栈大小 */ + rt_uint8_t priority; /* 线程优先级 */ + rt_uint8_t detachstate; /* 线程的分离状态 */ + rt_uint8_t policy; /* 线程调度策略 */ + rt_uint8_t inheritsched; /* 线程的继承性 */ +}; +``` + +#### 线程属性初始化及去初始化 + +线程属性初始化及去初始化函数如下所示: + +```c +int pthread_attr_init(pthread_attr_t *attr); +int pthread_attr_destroy(pthread_attr_t *attr); +``` + +| **参数** | **描述** | +|----|------------------| +| attr | 指向线程属性的指针 | +|**返回**| —— | +| 0 | 成功 | + +使用 pthread_attr_init() 函数会使用默认值初始化线程属性结构体 attr,等同于调用线程初始化函数时将此参数设置为 NULL,使用前需要定义一个 pthread_attr_t 属性对象,此函数必须在 pthread_create() 函数之前调用。 + +pthread_attr_destroy() 函数对 attr 指向的属性去初始化,之后可以再次调用 pthread_attr_init() 函数对此属性对象重新初始化。 + +#### 线程的分离状态 + +设置线程的分离状态 / 获取线程的分离状态如下所示,默认情况下线程是非分离状态。 + +```c +int pthread_attr_setdetachstate(pthread_attr_t *attr, int state); +int pthread_attr_getdetachstate(pthread_attr_t const *attr, int *state); +``` + +| **参数** | **描述** | +|----------|-------------------| +| attr | 指向线程属性的指针 | +| state | 线程分离状态 | +|**返回**| —— | +| 0 | 成功 | + +线程分离状态属性值 state 可以是 PTHREAD_CREATE_JOINABL(非分离)和 +PTHREAD_CREATE_DETACHED(分离)。 + +线程的分离状态决定一个线程以什么样的方式来回收自己运行结束后占用的资源。线程的分离状态有 2 种:joinable 或者 detached。当线程创建后,应该调用 pthread_join() 或者 pthread_detach() 回收线程结束运行后占用的资源。如果线程的分离状态为 joinable 其他线程可以调用 pthread_join() 函数等待该线程结束并获取线程返回值,然后回收线程占用的资源。分离状态为 detached 的线程不能被其他的线程所 join,自己运行结束后,马上释放系统资源。 + +#### 线程的调度策略 + +设置 \ 获取线程调度策略函数如下所示: + +```c +int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy); +int pthread_attr_getschedpolicy(pthread_attr_t const *attr, int *policy); +``` + +只实现了函数接口,默认不同优先级基于优先级调度,同一优先级时间片轮询调度 + +#### 线程的调度参数 + +设置线程的优先级 / 获取线程的优先级函数如下所示: + +```c +int pthread_attr_setschedparam(pthread_attr_t *attr, + struct sched_param const *param); +int pthread_attr_getschedparam(pthread_attr_t const *attr, + struct sched_param *param); +``` + +| **参数** | **描述** | +|----------|------------------| +| attr | 指向线程属性的指针 | +| param | 指向调度参数的指针 | +|**返回**| —— | +| 0 | 成功 | + +pthread_attr_setschedparam() 函数设置线程的优先级。使用 param 对线程属性优先级赋值。 + +**参数** struct sched_param 定义在 sched.h 里,结构如下: + +```c +struct sched_param +{ + int sched_priority; /* 线程优先级 */ +}; +``` + +结构体 sched_param 的成员 sched_priority 控制线程的优先级值。 + +#### 线程的堆栈大小 + +设置 / 获取 线程的堆栈大小的函数如下所示: + +```c +int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stack_size); +int pthread_attr_getstacksize(pthread_attr_t const *attr, size_t *stack_size); +``` + +| **参数** | **描述** | +|-----------|------------------| +| attr | 指向线程属性的指针 | +| stack_size | 线程堆栈大小 | +|**返回**| —— | +| 0 | 成功 | + +pthread_attr_setstacksize() 函数可以设置堆栈大小,单位是字节。在大多数系统中需要做栈空间地址对齐(例如 ARM 体系结构中需要向 4 字节地址对齐)。 + +#### 线程堆栈大小和地址 + +设置 / 获取 线程的堆栈地址和堆栈大小的函数如下所示: + +```c +int pthread_attr_setstack(pthread_attr_t *attr, + void *stack_base, + size_t stack_size); +int pthread_attr_getstack(pthread_attr_t const *attr, + void**stack_base, + size_t *stack_size); +``` + +| **参数** | **描述** | +|-----------|------------------| +| attr | 指向线程属性的指针 | +| stack_size | 线程堆栈大小 | +| stack_base | 线程堆栈地址 | +|**返回**| —— | +| 0 | 成功 | + +#### 线程属性相关函数 + +设置 / 获取线程的作用域的函数如下所示: + +```c +int pthread_attr_setscope(pthread_attr_t *attr, int scope); +int pthread_attr_getscope(pthread_attr_t const *attr); +``` + +| **参数** | **描述** | +|-----------|------------------| +| attr | 指向线程属性的指针 | +| scope | 线程作用域 | +|**返回**| —— | +| 0 | scope 为 PTHREAD_SCOPE_SYSTEM | +| EOPNOTSUPP | scope 为 PTHREAD_SCOPE_PROCESS | +| EINVAL | scope 为 PTHREAD_SCOPE_SYSTEM | + +#### 线程属性示例代码 + +这个程序会初始化 2 个线程,它们拥有共同的入口函数,但是它们的入口参数不相同。最先创建的线程会使用提供的 attr 线程属性,另外一个线程使用系统默认的属性。线程的优先级是很重要的一个参数,因此这个程序会修改第一个创建的线程的优先级为 8,而系统默认的优先级为 24。 + +```c +#include +#include +#include +#include + +/* 线程控制块 */ +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); + } +} +/* 线程入口函数 */ +static void* thread_entry(void* parameter) +{ + int count = 0; + int no = (int) parameter; /* 获得线程的入口参数 */ + + while (1) + { + /* 打印输出线程计数值 */ + printf("thread%d count: %d\n", no, count ++); + + sleep(2); /* 休眠 2 秒 */ + } +} + +/* 用户应用入口 */ +int rt_application_init() +{ + int result; + pthread_attr_t attr; /* 线程属性 */ + struct sched_param prio; /* 线程优先级 */ + + prio.sched_priority = 8; /* 优先级设置为 8 */ + pthread_attr_init(&attr); /* 先使用默认值初始化属性 */ + pthread_attr_setschedparam(&attr,&prio); /* 修改属性对应的优先级 */ + + /* 创建线程 1, 属性为 attr,入口函数是 thread_entry,入口函数参数是 1 */ + result = pthread_create(&tid1,&attr,thread_entry,(void*)1); + check_result("thread1 created",result); + + /* 创建线程 2, 属性为默认值,入口函数是 thread_entry,入口函数参数是 2 */ + result = pthread_create(&tid2,NULL,thread_entry,(void*)2); + check_result("thread2 created",result); + + return 0; +} +``` + +### 线程取消 + +取消是一种让一个线程可以结束其它线程运行的机制。一个线程可以对另一个线程发送一个取消请求。依据设置的不同,目标线程可能会置之不理,可能会立即结束也可能会将它推迟到下一个取消点才结束。 + +#### 发送取消请求 + +可使用如下函数发送取消请求: + +```c +int pthread_cancel(pthread_t thread); +``` + +| **参数** | **描述** | +|------|--------| +| thread | 线程句柄 | +|**返回**| —— | +| 0 | 成功 | + +此函数发送取消请求给 thread 线程。Thread 线程是否会对取消请求做出回应以及什么时候做出回应依赖于线程取消的状态及类型。 + +#### 设置取消状态 + +可使用如下函数设置取消请求: + +```c +int pthread_setcancelstate(int state, int *oldstate); +``` + +| **参数** | **描述** | +|--------|-------------------------------| +| state | 有两种值:PTHREAD_CANCEL_ENABLE:取消使能 PTHREAD_CANCEL_DISABLE:取消不使能(线程创建时的默认值) | +| oldstate | 保存原来的取消状态 | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | state 非 PTHREAD_CANCEL_ENABLE 或者 PTHREAD_CANCEL_DISABLE | + +此函数设置取消状态,由线程自己调用。取消使能的线程将会对取消请求做出反应,而取消没有使能的线程不会对取消请求做出反应。 + +#### 设置取消类型 + +可使用如下函数设置取消类型,由线程自己调用: + +```c +int pthread_setcanceltype(int type, int *oldtype); +``` + +| **参数** | **描述** | +|-------|---------------------------------| +| type | 有 2 种值:PTHREAD_CANCEL_DEFFERED:线程收到取消请求后继续运行至下一个取消点再结束。(线程创建时的默认值)PTHREAD_CANCEL_ASYNCHRONOUS:线程立即结束。 | +| oldtype | 保存原来的取消类型 | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | state 非 PTHREAD_CANCEL_DEFFERED 或者 PTHREAD_CANCEL_ASYNCHRONOUS | + +#### 设置取消点 + +可使用如下函数设置取消点: + +```c +void pthread_testcancel(void); +``` + +此函数在线程调用的地方创建一个取消点。主要由不包含取消点的线程调用,可以回应取消请求。如果在取消状态处于禁用状态下调用 pthread_testcancel(),则该函数不起作用。 + +#### 取消点 + +取消点也就是线程接受取消请求后会结束运行的地方,根据 POSIX 标准,pthread_join()、pthread_testcancel()、pthread_cond_wait()、pthread_cond_timedwait()、sem_wait() 等会引起阻塞的系统调用都是取消点。 + +RT-Thread 包含的所有取消点如下: + +- mq_receive() + +- mq_send() + +- mq_timedreceive() + +- mq_timedsend() + +- msgrcv() + +- msgsnd() + +- msync() + +- pthread_cond_timedwait() + +- pthread_cond_wait() + +- pthread_join() + +- pthread_testcancel() + +- sem_timedwait() + +- sem_wait() + +- pthread_rwlock_rdlock() + +- pthread_rwlock_timedrdlock() + +- pthread_rwlock_timedwrlock() + +- pthread_rwlock_wrlock() + +#### 线程取消示例代码 + +此程序会创建 2 个线程,线程 2 开始运行后马上休眠 8 秒,线程 1 设置了自己的取消状态和类型,之后在一个无限循环里打印运行计数信息。线程 2 唤醒后向线程 1 发送取消请求,线程 1 收到取消请求后马上结束运行。 + +```c +#include +#include +#include + +/* 线程控制块 */ +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); + } +} +/* 线程 1 入口函数 */ +static void* thread1_entry(void* parameter) +{ + int count = 0; + /* 设置线程 1 的取消状态使能,取消类型为线程收到取消点后马上结束 */ + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + + while(1) + { + /* 打印线程计数值输出 */ + printf("thread1 run count: %d\n",count ++); + sleep(2); /* 休眠 2 秒 */ + } +} +/* 线程 2 入口函数 */ +static void* thread2_entry(void* parameter) +{ + int count = 0; + sleep(8); + /* 向线程 1 发送取消请求 */ + pthread_cancel(tid1); + /* 阻塞等待线程 1 运行结束 */ + pthread_join(tid1,NULL); + printf("thread1 exited!\n"); + /* 线程 2 打印信息开始输出 */ + while(1) + { + /* 打印线程计数值输出 */ + printf("thread2 run count: %d\n",count ++); + sleep(2); /* 休眠 2 秒 */ + } +} +/* 用户应用入口 */ +int rt_application_init() +{ + int result; + /* 创建线程 1, 属性为默认值,分离状态为默认值 joinable, + 入口函数是 thread1_entry,入口函数参数为 NULL */ + result = pthread_create(&tid1,NULL,thread1_entry,NULL); + check_result("thread1 created",result); + + /* 创建线程 2, 属性为默认值,分离状态为默认值 joinable, + 入口函数是 thread2_entry,入口函数参数为 NULL */ + result = pthread_create(&tid2,NULL,thread2_entry,NULL); + check_result("thread2 created",result); + + return 0; +} +``` + +### 一次性初始化 + +可使用如下函数一次性初始化: + +```c +int pthread_once(pthread_once_t * once_control, void (*init_routine) (void)); +``` + +| **参数** | **描述** | +|-------------|--------| +| once_control | 控制变量 | +| init_routine | 执行函数 | +|**返回**| —— | +| 0 | 成功 | + +有时候我们需要对一些变量只进行一次初始化。如果我们进行多次初始化程序就会出现错误。在传统的顺序编程中,一次性初始化经常通过使用布尔变量来管理。控制变量被静态初始化为 0,而任何依赖于初始化的代码都能测试该变量。如果变量值仍然为 0,则它能实行初始化,然后将变量置为 1。以后检查的代码将跳过初始化。 + +### 线程结束后清理 + +线程清理函数接口如下所示: + +```c +void pthread_cleanup_pop(int execute); +void pthread_cleanup_push(void (*routine)(void*), void *arg); +``` + +| **参数** | **描述** | +|-------|-----------------------------| +| execute | 0 或 1,决定是否执行 cleanup 函数 | +| routine | 指向清理函数的指针 | +| arg | 传递给清理函数的参数 | + +pthread_cleanup_push() 把指定的清理函数 routine 放到线程的清理函数链表里, pthread_cleanup_pop() 从清理函数链表头部取出第一项函数,若 execute 为非 0 值,则执行此函数。 + +### 其他线程相关函数 + +#### 判断 2 个线程是否相等 + +```c +int pthread_equal (pthread_t t1, pthread_t t2); +``` + +| **参数** | **描述** | +|----------|--------| +| pthread_t | 线程句柄 | +|**返回**| —— | +| 0 | 不相等 | +| 1 | 相等 | + +#### 获取线程句柄 + +```c +pthread_t pthread_self (void); +``` +pthread_self() 返回调用线程的句柄。 + +#### 获取最大最小优先级 + +```c +int sched_get_priority_min(int policy); +int sched_get_priority_max(int policy); +``` + +| **参数** | **描述** | +|------|---------------------------------| +| policy | 2 个值可选:SCHED_FIFO,SCHED_RR | + +sched_get_priority_min() 返回值为 0,RT-Thread 里为最大优先级, sched_get_priority_max() 返回值最小优先级。 + +### 互斥锁属性 + +RT-Thread 实现的互斥锁属性包括互斥锁类型和互斥锁作用域。 + +#### 互斥锁属性初始化及去初始化 + +```c +int pthread_mutexattr_init(pthread_mutexattr_t *attr); +int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); +``` + +| **参数** | **描述** | +|----|------------------------| +| attr | 指向互斥锁属性对象的指针 | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | + +pthread_mutexattr_init() 函数将使用默认值初始化 attr 指向的属性对象,等同于调用 pthread_mutex_init() 函数时将属性参数设置为 NULL。 + +pthread_mutexattr_destroy() 函数将会对 attr 指向的属性对象去初始化,之后可以调用 pthread_mutexattr_init() 函数重新初始化。 + +#### 互斥锁作用域 + +```c +int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared); +int pthread_mutexattr_getpshared(pthread_mutexattr_t *attr, int *pshared); +``` + +| **参数** | **描述** | +|-------|--------------------| +| type | 互斥锁类型 | +| pshared | 有 2 个可选值: PTHREAD_PROCESS_PRIVATE:默认值,用于仅同步该进程中的线程。PTHREAD_PROCESS_SHARED:用于同步该进程和其他进程中的线程。 | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | + +#### 互斥锁类型 + +```c +int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type); +int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type); +``` + +| **参数** | **描述** | +|----|------------------------| +| type | 互斥锁类型 | +| attr | 指向互斥锁属性对象的指针 | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | + +互斥锁的类型决定了一个线程在获取一个互斥锁时的表现方式,RT-Thread 实现了 3 种互斥锁类型: + +- PTHREAD_MUTEX_NORMAL:普通锁,当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按先进先出方式获得锁。如果一个线程在不首先解除互斥锁的情况下尝试重新获得该互斥锁,不会产生死锁,而是返回错误码,和检错锁一样。 + +- PTHREAD_MUTEX_RECURSIVE:嵌套锁,允许一个线程对同一个锁成功获得多次,需要相同次数的解锁释放该互斥锁。 + +- PTHREAD_MUTEX_ERRORCHECK:检错锁,如果一个线程在不首先解除互斥锁的情况下尝试重新获得该互斥锁,则返回错误。这样就保证当不允许多次加锁时不会出现死锁。 + +### 条件变量属性 + +使用默认值 PTHREAD_PROCESS_PRIVATE 初始化条件变量属性 attr 可使用如下函数: + +```c +int pthread_condattr_init(pthread_condattr_t *attr); +``` + +| **参数** | **描述** | +|----|--------------------------| +| attr | 指向条件变量属性对象的指针 | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | + +#### 获取条件变量作用域 + +```c +int pthread_mutexattr_getpshared(pthread_mutexattr_t *attr, int *pshared); +``` + +| **参数** | **描述** | +|----|--------------------------| +| attr | 指向条件变量属性对象的指针 | +|**返回**| —— | +| 0 | 成功 | +| EINVAL | 参数无效 | + +### 读写锁属性 + +#### 初始化属性 + +```c +int pthread_rwlockattr_init (pthread_rwlockattr_t *attr); +``` + +| **参数** | **描述** | +|----|--------------------| +| attr | 指向读写锁属性的指针 | +|**返回**| —— | +| 0 | 成功 | +|-1 | 参数无效 | + +该函数会使用默认值 PTHREAD_PROCESS_PRIVATE 初始化读写锁属性 attr。 + +#### 获取作用域 + +```c +int pthread_rwlockattr_getpshared (const pthread_rwlockattr_t *attr, int *pshared); +``` + +| **参数** | **描述** | +|-------|--------------------------| +| attr | 指向读写锁属性的指针 | +| pshared | 指向保存读写锁作用域的指针 | +|**返回**| —— | +| 0 | 成功 | +|-1 | 参数无效 | + +pshared 指向的内存保存的值为 PTHREAD_PROCESS_PRIVATE。 + +### 屏障属性 + +#### 初始化属性 + +```c +int pthread_barrierattr_init(pthread_barrierattr_t *attr); +``` + +| **参数** | **描述** | +|----|------------------| +| attr | 指向屏障属性的指针 | +|**返回**| —— | +| 0 | 成功 | +|-1 | 参数无效 | + +改函数会使用默认值 PTHREAD_PROCESS_PRIVATE 初始化屏障属性 attr。 + +#### 获取作用域 + +```c +int pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr, int *pshared); +``` + +| **参数** | **描述** | +|-------|-----------------------------| +| attr | 指向屏障属性的指针 | +| pshared | 指向保存屏障作用域数据的指针 | +|**返回**| —— | +| 0 | 成功 | +|-1 | 参数无效 | + +### 消息队列属性 + +消息队列属性控制块如下: + +```c +struct mq_attr +{ + long mq_flags; /* 消息队列的标志,用来表示是否阻塞 */ + long mq_maxmsg; /* 消息队列最大消息数 */ + long mq_msgsize; /* 消息队列每个消息的最大字节数 */ + long mq_curmsgs; /* 消息队列当前消息数 */ +}; +``` +#### 获取属性 +```c +int mq_getattr(mqd_t mqdes, struct mq_attr *mqstat); +``` + +| **参数** | **描述** | +|------|------------------------| +| mqdes | 指向消息队列控制块的指针 | +| 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不同的地方。 + + +## timer 函数 + +![image-20220419112531014](../figure/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/posix/posix.md b/rt-thread-version/rt-thread-standard/programming-manual/posix/posix.md deleted file mode 100644 index bdc6560..0000000 --- a/rt-thread-version/rt-thread-standard/programming-manual/posix/posix.md +++ /dev/null @@ -1,2712 +0,0 @@ -# POSIX 接口 - -## Pthreads 简介 - -POSIX Threads 简称 Pthreads,POSIX 是 “Portable Operating System -Interface”(可移植操作系统接口) 的缩写,POSIX 是 IEEE Computer -Society 为了提高不同操作系统的兼容性和应用程序的可移植性而制定的一套标准。Pthreads 是线程的 POSIX 标准,被定义在 POSIX.1c, -Threads extensions (IEEE -Std1003.1c-1995)标准里,该标准定义了一套 C 程序语言的类型、函数和常量。定义在 pthread.h 头文件和一个线程库里,大约有 100 个 API,所有 API 都带有 “pthread_” 前缀,可以分为 4 大类: - -- 线程管理(Thread - management):包括线程创建(creating)、分离(detaching)、连接(joining)及设置和查询线程属性的函数等。 - -- 互斥锁(Mutex):“mutual - exclusion” 的缩写,用了限制线程对共享数据的访问,保护共享数据的完整性。包括创建、销毁、锁定和解锁互斥锁及一些用于设置或修改互斥量属性等函数。 - -- 条件变量(Condition - variable):用于共享一个互斥量的线程间的通信。包括条件变量的创建、销毁、等待和发送信号(signal)等函数。 - -- 读写锁(read/write - lock)和屏障(barrier):包括读写锁和屏障的创建、销毁、等待及相关属性设置等函数。 - -- POSIX 信号量(semaphore)和 Pthreads 一起使用,但不是 Pthreads 标准定义的一部分,被定义在 POSIX.1b, - Real-time extensions (IEEE - Std1003.1b-1993)标准里。因此信号量相关函数的前缀是 “sem_” 而不是“pthread_”。 - -- 消息队列(Message - queue)和信号量一样,和 Pthreads 一起使用,也不是 Pthreads 标准定义的一部分,被定义在 IEEE - Std 1003.1-2001 标准里。消息队列相关函数的前缀是 “mq_”。 - -| 函数前缀 | 函数组 | -|----------------------|----------------------| -| `pthread_ ` | 线程本身和各种相关函数 | -| `pthread_attr_` | 线程属性对象 | -| `Pthread_mutex_` | 互斥锁 | -| `pthread_mutexattr_` | 互斥锁属性对象 | -| `pthread_cond_ ` | 条件变量 | -| `pthread_condattr_` | 条件变量属性对象 | -| `pthread_rwlock_` | 读写锁 | -| `pthread_rwlockattr_` | 读写锁属性对象 | -| `pthread_spin_` | 自旋锁 | -| `pthread_barrier_ ` | 屏障 | -| `pthread_barrierattr_` | 屏障属性对象 | -| `sem_` | 信号量 | -| `mq_ ` | 消息队列 | - -绝大部分 Pthreads 的函数执行成功则返回 0 值,不成功则返回一个包含在 `errno.h` 头文件中的错误代码。很多操作系统都支持 Pthreads,比如 Linux、MacOSX、 Android -和 Solaris,因此使用 Pthreads 的函数编写的应用程序有很好的可移植性,可以在很多支持 Pthreads 的平台上直接编译运行。 - -### 在 RT-Thread 中使用 POSIX - -在 RT-Thread 中使用 POSIX API 接口包括几个部分:libc(例如 newlib),filesystem,pthread 等。需要在 rtconfig.h 中打开相关的选项: - -```c -#define RT_USING_LIBC -#define RT_USING_DFS -#define RT_USING_DFS_DEVFS -#define RT_USING_PTHREADS -``` - -RT-Thread 实现了 Pthreads 的大部分函数和常量,按照 POSIX 标准定义在 pthread.h、mqueue.h、semaphore.h 和 sched.h 头文件里。Pthreads 是 libc 的一个子库,RT-Thread 中的 Pthreads 是基于 RT-Thread 内核函数的封装,使其符合 POSIX 标准。后续章节会详细介绍 RT-Thread 中实现的 Pthreads 函数及相关功能。 - -## 线程 - -### 线程句柄 - -```c -typedef rt_thread_t pthread_t; -``` - -pthread_t 是 rt_thread_t 类型的重定义,定义在 pthread.h 头文件里。rt_thread_t 是 RT-Thread 的线程句柄(或线程标识符),是指向线程控制块的指针。在创建线程前需要先定义一个 pthread_t 类型的变量。每个线程都对应了自己的线程控制块,线程控制块是操作系统用于控制线程的一个数据结构,它存放了线程的一些信息,例如优先级,线程名称和线程堆栈地址等。线程控制块及线程具体信息在 RT-Thread 编程手册的线程调度与管理一章有详细的介绍。 - -### 创建线程 - -```c -int pthread_create (pthread_t *tid, - const pthread_attr_t *attr, - void *(*start) (void *), void *arg); -``` - -| **参数** | **描述** | -|----------|------------------------------------------------------| -| tid | 指向线程句柄 (线程标识符) 的指针,不能为 NULL | -| attr | 指向线程属性的指针,如果使用 NULL,则使用默认的线程属性 | -| start | 线程入口函数地址 | -| arg | 传递给线程入口函数的参数 | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | -| ENOMEM | 动态分配内存失败 | - -此函数创建一个 pthread 线程。此函数会动态分配 POSIX 线程数据块和 RT-Thread 线程控制块,并把线程控制块的起始地址(线程 ID)保存在参数 tid 指向的内存里,此线程标识符可用于在其他线程中操作此线程;并把 attr 指向的线程属性、start 指向的线程入口函数及入口函数参数 arg 保存在线程数据块和线程控制块里。如果线程创建成功,线程立刻进入就绪态,参与系统的调度,如果线程创建失败,则会释放之前线程占有的资源。 - -关于线程属性及相关函数会在线程高级编程一章里有详细介绍,一般情况下采用默认属性就可以。 - -> [!NOTE] -> 注:创建出 pthread 线程后,如果线程需要重复创建使用,需要设置 pthread 线程为 detach 模式,或者使用 pthread_join 等待创建后的 pthread 线程结束。 - -#### 创建线程示例代码 - -以下程序会初始化 2 个线程,它们拥有共同的入口函数,但是它们的入口参数不相同。其他的,它们具备相同的优先级,并以时间片进行轮转调度。 - -```c -#include -#include -#include - -/* 线程控制块 */ -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); - } -} - -/* 线程入口函数 */ -static void* thread_entry(void* parameter) -{ - int count = 0; - int no = (int) parameter; /* 获得线程的入口参数 */ - - while (1) - { - /* 打印输出线程计数值 */ - printf("thread%d count: %d\n", no, count ++); - - sleep(2); /* 休眠 2 秒 */ - } -} - -/* 用户应用入口 */ -int rt_application_init() -{ - int result; - - /* 创建线程 1, 属性为默认值,入口函数是 thread_entry,入口函数参数是 1 */ - result = pthread_create(&tid1,NULL,thread_entry,(void*)1); - check_result("thread1 created", result); - - /* 创建线程 2, 属性为默认值,入口函数是 thread_entry,入口函数参数是 2 */ - result = pthread_create(&tid2,NULL,thread_entry,(void*)2); - check_result("thread2 created", result); - - return 0; -} -``` - -### 脱离线程 - -```c -int pthread_detach (pthread_t thread); -``` - -| **参数** | **描述** | -|------|----------------------| -| thread | 线程句柄(线程标识符) | -|**返回**| —— | -| 0 | 成功 | - -调用此函数,如果 pthread 线程没有结束,则将 thread 线程属性的分离状态设置为 detached;当 thread 线程已经结束时,系统将回收 pthread 线程占用的资源。 - -使用方法:子线程调用 pthread_detach(pthread_self())(pthread_self() 返回当前调用线程的线程句柄),或者其他线程调用 pthread_detach(thread_id)。关于线程属性的分离状态会在后面详细介绍。 - -> [!NOTE] -> 注:一旦线程属性的分离状态设置为 detached,该线程不能被 pthread_join() 函数等待或者重新被设置为 detached。 - -#### 脱离线程示例代码 - -以下程序会初始化 2 个线程,它们拥有相同的优先级,并按照时间片轮转调度。2 个线程都会被设置为脱离状态,2 个线程循环打印 3 次信息后自动退出,退出后系统将会自动回收其资源。 - -```c -#include -#include -#include - -/* 线程控制块 */ -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); - } -} - -/* 线程 1 入口函数 */ -static void* thread1_entry(void* parameter) -{ - int i; - - printf("i'm thread1 and i will detach myself!\n"); - pthread_detach(pthread_self()); /* 线程 1 脱离自己 */ - - for (i = 0;i < 3;i++) /* 循环打印 3 次信息 */ - { - printf("thread1 run count: %d\n",i); - sleep(2); /* 休眠 2 秒 */ - } - - printf("thread1 exited!\n"); - return NULL; -} - -/* 线程 2 入口函数 */ -static void* thread2_entry(void* parameter) -{ - int i; - - for (i = 0;i < 3;i++) /* 循环打印 3 次信息 */ - { - printf("thread2 run count: %d\n",i); - sleep(2); /* 休眠 2 秒 */ - } - - printf("thread2 exited!\n"); - return NULL; -} -/* 用户应用入口 */ -int rt_application_init() -{ - int result; - - /* 创建线程 1, 属性为默认值,分离状态为默认值 joinable, - * 入口函数是 thread1_entry,入口函数参数为 NULL */ - result = pthread_create(&tid1,NULL,thread1_entry,NULL); - check_result("thread1 created",result); - - /* 创建线程 2, 属性为默认值,分离状态为默认值 joinable, - * 入口函数是 thread2_entry,入口函数参数为 NULL */ - result = pthread_create(&tid2,NULL,thread2_entry,NULL); - check_result("thread2 created",result); - - pthread_detach(tid2); /* 脱离线程 2 */ - - return 0; -} -``` - -### 等待线程结束 - -```c -int pthread_join (pthread_t thread, void**value_ptr); -``` - -| **参数** | **描述** | -|----------|----------------------| -| thread | 线程句柄(线程标识符) | -| value_ptr | 用户定义的指针,用来存储被等待线程的返回值地址,可由函数 pthread_join() 获取 | -|**返回**| —— | -| 0 | 成功 | -| EDEADLK | 线程 join 自己 | -| EINVAL | join 一个分离状态为 detached 的线程 | -| ESRCH | 找不到 thread 线程 | - -此函数会使调用该函数的线程以阻塞的方式等待线程分离属性为 joinable 的 thread 线程运行结束,并获得 thread 线程的返回值,返回值的地址保存在 value_ptr 里,并释放 thread 线程占用的资源。 - -pthread_join() 和 pthread_detach() 函数功能类似,都是在线程结束后用来回收线程占用的资源。线程不能等待自己结束,thread 线程的分离状态必须是 joinable,一个线程只对应一次 `pthread_join()` 调用。分离状态为 joinable 的线程仅当有其他线程对其执行了 `pthread_join()` 后,它所占用的资源才会释放。因此为了避免内存泄漏,所有会结束运行的线程,分离状态要么已设为 detached,要么使用 pthread_join() 来回收其占用的资源。 - -#### 等待线程结束示例代码 - -以下程序代码会初始化 2 个线程,它们拥有相同的优先级,相同优先级的线程是按照时间片轮转调度。2 个线程属性的分离状态为默认值 joinable,线程 1 先开始运行,循环打印 3 次信息后结束。线程 2 调用 pthread_join() 阻塞等待线程 1 结束,并回收线程 1 占用的资源,然后线程 2 每隔 2 秒钟会打印一次信息。 - -```c -#include -#include -#include - -/* 线程控制块 */ -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); - } -} - -/* 线程 1 入口函数 */ -static void* thread1_entry(void* parameter) -{ - int i; - - for (int i = 0;i < 3;i++) /* 循环打印 3 次信息 */ - { - printf("thread1 run count: %d\n",i); - sleep(2); /* 休眠 2 秒 */ - } - - printf("thread1 exited!\n"); - return NULL; -} - -/* 线程 2 入口函数 */ -static void* thread2_entry(void* parameter) -{ - int count = 0; - void* thread1_return_value; - - /* 阻塞等待线程 1 运行结束 */ - pthread_join(tid1, NULL); - - /* 线程 2 打印信息开始输出 */ - while(1) - { - /* 打印线程计数值输出 */ - printf("thread2 run count: %d\n",count ++); - sleep(2); /* 休眠 2 秒 */ - } - - return NULL; -} - -/* 用户应用入口 */ -int rt_application_init() -{ - int result; - /* 创建线程 1, 属性为默认值,分离状态为默认值 joinable, - * 入口函数是 thread1_entry,入口函数参数为 NULL */ - result = pthread_create(&tid1,NULL,thread1_entry,NULL); - check_result("thread1 created",result); - - /* 创建线程 2, 属性为默认值,分离状态为默认值 joinable, - * 入口函数是 thread2_entry,入口函数参数为 NULL */ - result = pthread_create(&tid2,NULL,thread2_entry,NULL); - check_result("thread2 created",result); - - return 0; -} -``` - -### 退出线程 - -```c -void pthread_exit(void *value_ptr); -``` - -| **参数** | **描述** | -|----------|---------------------------| -| value_ptr | 用户定义的指针,用来存储被等待线程的返回值地址,可由函数 pthread_join() 获取 | - -pthread 线程调用此函数会终止执行,如同进程调用 exit() 函数一样,并返回一个指向线程返回值的指针。线程退出由线程自身发起。 - -> [!NOTE] -> 注:若线程的分离状态为 joinable,线程退出后该线程占用的资源并不会被释放,必须调用 pthread_join() 函数释放线程占用的资源。 - -#### 退出线程示例代码 - -这个程序会初始化 2 个线程,它们拥有相同的优先级,相同优先级的线程是按照时间片轮转调度。2 个线程属性的分离状态为默认值 joinable,线程 1 先开始运行,打印一次信息后休眠 2 秒,之后打印退出信息然后结束运行。线程 2 调用 pthread_join() 阻塞等待线程 1 结束,并回收线程 1 占用的资源,然后线程 2 每隔 2 秒钟会打印一次信息。 - -```c -#include -#include -#include - -/* 线程控制块 */ -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); - } -} - -/* 线程 1 入口函数 */ -static void* thread1_entry(void* parameter) -{ - int count = 0; - while(1) - { - /* 打印线程计数值输出 */ - printf("thread1 run count: %d\n",count ++); - sleep(2); /* 休眠 2 秒 */ - printf("thread1 will exit!\n"); - - pthread_exit(0); /* 线程 1 主动退出 */ - } -} - -/* 线程 2 入口函数 */ -static void* thread2_entry(void* parameter) -{ - int count = 0; - - /* 阻塞等待线程 1 运行结束 */ - pthread_join(tid1,NULL); - /* 线程 2 开始输出打印信息 */ - while(1) - { - /* 打印线程计数值输出 */ - printf("thread2 run count: %d\n",count ++); - sleep(2); /* 休眠 2 秒 */ - } -} - -/* 用户应用入口 */ -int rt_application_init() -{ - int result; - - /* 创建线程 1, 属性为默认值,分离状态为默认值 joinable, - * 入口函数是 thread1_entry,入口函数参数为 NULL */ - result = pthread_create(&tid1,NULL,thread1_entry,NULL); - check_result("thread1 created",result); - - /* 创建线程 2, 属性为默认值,分离状态为默认值 joinable, - * 入口函数是 thread2_entry,入口函数参数为 NULL */ - result = pthread_create(&tid2,NULL,thread2_entry,NULL); - check_result("thread2 created",result); - - return 0; -} -``` - -## 互斥锁 - -互斥锁又叫相互排斥的信号量,是一种特殊的二值信号量。互斥锁用来保证共享资源的完整性,保证在任一时刻,只能有一个线程访问该共享资源,线程要访问共享资源,必须先拿到互斥锁,访问完成后需要释放互斥锁。嵌入式的共享资源包括内存、IO、SCI、SPI 等,如果两个线程同时访问共享资源可能会出现问题,因为一个线程可能在另一个线程修改共享资源的过程中使用了该资源,并认为共享资源没有变化。 - -互斥锁的操作只有两种上锁或解锁,同一时刻只会有一个线程持有某个互斥锁。当有线程持有它时,互斥量处于闭锁状态,由这个线程获得它的所有权。相反,当这个线程释放它时,将对互斥量进行开锁,失去它的所有权。当一个线程持有互斥量时,其他线程将不能够对它进行解锁或持有它。 - -对互斥锁的主要操作包括:调用 pthread_mutex_init() 初始化一个互斥锁,调用 pthread_mutex_destroy() 销毁互斥锁,调用 pthread_mutex_lock() 对互斥锁上锁,调 pthread_mutex_unlock() 对互斥锁解锁。 - -使用互斥锁会导致一个潜在问题是线程优先级翻转。在 RT-Thread 操作系统中实现的是优先级继承算法。优先级继承是指,提高某个占有某种资源的低优先级线程的优先级,使之与所有等待该资源的线程中优先级最高的那个线程的优先级相等,然后执行,而当这个低优先级线程释放该资源时,优先级重新回到初始设定。因此,继承优先级的线程避免了系统资源被任何中间优先级的线程抢占。 - -有关优先级反转的详细介绍请参考[《线程间同步》](../ipc1/ipc1.md) 互斥量小节。 - -### 互斥锁控制块 - -每个互斥锁对应一个互斥锁控制块,包含对互斥锁进行的控制的一些信息。创建互斥锁前必须先定义一个 pthread_mutex_t 类型的变量,pthread_mutex_t 是 pthread_mutex 的重定义,pthread_mutex 数据结构定义在 pthread.h 头文件里,数据结构如下: - -```c -struct pthread_mutex -{ - pthread_mutexattr_t attr; /* 互斥锁属性 */ - struct rt_mutex lock; /* RT-Thread 互斥锁控制块 */ -}; -typedef struct pthread_mutex pthread_mutex_t; - -rt_mutex 是 RT-Thread 内核里定义的一个数据结构,定义在 rtdef.h 头文件里,数据结构如下: - -struct rt_mutex -{ - struct rt_ipc_object parent; /* 继承自 ipc_object 类 */ - rt_uint16_t value; /* 互斥锁的值 */ - rt_uint8_t original_priority; /* 持有线程的原始优先级 */ - rt_uint8_t hold; /* 互斥锁持有计数 */ - struct rt_thread *owner; /* 当前拥有互斥锁的线程 */ -}; -typedef struct rt_mutex* rt_mutex_t; /* rt_mutext_t 为指向互斥锁结构体的指针 */ -``` - -### 初始化互斥锁 - -```c -int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); -``` - -| **参数** | **描述** | -|----------|------------------------------------------------------| -| mutex | 互斥锁句柄,不能为 NULL | -| attr | 指向互斥锁属性的指针,若该指针 NULL,则使用默认的属性。 | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | - -此函数会初始化 mutex 互斥锁,并根据 attr 指向的互斥锁属性对象设置 mutex 属性,成功初始化后互斥锁处于未上锁状态,线程可以获取,此函数是对 rt_mutex_init() 函数的封装。 - -除了调用 pthread_mutex_init() 函数创建一个互斥锁,还可以用宏 PTHREAD_MUTEX_INITIALIZER 来静态初始化互斥锁,方法:`pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER`(结构体常量),等同于调用 pthread_mutex_init() 时 attr 指定为 NULL。 - -关于互斥锁属性及相关函数会在线程高级编程一章里有详细介绍,一般情况下采用默认属性就可以。 - -### 销毁互斥锁 - -```c -int pthread_mutex_destroy(pthread_mutex_t *mutex); -``` - -| **参数** | **描述** | -|----------|----------------------| -| mutex | 互斥锁句柄,不能为 NULL | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | mutex 为空或者 mutex 已经被销毁过 | -| EBUSY | 互斥锁正在被使用 | - -此函数会销毁 mutex 互斥锁。销毁后互斥锁 mutex 处于未初始化状态。销毁以后互斥锁的属性和控制块参数将不在有效,但可以调用 pthread_mutex_init() 对销毁后的互斥锁重新初始化。但不需要销毁使用宏 PTHREAD_MUTEX_INITIALIZER 静态初始化的互斥锁。 - -当确定互斥锁没有被锁住,且没有线程阻塞在该互斥锁上,才可以销毁该互斥锁。 - -### 阻塞方式对互斥锁上锁 - -```c -int pthread_mutex_lock(pthread_mutex_t *mutex); -``` - -| **参数** | **描述** | -|----------|----------------------| -| mutex | 互斥锁句柄,不能为 NULL | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | -| EDEADLK | 互斥锁 mutex 不为嵌套锁的情况下线程重复调用此函数 | - -此函数对 mutex 互斥锁上锁,此函数是对 rt_mutex_take() 函数的封装。如果互斥锁 mutex 还没有被上锁,那么申请该互斥锁的线程将成功对该互斥锁上锁。如果互斥锁 mutex 已经被当前线程上锁,且互斥锁类型为嵌套锁,则该互斥锁的持有计数加 1,当前线程也不会挂起等待(死锁),但线程必须对应相同次数的解锁。如果互斥锁 mutex 被其他线程上锁持有,则当前线程将被阻塞,一直到其他线程对该互斥锁解锁后,等待该互斥锁的线程将按照先进先出的原则获取互斥锁。 - -### 非阻塞方式对互斥锁上锁 - -```c -int pthread_mutex_trylock(pthread_mutex_t *mutex); -``` - -| **参数** | **描述** | -|----------|----------------------| -| mutex | 互斥锁句柄,不能为 NULL | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | -| EDEADLK | 互斥锁 mutex 不为嵌套锁的情况下线程重复调用此函数 | -| EBUSY | 互斥锁 mutex 已经被其他线程上锁 | - -此函数是 pthread_mutex_lock() 函数的非阻塞版本。区别在于如果互斥锁 mutex 已经被上锁,线程不会被阻塞,而是马上返回错误码。 - -### 解锁互斥锁 - -```c -int pthread_mutex_unlock(pthread_mutex_t *mutex); -``` - -| **参数** | **描述** | -|----------|----------------------| -| mutex | 互斥锁句柄,不能为 NULL | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | -| EPERM | 互斥锁 mutex 不为嵌套锁的情况下线程重复调用此函数 | -| EBUSY | 解锁其他线程持有的类型为检错锁的互斥锁 | - -调用此函数给 mutex 互斥锁解锁,是对 rt_mutex_release() 函数的封装。当线程完成共享资源的访问后,应尽快释放占有的互斥锁,使得其他线程能及时获取该互斥锁。只有已经拥有互斥锁的线程才能释放它,每释放一次该互斥锁,它的持有计数就减 1。当该互斥量的持有计数为零时(即持有线程已经释放所有的持有操作),互斥锁才变为可用,等待在该互斥锁上的线程将按先进先出方式被唤醒。如果线程的运行优先级被互斥锁提升,那么当互斥锁被释放后,线程恢复为持有互斥锁前的优先级。 - -### 互斥锁示例代码 - -这个程序会初始化 2 个线程,它们拥有相同的优先级,2 个线程都会调用同一个 printer() 函数输出自己的字符串,printer() 函数每次只输出一个字符,之后休眠 1 秒,调用 printer() 函数的线程同样也休眠。如果不使用互斥锁,线程 1 打印了一个字符,休眠后执行线程 2,线程 2 打印一个字符,这样就不能完整的打印线程 1 和线程 2 的字符串,打印出的字符串是混乱的。如果使用了互斥锁保护 2 个线程共享的打印函数 printer(),线程 1 拿到互斥锁后执行 printer() 打印函数打印一个字符,之后休眠 1 秒,这是切换到线程 2,因为互斥锁已经被线程 1 上锁,线程 2 将阻塞,直到线程 1 的字符串打印完整后主动释放互斥锁后线程 2 才会被唤醒。 - -```c -#include -#include -#include - -/* 线程控制块 */ -static pthread_t tid1; -static pthread_t tid2; -/* 互斥锁控制块 */ -static pthread_mutex_t mutex; -/* 线程共享的打印函数 */ -static void printer(char* str) -{ - while(*str != 0) - { - putchar(*str); /* 输出一个字符 */ - str++; - sleep(1); /* 休眠 1 秒 */ - } - printf("\n"); -} -/* 函数返回值检查 */ -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); - } -} -/* 线程入口 */ -static void* thread1_entry(void* parameter) -{ - char* str = "thread1 hello RT-Thread"; - while (1) - { - pthread_mutex_lock(&mutex); /* 互斥锁上锁 */ - - printer(str); /* 访问共享打印函数 */ - - pthread_mutex_unlock(&mutex); /* 访问完成后解锁 */ - - sleep(2); /* 休眠 2 秒 */ - } -} -static void* thread2_entry(void* parameter) -{ - char* str = "thread2 hi world"; - while (1) - { - pthread_mutex_lock(&mutex); /* 互斥锁上锁 */ - - printer(str); /* 访问共享打印函数 */ - - pthread_mutex_unlock(&mutex); /* 访问完成后解锁 */ - - sleep(2); /* 休眠 2 秒 */ - } -} -/* 用户应用入口 */ -int rt_application_init() -{ - int result; - /* 初始化一个互斥锁 */ - pthread_mutex_init(&mutex,NULL); - - /* 创建线程 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); - - return 0; -} -``` - -## 条件变量 - -条件变量其实就是一个信号量,用于线程间同步。条件变量用来阻塞一个线程,当条件满足时向阻塞的线程发送一个条件,阻塞线程就被唤醒,条件变量需要和互斥锁配合使用,互斥锁用来保护共享数据。 - -条件变量可以用来通知共享数据状态。比如一个处理共享资源队列的线程发现队列为空,则此线程只能等待,直到有一个节点被添加到队列中,添加后在发一个条件变量信号激活等待线程。 - -条件变量的主要操作包括:调用 pthread_cond_init() 对条件变量初始化,调用 -pthread_cond_destroy() 销毁一个条件变量,调用 pthread_cond_wait() 等待一个条件变量,调用 pthread_cond_signal() 发送一个条件变量。 - -### 条件变量控制块 - -每个条件变量对应一个条件变量控制块,包括对条件变量进行操作的一些信息。初始化一个条件变量前需要先定义一个 pthread_cond_t 条件变量控制块。pthread_cond_t 是 pthread_cond 结构体类型的重定义,定义在 pthread.h 头文件里。 - -```c -struct pthread_cond -{ - pthread_condattr_t attr; /* 条件变量属性 */ - struct rt_semaphore sem; /* RT-Thread 信号量控制块 */ -}; -typedef struct pthread_cond pthread_cond_t; - -rt_semaphore 是 RT-Thread 内核里定义的一个数据结构,是信号量控制块, -定义在 rtdef.h 头文件里 - -struct rt_semaphore -{ - struct rt_ipc_object parent;/* 继承自 ipc_object 类 */ - rt_uint16_t value; /* 信号量的值 */ -}; -``` - -### 初始化条件变量 - -```c -int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); -``` - -| **参数** | **描述** | -|----|------------------------------------------------| -| cond | 条件变量句柄,不能为 NULL | -| attr | 指向条件变量属性的指针,若为 NULL 则使用默认属性值 | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | - -此函数会初始化 cond 条件变量,并根据 attr 指向的条件变量属性设置其属性,此函数是对 rt_sem_init() 函数的一个封装,基于信号量实现。初始化成功后条件变量处于不可用状态。 - -还可以用宏 PTHREAD_COND_INITIALIZER 静态初始化一个条件变量,方法: -`pthread_cond_t cond = PTHREAD_COND_INITIALIZER`(结构体常量),等同于调用 -pthread_cond_init() 时 attr 指定为 NULL。 - -attr 一般设置 NULL 使用默认值即可,具体会在线程高级编程一章介绍。 - -### 销毁条件变量 - -```c -int pthread_cond_destroy(pthread_cond_t *cond); -``` - -| **参数** | **描述** | -|----|------------------------| -| cond | 条件变量句柄,不能为 NULL | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | -| EPERM | 互斥锁 mutex 不为嵌套锁的情况下线程重复调用此函数 | -| EBUSY | 条件变量正在被使用 | - -此函数会销毁 cond 条件变量,销毁后 cond 处于未初始化状态。销毁之后条件变量的属性及控制块参数将不在有效,但可以调用 pthread_cond_init() 或者静态方式重新初始化。 - -销毁条件变量前需要确定没有线程被阻塞在该条件变量上,也不会等待获取、发信号或者广播。 - -### 阻塞方式获取条件变量 - -```c -int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); -``` - -| **参数** | **描述** | -|----------|----------------------------------| -| cond | 条件变量句柄,不能为 NULL | -| mutex | 指向互斥锁控制块的指针,不能为 NULL | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | - -此函数会以阻塞方式获取 cond 条件变量。线程等待条件变量前需要先将 mutex 互斥锁锁住,此函数首先判断条件变量是否可用,如果不可用则初始化一个条件变量,之后解锁 mutex 互斥锁,然后尝试获取一个信号量,当信号量值大于零时,表明信号量可用,线程将获得信号量,也就获得该条件变量,相应的信号量值会减 1。如果信号量的值等于零,表明信号量不可用,线程将阻塞直到信号量可用,之后将对 mutex 互斥锁再次上锁。 - -### 指定阻塞时间获取条件变量 - -```c -int pthread_cond_timedwait(pthread_cond_t *cond, - pthread_mutex_t *mutex, - const struct timespec *abstime); -``` - -| **参数** | **描述** | -|-------|-------------------------------------------------| -| cond | 条件变量句柄,不能为 NULL | -| mutex | 指向互斥锁控制块的指针,不能为 NULL | -| abstime | 指定的等待时间,单位是操作系统时钟节拍(OS Tick) | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | -| EPERM | 互斥锁 mutex 不为嵌套锁的情况下线程重复调用此函数 | -| ETIMEDOUT | 超时 | - -此函数和 pthread_cond_wait() 函数唯一的差别在于,如果条件变量不可用,线程将被阻塞 abstime 时长,超时后函数将直接返回 ETIMEDOUT 错误码,线程将会被唤醒进入就绪态。 - -### 发送满足条件信号量 - -```c -int pthread_cond_signal(pthread_cond_t *cond); -``` - -| **参数** | **描述** | -|----|------------------------| -| cond | 条件变量句柄,不能为 NULL | -|**返回**| —— | -| 0 | 成功 | - -此函数会发送一个信号且只唤醒一个等待 cond 条件变量的线程,是对 rt_sem_release() 函数的封装,也就是发送一个信号量。当信号量的值等于零,并且有线程等待这个信号量时,将唤醒等待在该信号量线程队列中的第一个线程,由它获取信号量。否则将把信号量的值加 1。 - -### 广播 - -```c -int pthread_cond_broadcast(pthread_cond_t *cond); -``` - -| **参数** | **描述** | -|----|------------------------| -| cond | 条件变量句柄,不能为 NULL | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | - -调用此函数将唤醒所有等待 cond 条件变量的线程。 - -### 条件变量示例代码 - -这个程序是一个生产者消费者模型,有一个生产者线程,一个消费者线程,它们拥有相同的优先级。生产者每隔 2 秒会生产一个数字,放到 head 指向的链表里面,之后调用 pthread_cond_signal() 给消费者线程发信号,通知消费者线程链表里面有数据。消费者线程会调用 pthread_cond_wait() 等待生产者线程发送信号。 - -```c -#include -#include -#include -#include - -/* 静态方式初始化一个互斥锁和一个条件变量 */ -static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; - -/* 指向线程控制块的指针 */ -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; - - pthread_mutex_lock(&mutex); /* 对互斥锁上锁 */ - - while (1) - { - while (head == NULL) /* 判断链表里是否有元素 */ - { - pthread_cond_wait(&cond,&mutex); /* 尝试获取条件变量 */ - } - /* - pthread_cond_wait() 会先对 mutex 解锁, - 然后阻塞在等待队列,直到获取条件变量被唤醒, - 被唤醒后,该线程会再次对 mutex 上锁,成功进入临界区。 - */ - - p_node = head; /* 拿到资源 */ - head = head->n_next; /* 头指针指向下一个资源 */ - /* 打印输出 */ - printf("consume %d\n",p_node->n_number); - - free(p_node); /* 拿到资源后释放节点占用的内存 */ - } - pthread_mutex_unlock(&mutex); /* 释放互斥锁 */ - return 0; -} -/* 生产者线程入口函数 */ -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); - - pthread_cond_signal(&cond); /* 发信号唤醒一个线程 */ - - sleep(2); /* 休眠 2 秒 */ - } - else - { - printf("product malloc node failed!\n"); - break; - } - } -} - -int rt_application_init() -{ - int result; - - /* 创建生产者线程, 属性为默认值,入口函数是 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; -} -``` - -## 读写锁 - -读写锁也称为多读者单写者锁。读写锁把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。同一时间只能有一个线程可以占有写模式的读写锁, -但是可以有多个线程同时占有读模式的读写锁。读写锁适合于对数据结构的读次数比写次数多得多的情况,因为读模式锁定时可以共享, -写模式锁定时意味着独占。 - -读写锁通常是基于互斥锁和条件变量实现的。一个线程可以对一个读写锁进行多次读写锁定,同样必须有对应次数的解锁。 - -读写锁的主要操作包括:调用 pthread_rwlock_init() 初始化一个读写锁,写线程调用 pthread_rwlock_wrlock() 对读写锁写锁定,读线程调用 pthread_rwlock_rdlock() 对读写锁读锁定,当不需要使用此读写锁时调用 pthread_rwlock_destroy() 销毁读写锁。 - -### 读写锁控制块 - -每个读写锁对应一个读写锁控制块,包括对读写锁进行操作的一些信息。pthread_rwlock_t 是 pthread_rwlock 数据结构的重定义,定义在 pthread.h 头文件里。在创建一个读写锁之前需要先定义一个 pthread_rwlock_t 类型的数据结构。 - -```c -struct pthread_rwlock -{ - pthread_rwlockattr_t attr; /* 读写锁属性 */ - pthread_mutex_t rw_mutex; /* 互斥锁 */ - pthread_cond_t rw_condreaders; /* 条件变量,供读者线程使用 */ - pthread_cond_t rw_condwriters; /* 条件变量,供写者线程使用 */ - int rw_nwaitreaders; /* 读者线程等待计数 */ - int rw_nwaitwriters; /* 写者线程等待计数 */ - /* 读写锁值,值为 0:未上锁, 值为 - 1:被写者线程锁定, 大于 0 值:被读者线程锁定数量 */ - int rw_refcount; -}; -typedef struct pthread_rwlock pthread_rwlock_t; /* 类型重定义 */ -``` - -### 初始化读写锁初始化 - -```c -int pthread_rwlock_init (pthread_rwlock_t *rwlock, - const pthread_rwlockattr_t *attr); -``` - -| **参数** | **描述** | -|------|-------------------------------------------| -| rwlock | 读写锁句柄,不能为 NULL | -| attr | 指向读写锁属性的指针,RT-Thread 不使用此变量 | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | - -此函数会初始化一个 rwlock 读写锁。此函数使用默认值初始化读写锁控制块的信号量和条件变量,相关计数参数初始为 0 值。初始化后的读写锁处于未上锁状态。 - -还可以使用宏 PTHREAD_RWLOCK_INITIALIZER 来静态初始化读写锁,方法: -pthread_rwlock_t mutex = PTHREAD_RWLOCK_INITIALIZER(结构体常量),等同于调用pthread_rwlock_init() 时 attr 指定为 NULL。 - -attr 一般设置 NULL 使用默认值即可,具体会在线程高级编程一章介绍。 - -### 销毁读写锁 - -```c -int pthread_rwlock_destroy (pthread_rwlock_t *rwlock); -``` - -| **参数** | **描述** | -|------|----------------------| -| rwlock | 读写锁句柄,不能为 NULL | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | -| EBUSY | 读写锁目前正在被使用或者有线程等待该读写锁 | -| EDEADLK | 死锁 | - -此函数会销毁一个 rwlock 读写锁,对应的会销毁读写锁里的互斥锁和条件变量。销毁之后读写锁的属性及控制块参数将不在有效,但可以调用 pthread_rwlock_init() 或者静态方式重新初始化读写锁。 - -### 读写锁读锁定 - -#### 阻塞方式对读写锁读锁定 - -```c -int pthread_rwlock_rdlock (pthread_rwlock_t *rwlock); -``` - -| **参数** | **描述** | -|------|----------------------| -| rwlock | 读写锁句柄,不能为 NULL | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | -| EDEADLK | 死锁 | - -读者线程可以调用此函数来对 rwlock 读写锁进行读锁定。如果读写锁没有被写锁定并且没有写者线程阻塞在该读写锁上,读写线程将成功获取该读写锁。如果读写锁已经被写锁定,读者线程将会阻塞,直到写锁定该读写锁的线程解锁。 - -#### 非阻塞方式对读写锁读锁定 - -```c -int pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock); -``` - -| **参数** | **描述** | -|------|----------------------| -| rwlock | 读写锁句柄,不能为 NULL | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | -| EBUSY | 读写锁目前正在被使用或者有线程等待该读写锁 | -| EDEADLK | 死锁 | - -此函数和 pthread_rwlock_rdlock() 函数的不同在于,如果读写锁已经被写锁定,读者线程不会被阻塞,而是返回一个错误码 EBUSY。 - -#### 指定阻塞时间对读写锁读锁定 - -```c -int pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock, - const struct timespec *abstime); -``` - -| **参数** | **描述** | -|-------|-------------------------------------------------| -| rwlock | 读写锁句柄,不能为 NULL | -| abstime | 指定的等待时间,单位是操作系统时钟节拍(OS Tick) | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | -| ETIMEDOUT | 超时 | -| EDEADLK | 死锁 | - -此函数和 pthread_rwlock_rdlock() 函数的不同在于,如果读写锁已经被写锁定,读者线程将会阻塞指定的 abstime 时长,超时后函数将返回错误码 ETIMEDOUT,线程将会被唤醒进入就绪态。 - -### 读写锁写锁定 - -#### 阻塞方式对读写锁写锁定 - -```c -int pthread_rwlock_wrlock (pthread_rwlock_t *rwlock); -``` - -| **参数** | **描述** | -|------|----------------------| -| rwlock | 读写锁句柄,不能为 NULL | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | -| EDEADLK | 死锁 | - -写者线程调用此函数对 rwlock 读写锁进行写锁定。写锁定读写锁类似互斥量,同一时刻只能有一个线程写锁定读写锁。如果没有线程锁定该读写锁,即读写锁值为 0,调用此函数的写者线程将会写锁定读写锁,其他线程此时都不能获取读写锁,如果已经有线程锁定该读写锁,即读写锁值不为 0,则写线程将被阻塞,直到读写锁解锁。 - -#### 非阻塞方式写锁定读写锁 - -```c -int pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock); -``` - -| **参数** | **描述** | -|------|----------------------| -| rwlock | 读写锁句柄,不能为 NULL | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | -| EBUSY | 读写锁目前被写锁定或者有写着线程阻塞在该读写锁上 | -| EDEADLK | 死锁 | - -此函数和 pthread_rwlock_wrlock() 函数唯一的不同在于,如果已经有线程锁定该读写锁,即读写锁值不为 0,则调用该函数的写者线程会直接返回一个错误代码,线程不会被阻塞。 - -#### 指定阻塞时长写锁定读写锁 - -```c -int pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock, - const struct timespec *abstime); -``` - -| **参数** | **描述** | -|--------------|---------------------| -| rwlock abstime | 读写锁句柄,不能为 NULL 指定的等待时间,单位是操作系统时钟节拍(OS Tick) | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | -| ETIMEDOUT | 超时 | -| EDEADLK | 死锁 | - -此函数和 pthread_rwlock_wrlock() 函数唯一的不同在于,如果已经有线程锁定该读写锁,即读写锁值不为 0,调用线程阻塞指定的 abstime 时长,超时后函数将返回错误码 ETIMEDOUT,线程将会被唤醒进入就绪态。 - -### 读写锁解锁 - -```c -int pthread_rwlock_unlock (pthread_rwlock_t *rwlock); -``` - -| **参数** | **描述** | -|------|----------------------| -| rwlock | 读写锁句柄,不能为 NULL | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | -| EDEADLK | 死锁 | - -此函数可以对 rwlock 读写锁解锁。线程对同一个读写锁加锁多次,必须有同样次数的解锁,若解锁后有多个线程等待对读写锁进行锁定,系统将按照先进先出的规则激活等待的线程。 - -### 读写锁示例代码 - -这个程序有 2 个读者线程,一个写着线程。2 个读者线程先对读写锁读锁定,之后休眠 2 秒,这是其他的读者线程还是可以对该读写锁读锁定,然后读取共享数据。 - -```c -#include -#include -#include - -/* 线程控制块 */ -static pthread_t reader1; -static pthread_t reader2; -static pthread_t writer1; -/* 共享数据 book */ -static int book = 0; -/* 读写锁 */ -static pthread_rwlock_t rwlock; -/* 函数结果检查 */ -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); - } -} -/* 线程入口 */ -static void* reader1_entry(void* parameter) -{ - while (1) - { - - pthread_rwlock_rdlock(&rwlock); /* 尝试读锁定该读写锁 */ - - printf("reader1 read book value is %d\n",book); - sleep(2); /* 线程休眠 2 秒,切换到其他线程运行 */ - - pthread_rwlock_unlock(&rwlock); /* 线程运行后对读写锁解锁 */ - } -} -static void* reader2_entry(void* parameter) -{ - while (1) - { - pthread_rwlock_rdlock(&rwlock); /* 尝试读锁定该读写锁 */ - - printf("reader2 read book value is %d\n",book); - sleep(2); /* 线程休眠 2 秒,切换到其他线程运行 */ - - pthread_rwlock_unlock(&rwlock); /* 线程运行后对读写锁解锁 */ - } -} -static void* writer1_entry(void* parameter) -{ - while (1) - { - pthread_rwlock_wrlock(&rwlock); /* 尝试写锁定该读写锁 */ - - book++; - printf("writer1 write book value is %d\n",book); - - pthread_rwlock_unlock(&rwlock); /* 对读写锁解锁 */ - - sleep(2); /* 线程休眠 2 秒,切换到其他线程运行 */ - } -} -/* 用户应用入口 */ -int rt_application_init() -{ - int result; - /* 默认属性初始化读写锁 */ - pthread_rwlock_init(&rwlock,NULL); - - /* 创建 reader1 线程, 线程入口是 reader1_entry, 线程属性为默认值,入口参数为 NULL*/ - result = pthread_create(&reader1,NULL,reader1_entry,NULL); - check_result("reader1 created",result); - - /* 创建 reader2 线程, 线程入口是 reader2_entry, 线程属性为默认值,入口参数为 NULL*/ - result = pthread_create(&reader2,NULL,reader2_entry,NULL); - check_result("reader2 created",result); - - /* 创建 writer1 线程, 线程入口是 writer1_entry, 线程属性为,入口参数为 NULL*/ - result = pthread_create(&writer1,NULL,writer1_entry,NULL); - check_result("writer1 created",result); - - return 0; -} -``` - -## 屏障 - -屏障是多线程同步的一种方法。barrier 意为屏障或者栏杆,把先后到达的多个线程挡在同一栏杆前,直到所有线程到齐,然后撤下栏杆同时放行。先到达的线程将会阻塞,等到所有调用 pthread_barrier_wait() 函数的线程(数量等于屏障初始化时指定的 count)都到达后,这些线程才会由阻塞状态进入就绪状态再次参与系统调度。 - -屏障是基于条件变量和互斥锁实现的。主要操作包括:调用 pthread_barrier_init() 初始化一个屏障,其他线程调用 pthread_barrier_wait(),所有线程到期后线程唤醒进入准备状态,屏障不在使用调用 pthread_barrier_destroy() 销毁一个屏障。 - -### 屏障控制块 - -创建一个屏障前需要先定义一个 pthread_barrier_t 屏障控制块。pthread_barrier_t 是 pthread_barrier 结构体类型的重定义,定义在 pthread.h 头文件里。 - -```c -struct pthread_barrier -{ - int count; /* 指定的等待线程个数 */ - pthread_cond_t cond; /* 条件变量 */ - pthread_mutex_t mutex; /* 互斥锁 */ -}; -typedef struct pthread_barrier pthread_barrier_t; -``` - -### 创建屏障 - -```c -int pthread_barrier_init(pthread_barrier_t *barrier, - const pthread_barrierattr_t *attr, - unsigned count); -``` - -| **参数** | **描述** | -|-------|-------------------------------| -| attr | 指向屏障属性的指针,传入 NULL,则使用默认值,非 NULL 必须使用 PTHREAD_PROCESS_PRIVATE | -| barrier | 屏障句柄 | -| count | 指定的等待线程个数 | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | - -此函数会创建一个 barrier 屏障,并根据默认的参数对屏障控制块的条件变量和互斥锁初始化,初始化后指定的等待线程个数为 count 个,必须对应 count 个线程调用 pthread_barrier_wait()。 - -attr 一般设置 NULL 使用默认值即可,具体会在线程高级编程一章介绍。 - -### 销毁屏障 - -```c -int pthread_barrier_destroy(pthread_barrier_t *barrier); -``` - -| **参数** | **描述** | -|-------|--------| -| barrier | 屏障句柄 | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | - -此函数会销毁一个 barrier 屏障。销毁之后屏障的属性及控制块参数将不在有效,但可以调用 pthread_barrier_init() 重新初始化。 - -### 等待屏障 - -```c -int pthread_barrier_wait(pthread_barrier_t *barrier); -``` - -| **参数** | **描述** | -|-------|--------| -| barrier | 屏障句柄 | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | - -此函数同步等待在 barrier 前的线程,由每个线程主动调用,若屏障等待线程个数 count 不为 0,count 将减 1,若减 1 后 count 为 0,表明所有线程都已经到达栏杆前,所有到达的线程将被唤醒重新进入就绪状态,参与系统调度。若减一后 count 不为 0,表明还有线程没有到达屏障,调用的线程将阻塞直到所有线程到达屏障。 - -### 屏障示例代码 - -此程序会创建 3 个线程,初始化一个屏障,屏障等待线程数初始化为 3。3 个线程都会调用 pthread_barrier_wait() 等待在屏障前,当 3 个线程都到齐后,3 个线程进入就绪态,之后会每隔 2 秒打印输出计数信息。 - -```c -#include -#include -#include - -/* 线程控制块 */ -static pthread_t tid1; -static pthread_t tid2; -static pthread_t tid3; -/* 屏障控制块 */ -static pthread_barrier_t barrier; -/* 函数返回值检查函数 */ -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) -{ - int count = 0; - - printf("thread1 have arrived the barrier!\n"); - pthread_barrier_wait(&barrier); /* 到达屏障,并等待其他线程到达 */ - - while (1) - { - /* 打印线程计数值输出 */ - printf("thread1 count: %d\n",count ++); - - /* 休眠 2 秒 */ - sleep(2); - } -} -/* 线程 2 入口函数 */ -static void* thread2_entry(void* parameter) -{ - int count = 0; - - printf("thread2 have arrived the barrier!\n"); - pthread_barrier_wait(&barrier); - - while (1) - { - /* 打印线程计数值输出 */ - printf("thread2 count: %d\n",count ++); - - /* 休眠 2 秒 */ - sleep(2); - } -} -/* 线程 3 入口函数 */ -static void* thread3_entry(void* parameter) -{ - int count = 0; - - printf("thread3 have arrived the barrier!\n"); - pthread_barrier_wait(&barrier); - - while (1) - { - /* 打印线程计数值输出 */ - printf("thread3 count: %d\n",count ++); - - /* 休眠 2 秒 */ - sleep(2); - } -} -/* 用户应用入口 */ -int rt_application_init() -{ - int result; - pthread_barrier_init(&barrier,NULL,3); - - /* 创建线程 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); - -} -``` - -## 信号量 - -信号量可以用于进程与进程之间,或者进程内线程之间的通信。每个信号量都有一个不会小于 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; -} -``` - -## 线程高级编程 - -本章节会对一些很少使用的属性对象及相关函数做详细介绍。 - -RT-Thread 实现的线程属性包括线程栈大小、线程优先级、线程分离状态、线程调度策略。pthread_create() 使用属性对象前必须先对属性对象进行初始化。设置线程属性之类的 API 函数应在创建线程之前就调用。线程属性的变更不会影响到已创建的线程。 - -线程属性结构 pthread_attr_t 定义在 pthread.h 头文件里。线程属性结构如下: - -```c -/* pthread_attr_t 类型重定义 */ -typedef struct pthread_attr pthread_attr_t; -/* 线程属性结构体 */ -struct pthread_attr -{ - void* stack_base; /* 线程栈的地址 */ - rt_uint32_t stack_size; /* 线程栈大小 */ - rt_uint8_t priority; /* 线程优先级 */ - rt_uint8_t detachstate; /* 线程的分离状态 */ - rt_uint8_t policy; /* 线程调度策略 */ - rt_uint8_t inheritsched; /* 线程的继承性 */ -}; -``` - -#### 线程属性初始化及去初始化 - -线程属性初始化及去初始化函数如下所示: - -```c -int pthread_attr_init(pthread_attr_t *attr); -int pthread_attr_destroy(pthread_attr_t *attr); -``` - -| **参数** | **描述** | -|----|------------------| -| attr | 指向线程属性的指针 | -|**返回**| —— | -| 0 | 成功 | - -使用 pthread_attr_init() 函数会使用默认值初始化线程属性结构体 attr,等同于调用线程初始化函数时将此参数设置为 NULL,使用前需要定义一个 pthread_attr_t 属性对象,此函数必须在 pthread_create() 函数之前调用。 - -pthread_attr_destroy() 函数对 attr 指向的属性去初始化,之后可以再次调用 pthread_attr_init() 函数对此属性对象重新初始化。 - -#### 线程的分离状态 - -设置线程的分离状态 / 获取线程的分离状态如下所示,默认情况下线程是非分离状态。 - -```c -int pthread_attr_setdetachstate(pthread_attr_t *attr, int state); -int pthread_attr_getdetachstate(pthread_attr_t const *attr, int *state); -``` - -| **参数** | **描述** | -|----------|-------------------| -| attr | 指向线程属性的指针 | -| state | 线程分离状态 | -|**返回**| —— | -| 0 | 成功 | - -线程分离状态属性值 state 可以是 PTHREAD_CREATE_JOINABL(非分离)和 -PTHREAD_CREATE_DETACHED(分离)。 - -线程的分离状态决定一个线程以什么样的方式来回收自己运行结束后占用的资源。线程的分离状态有 2 种:joinable 或者 detached。当线程创建后,应该调用 pthread_join() 或者 pthread_detach() 回收线程结束运行后占用的资源。如果线程的分离状态为 joinable 其他线程可以调用 pthread_join() 函数等待该线程结束并获取线程返回值,然后回收线程占用的资源。分离状态为 detached 的线程不能被其他的线程所 join,自己运行结束后,马上释放系统资源。 - -#### 线程的调度策略 - -设置 \ 获取线程调度策略函数如下所示: - -```c -int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy); -int pthread_attr_getschedpolicy(pthread_attr_t const *attr, int *policy); -``` - -只实现了函数接口,默认不同优先级基于优先级调度,同一优先级时间片轮询调度 - -#### 线程的调度参数 - -设置线程的优先级 / 获取线程的优先级函数如下所示: - -```c -int pthread_attr_setschedparam(pthread_attr_t *attr, - struct sched_param const *param); -int pthread_attr_getschedparam(pthread_attr_t const *attr, - struct sched_param *param); -``` - -| **参数** | **描述** | -|----------|------------------| -| attr | 指向线程属性的指针 | -| param | 指向调度参数的指针 | -|**返回**| —— | -| 0 | 成功 | - -pthread_attr_setschedparam() 函数设置线程的优先级。使用 param 对线程属性优先级赋值。 - -**参数** struct sched_param 定义在 sched.h 里,结构如下: - -```c -struct sched_param -{ - int sched_priority; /* 线程优先级 */ -}; -``` - -结构体 sched_param 的成员 sched_priority 控制线程的优先级值。 - -#### 线程的堆栈大小 - -设置 / 获取 线程的堆栈大小的函数如下所示: - -```c -int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stack_size); -int pthread_attr_getstacksize(pthread_attr_t const *attr, size_t *stack_size); -``` - -| **参数** | **描述** | -|-----------|------------------| -| attr | 指向线程属性的指针 | -| stack_size | 线程堆栈大小 | -|**返回**| —— | -| 0 | 成功 | - -pthread_attr_setstacksize() 函数可以设置堆栈大小,单位是字节。在大多数系统中需要做栈空间地址对齐(例如 ARM 体系结构中需要向 4 字节地址对齐)。 - -#### 线程堆栈大小和地址 - -设置 / 获取 线程的堆栈地址和堆栈大小的函数如下所示: - -```c -int pthread_attr_setstack(pthread_attr_t *attr, - void *stack_base, - size_t stack_size); -int pthread_attr_getstack(pthread_attr_t const *attr, - void**stack_base, - size_t *stack_size); -``` - -| **参数** | **描述** | -|-----------|------------------| -| attr | 指向线程属性的指针 | -| stack_size | 线程堆栈大小 | -| stack_base | 线程堆栈地址 | -|**返回**| —— | -| 0 | 成功 | - -#### 线程属性相关函数 - -设置 / 获取线程的作用域的函数如下所示: - -```c -int pthread_attr_setscope(pthread_attr_t *attr, int scope); -int pthread_attr_getscope(pthread_attr_t const *attr); -``` - -| **参数** | **描述** | -|-----------|------------------| -| attr | 指向线程属性的指针 | -| scope | 线程作用域 | -|**返回**| —— | -| 0 | scope 为 PTHREAD_SCOPE_SYSTEM | -| EOPNOTSUPP | scope 为 PTHREAD_SCOPE_PROCESS | -| EINVAL | scope 为 PTHREAD_SCOPE_SYSTEM | - -#### 线程属性示例代码 - -这个程序会初始化 2 个线程,它们拥有共同的入口函数,但是它们的入口参数不相同。最先创建的线程会使用提供的 attr 线程属性,另外一个线程使用系统默认的属性。线程的优先级是很重要的一个参数,因此这个程序会修改第一个创建的线程的优先级为 8,而系统默认的优先级为 24。 - -```c -#include -#include -#include -#include - -/* 线程控制块 */ -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); - } -} -/* 线程入口函数 */ -static void* thread_entry(void* parameter) -{ - int count = 0; - int no = (int) parameter; /* 获得线程的入口参数 */ - - while (1) - { - /* 打印输出线程计数值 */ - printf("thread%d count: %d\n", no, count ++); - - sleep(2); /* 休眠 2 秒 */ - } -} - -/* 用户应用入口 */ -int rt_application_init() -{ - int result; - pthread_attr_t attr; /* 线程属性 */ - struct sched_param prio; /* 线程优先级 */ - - prio.sched_priority = 8; /* 优先级设置为 8 */ - pthread_attr_init(&attr); /* 先使用默认值初始化属性 */ - pthread_attr_setschedparam(&attr,&prio); /* 修改属性对应的优先级 */ - - /* 创建线程 1, 属性为 attr,入口函数是 thread_entry,入口函数参数是 1 */ - result = pthread_create(&tid1,&attr,thread_entry,(void*)1); - check_result("thread1 created",result); - - /* 创建线程 2, 属性为默认值,入口函数是 thread_entry,入口函数参数是 2 */ - result = pthread_create(&tid2,NULL,thread_entry,(void*)2); - check_result("thread2 created",result); - - return 0; -} -``` - -### 线程取消 - -取消是一种让一个线程可以结束其它线程运行的机制。一个线程可以对另一个线程发送一个取消请求。依据设置的不同,目标线程可能会置之不理,可能会立即结束也可能会将它推迟到下一个取消点才结束。 - -#### 发送取消请求 - -可使用如下函数发送取消请求: - -```c -int pthread_cancel(pthread_t thread); -``` - -| **参数** | **描述** | -|------|--------| -| thread | 线程句柄 | -|**返回**| —— | -| 0 | 成功 | - -此函数发送取消请求给 thread 线程。Thread 线程是否会对取消请求做出回应以及什么时候做出回应依赖于线程取消的状态及类型。 - -#### 设置取消状态 - -可使用如下函数设置取消请求: - -```c -int pthread_setcancelstate(int state, int *oldstate); -``` - -| **参数** | **描述** | -|--------|-------------------------------| -| state | 有两种值:PTHREAD_CANCEL_ENABLE:取消使能 PTHREAD_CANCEL_DISABLE:取消不使能(线程创建时的默认值) | -| oldstate | 保存原来的取消状态 | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | state 非 PTHREAD_CANCEL_ENABLE 或者 PTHREAD_CANCEL_DISABLE | - -此函数设置取消状态,由线程自己调用。取消使能的线程将会对取消请求做出反应,而取消没有使能的线程不会对取消请求做出反应。 - -#### 设置取消类型 - -可使用如下函数设置取消类型,由线程自己调用: - -```c -int pthread_setcanceltype(int type, int *oldtype); -``` - -| **参数** | **描述** | -|-------|---------------------------------| -| type | 有 2 种值:PTHREAD_CANCEL_DEFFERED:线程收到取消请求后继续运行至下一个取消点再结束。(线程创建时的默认值)PTHREAD_CANCEL_ASYNCHRONOUS:线程立即结束。 | -| oldtype | 保存原来的取消类型 | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | state 非 PTHREAD_CANCEL_DEFFERED 或者 PTHREAD_CANCEL_ASYNCHRONOUS | - -#### 设置取消点 - -可使用如下函数设置取消点: - -```c -void pthread_testcancel(void); -``` - -此函数在线程调用的地方创建一个取消点。主要由不包含取消点的线程调用,可以回应取消请求。如果在取消状态处于禁用状态下调用 pthread_testcancel(),则该函数不起作用。 - -#### 取消点 - -取消点也就是线程接受取消请求后会结束运行的地方,根据 POSIX 标准,pthread_join()、pthread_testcancel()、pthread_cond_wait()、pthread_cond_timedwait()、sem_wait() 等会引起阻塞的系统调用都是取消点。 - -RT-Thread 包含的所有取消点如下: - -- mq_receive() - -- mq_send() - -- mq_timedreceive() - -- mq_timedsend() - -- msgrcv() - -- msgsnd() - -- msync() - -- pthread_cond_timedwait() - -- pthread_cond_wait() - -- pthread_join() - -- pthread_testcancel() - -- sem_timedwait() - -- sem_wait() - -- pthread_rwlock_rdlock() - -- pthread_rwlock_timedrdlock() - -- pthread_rwlock_timedwrlock() - -- pthread_rwlock_wrlock() - -#### 线程取消示例代码 - -此程序会创建 2 个线程,线程 2 开始运行后马上休眠 8 秒,线程 1 设置了自己的取消状态和类型,之后在一个无限循环里打印运行计数信息。线程 2 唤醒后向线程 1 发送取消请求,线程 1 收到取消请求后马上结束运行。 - -```c -#include -#include -#include - -/* 线程控制块 */ -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); - } -} -/* 线程 1 入口函数 */ -static void* thread1_entry(void* parameter) -{ - int count = 0; - /* 设置线程 1 的取消状态使能,取消类型为线程收到取消点后马上结束 */ - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); - pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - - while(1) - { - /* 打印线程计数值输出 */ - printf("thread1 run count: %d\n",count ++); - sleep(2); /* 休眠 2 秒 */ - } -} -/* 线程 2 入口函数 */ -static void* thread2_entry(void* parameter) -{ - int count = 0; - sleep(8); - /* 向线程 1 发送取消请求 */ - pthread_cancel(tid1); - /* 阻塞等待线程 1 运行结束 */ - pthread_join(tid1,NULL); - printf("thread1 exited!\n"); - /* 线程 2 打印信息开始输出 */ - while(1) - { - /* 打印线程计数值输出 */ - printf("thread2 run count: %d\n",count ++); - sleep(2); /* 休眠 2 秒 */ - } -} -/* 用户应用入口 */ -int rt_application_init() -{ - int result; - /* 创建线程 1, 属性为默认值,分离状态为默认值 joinable, - 入口函数是 thread1_entry,入口函数参数为 NULL */ - result = pthread_create(&tid1,NULL,thread1_entry,NULL); - check_result("thread1 created",result); - - /* 创建线程 2, 属性为默认值,分离状态为默认值 joinable, - 入口函数是 thread2_entry,入口函数参数为 NULL */ - result = pthread_create(&tid2,NULL,thread2_entry,NULL); - check_result("thread2 created",result); - - return 0; -} -``` - -### 一次性初始化 - -可使用如下函数一次性初始化: - -```c -int pthread_once(pthread_once_t * once_control, void (*init_routine) (void)); -``` - -| **参数** | **描述** | -|-------------|--------| -| once_control | 控制变量 | -| init_routine | 执行函数 | -|**返回**| —— | -| 0 | 成功 | - -有时候我们需要对一些变量只进行一次初始化。如果我们进行多次初始化程序就会出现错误。在传统的顺序编程中,一次性初始化经常通过使用布尔变量来管理。控制变量被静态初始化为 0,而任何依赖于初始化的代码都能测试该变量。如果变量值仍然为 0,则它能实行初始化,然后将变量置为 1。以后检查的代码将跳过初始化。 - -### 线程结束后清理 - -线程清理函数接口如下所示: - -```c -void pthread_cleanup_pop(int execute); -void pthread_cleanup_push(void (*routine)(void*), void *arg); -``` - -| **参数** | **描述** | -|-------|-----------------------------| -| execute | 0 或 1,决定是否执行 cleanup 函数 | -| routine | 指向清理函数的指针 | -| arg | 传递给清理函数的参数 | - -pthread_cleanup_push() 把指定的清理函数 routine 放到线程的清理函数链表里, pthread_cleanup_pop() 从清理函数链表头部取出第一项函数,若 execute 为非 0 值,则执行此函数。 - -### 其他线程相关函数 - -#### 判断 2 个线程是否相等 - -```c -int pthread_equal (pthread_t t1, pthread_t t2); -``` - -| **参数** | **描述** | -|----------|--------| -| pthread_t | 线程句柄 | -|**返回**| —— | -| 0 | 不相等 | -| 1 | 相等 | - -#### 获取线程句柄 - -```c -pthread_t pthread_self (void); -``` -pthread_self() 返回调用线程的句柄。 - -#### 获取最大最小优先级 - -```c -int sched_get_priority_min(int policy); -int sched_get_priority_max(int policy); -``` - -| **参数** | **描述** | -|------|---------------------------------| -| policy | 2 个值可选:SCHED_FIFO,SCHED_RR | - -sched_get_priority_min() 返回值为 0,RT-Thread 里为最大优先级, sched_get_priority_max() 返回值最小优先级。 - -### 互斥锁属性 - -RT-Thread 实现的互斥锁属性包括互斥锁类型和互斥锁作用域。 - -#### 互斥锁属性初始化及去初始化 - -```c -int pthread_mutexattr_init(pthread_mutexattr_t *attr); -int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); -``` - -| **参数** | **描述** | -|----|------------------------| -| attr | 指向互斥锁属性对象的指针 | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | - -pthread_mutexattr_init() 函数将使用默认值初始化 attr 指向的属性对象,等同于调用 pthread_mutex_init() 函数时将属性参数设置为 NULL。 - -pthread_mutexattr_destroy() 函数将会对 attr 指向的属性对象去初始化,之后可以调用 pthread_mutexattr_init() 函数重新初始化。 - -#### 互斥锁作用域 - -```c -int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared); -int pthread_mutexattr_getpshared(pthread_mutexattr_t *attr, int *pshared); -``` - -| **参数** | **描述** | -|-------|--------------------| -| type | 互斥锁类型 | -| pshared | 有 2 个可选值: PTHREAD_PROCESS_PRIVATE:默认值,用于仅同步该进程中的线程。PTHREAD_PROCESS_SHARED:用于同步该进程和其他进程中的线程。 | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | - -#### 互斥锁类型 - -```c -int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type); -int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type); -``` - -| **参数** | **描述** | -|----|------------------------| -| type | 互斥锁类型 | -| attr | 指向互斥锁属性对象的指针 | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | - -互斥锁的类型决定了一个线程在获取一个互斥锁时的表现方式,RT-Thread 实现了 3 种互斥锁类型: - -- PTHREAD_MUTEX_NORMAL:普通锁,当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按先进先出方式获得锁。如果一个线程在不首先解除互斥锁的情况下尝试重新获得该互斥锁,不会产生死锁,而是返回错误码,和检错锁一样。 - -- PTHREAD_MUTEX_RECURSIVE:嵌套锁,允许一个线程对同一个锁成功获得多次,需要相同次数的解锁释放该互斥锁。 - -- PTHREAD_MUTEX_ERRORCHECK:检错锁,如果一个线程在不首先解除互斥锁的情况下尝试重新获得该互斥锁,则返回错误。这样就保证当不允许多次加锁时不会出现死锁。 - -### 条件变量属性 - -使用默认值 PTHREAD_PROCESS_PRIVATE 初始化条件变量属性 attr 可使用如下函数: - -```c -int pthread_condattr_init(pthread_condattr_t *attr); -``` - -| **参数** | **描述** | -|----|--------------------------| -| attr | 指向条件变量属性对象的指针 | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | - -#### 获取条件变量作用域 - -```c -int pthread_mutexattr_getpshared(pthread_mutexattr_t *attr, int *pshared); -``` - -| **参数** | **描述** | -|----|--------------------------| -| attr | 指向条件变量属性对象的指针 | -|**返回**| —— | -| 0 | 成功 | -| EINVAL | 参数无效 | - -### 读写锁属性 - -#### 初始化属性 - -```c -int pthread_rwlockattr_init (pthread_rwlockattr_t *attr); -``` - -| **参数** | **描述** | -|----|--------------------| -| attr | 指向读写锁属性的指针 | -|**返回**| —— | -| 0 | 成功 | -|-1 | 参数无效 | - -该函数会使用默认值 PTHREAD_PROCESS_PRIVATE 初始化读写锁属性 attr。 - -#### 获取作用域 - -```c -int pthread_rwlockattr_getpshared (const pthread_rwlockattr_t *attr, int *pshared); -``` - -| **参数** | **描述** | -|-------|--------------------------| -| attr | 指向读写锁属性的指针 | -| pshared | 指向保存读写锁作用域的指针 | -|**返回**| —— | -| 0 | 成功 | -|-1 | 参数无效 | - -pshared 指向的内存保存的值为 PTHREAD_PROCESS_PRIVATE。 - -### 屏障属性 - -#### 初始化属性 - -```c -int pthread_barrierattr_init(pthread_barrierattr_t *attr); -``` - -| **参数** | **描述** | -|----|------------------| -| attr | 指向屏障属性的指针 | -|**返回**| —— | -| 0 | 成功 | -|-1 | 参数无效 | - -改函数会使用默认值 PTHREAD_PROCESS_PRIVATE 初始化屏障属性 attr。 - -#### 获取作用域 - -```c -int pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr, int *pshared); -``` - -| **参数** | **描述** | -|-------|-----------------------------| -| attr | 指向屏障属性的指针 | -| pshared | 指向保存屏障作用域数据的指针 | -|**返回**| —— | -| 0 | 成功 | -|-1 | 参数无效 | - -### 消息队列属性 - -消息队列属性控制块如下: - -```c -struct mq_attr -{ - long mq_flags; /* 消息队列的标志,用来表示是否阻塞 */ - long mq_maxmsg; /* 消息队列最大消息数 */ - long mq_msgsize; /* 消息队列每个消息的最大字节数 */ - long mq_curmsgs; /* 消息队列当前消息数 */ -}; -``` -#### 获取属性 -```c -int mq_getattr(mqd_t mqdes, struct mq_attr *mqstat); -``` - -| **参数** | **描述** | -|------|------------------------| -| mqdes | 指向消息队列控制块的指针 | -| 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不同的地方。 -- Gitee From a6fd8e97d441a99b8b435c417921c247fd0165fa Mon Sep 17 00:00:00 2001 From: liuxianliang Date: Thu, 28 Apr 2022 21:02:29 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E3=80=90=E6=9B=B4=E6=96=B0=E3=80=91?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../programming-manual/libc/introduction.md | 6 ++--- .../libc/posix/introduction.md | 22 +++++++++---------- .../programming-manual/libc/posix/pse51.md | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) 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 db67597..ac8e148 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,7 +4,7 @@ RT-Thread 提供的 LIBC (C library, C库),包含编译器配平层和 POSIX ![libc_structure](figures/libc_structure.png) - 由于在不同的平台开发所使用的编译工具链不一致;所以在实现时,RT-Thread 区分了三个概念:**广义的 POSIX**,**狭隘的 POSIX**,**编译器配平层**;下面结合关系图,描述一下对应的概念: +由于在不同的平台开发所使用的编译工具链不一致;所以在实现时,RT-Thread 区分了三个概念:**广义的 POSIX**,**狭隘的 POSIX**,**编译器配平层**;下面结合关系图,描述一下对应的概念: * 广义的 POSIX ,即 POSIX 接口;这里面包含了一些关于 pthread, signal, IPC 等函数以及 C 库的相关内容; * 狭隘的 POSIX,即不是 C 库标准提供以外的 POSIX 函数,例如 pthread 这些内容; @@ -14,7 +14,7 @@ RT-Thread 提供的 LIBC (C library, C库),包含编译器配平层和 POSIX ##### 不同编译平台的兼容 - 在上一节,编译器配平层的存在解决了多组编译工具链不一致造成的问题;同时,也引入了一些在编写代码时需要注意的事项。这些注意事项是为了解决对应的编译错误。因为编译工具链有的允许头文件替换,有的不支持这些问题。RT-Thread 提供了一个相对轻便的解决办法: +在上一节,编译器配平层的存在解决了多组编译工具链不一致造成的问题;同时,也引入了一些在编写代码时需要注意的事项。这些注意事项是为了解决对应的编译错误。因为编译工具链有的允许头文件替换,有的不支持这些问题。RT-Thread 提供了一个相对轻便的解决办法: * 为保证跨不同编译器、不同工具链的兼容性,建议用户应用层代码: * 使用 代替 @@ -25,4 +25,4 @@ RT-Thread 提供的 LIBC (C library, C库),包含编译器配平层和 POSIX ##### Net 接口 - 启用网络功能后,并不是所有的 BSD Socket 接口都支持,在 RT-Thread 中只实现了一部分函数。因此如果对应函数缺失,需要按照代码逻辑替换为基础的 Socket 接口来兼容原逻辑。完整的 BSD Socket 功能将持续进行来解决这个问题。 +启用网络功能后,并不是所有的 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 34ce895..eaa1b32 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,6 +1,6 @@ # POSIX 简介 - 可移植操作系统接口,POSIX 标准定义了操作系统(很多时候针对的是类 Unix 操作系统)应该为应用程序提供的接口标准,从而保证了应用程序在源码层次的可移植性,如今主流的 Linux 系统都做到了兼容 POSIX 标准。由此可见,可移植性是 POSIX 的一大特性,如果一个操作系统拟合了POSIX系统,就将可以将自己的 POSIX 应用轻松迁移到其他同样符合POSIX标准的系统平台,并且可以获得大量的 POSIX 应用,丰富自己系统的应用生态。 +可移植操作系统接口,POSIX 标准定义了操作系统(很多时候针对的是类 Unix 操作系统)应该为应用程序提供的接口标准,从而保证了应用程序在源码层次的可移植性,如今主流的 Linux 系统都做到了兼容 POSIX 标准。由此可见,可移植性是 POSIX 的一大特性,如果一个操作系统拟合了POSIX系统,就将可以将自己的 POSIX 应用轻松迁移到其他同样符合POSIX标准的系统平台,并且可以获得大量的 POSIX 应用,丰富自己系统的应用生态。 POSIX 为了提供不同层级的RTOS能力,划定了 PSE51, PSE52, PSE53, PSE54 共四个级别;分别对应了四种操作系统。 @@ -9,15 +9,15 @@ POSIX 为了提供不同层级的RTOS能力,划定了 PSE51, PSE52, PSE53, PSE 3. Dedicated Real-time Profile IEEE Std 1003.13 PSE53,拥有网络功能...等 4. Multi-Purpose Real-time Profile IEEE Std 1003.13 PSE54,完整的文件系统,带有 Shell 组件...等 -![image-20220421115323880](../figure/51_52_53_54.png) +![image-20220421115323880](../figures/51_52_53_54.png) - 由于标准的制定是随着时间发展而不断更新的,PSE51, 52, 53, 54 的标准也随着 MCU,MPU 芯片产业的发展,不断增强的芯片功能和性能使得这些标准所对应的四种操作系统的界限而越显模糊。在 RT-Thread 中也依靠自身的组件内容,提供了多种标准的部分内容,为不同的 POSIX 应用提供可供在 RTOS 系统上使用的软件。 +由于标准的制定是随着时间发展而不断更新的,PSE51, 52, 53, 54 的标准也随着 MCU,MPU 芯片产业的发展,不断增强的芯片功能和性能使得这些标准所对应的四种操作系统的界限而越显模糊。在 RT-Thread 中也依靠自身的组件内容,提供了多种标准的部分内容,为不同的 POSIX 应用提供可供在 RTOS 系统上使用的软件。 -### 2. POSIX 在 RT-Thread 中支持情况 +## POSIX 在 RT-Thread 中支持情况 - RT-Thread 针对 PSE51 的标准规定的头文件及对应接口,提供了大部分接口,其 PSE51 接口数目占比如下饼状图所示。除此之外,我们还提供了更加完整的文件系统的接口,以及基于我们 SAL 组件的网络接口。这为 POSIX 的应用迁移打下了基础,可以使得较复杂的 POSIX 应用也可以在 RT-Thread 的平台上经过小部分修改就可以在 RT-Thread 上应用。 +RT-Thread 针对 PSE51 的标准规定的头文件及对应接口,提供了大部分接口,其 PSE51 接口数目占比如下饼状图所示。除此之外,我们还提供了更加完整的文件系统的接口,以及基于我们 SAL 组件的网络接口。这为 POSIX 的应用迁移打下了基础,可以使得较复杂的 POSIX 应用也可以在 RT-Thread 的平台上经过小部分修改就可以在 RT-Thread 上应用。 -![image-20220419112531014](../figure/menuconfig_info.png) +![image-20220419112531014](../figures/menuconfig_info.png) * POSIX FILE 相关接口,包含了文件系统的内容,接口比较丰富;标准 I/O 接口,Poll / Select 等较复杂的功能也已经支持 * Delay 相关接口 @@ -27,20 +27,20 @@ POSIX 为了提供不同层级的RTOS能力,划定了 PSE51, PSE52, PSE53, PSE * Dynamic 动态模块 * POSIX 的 标准 IPC 接口 :pipe ,message,semaphore 等函数 - 通过上面灵活的配置项,开发者可以启用对应的功能来添加该类函数的支持;这对与同样使用功能宏来标识一些函数功能的 POSIX 接口来说十分类似,在该界面可以配置对应的 POSIX 函数支持。 +通过上面灵活的配置项,开发者可以启用对应的功能来添加该类函数的支持;这对与同样使用功能宏来标识一些函数功能的 POSIX 接口来说十分类似,在该界面可以配置对应的 POSIX 函数支持。 -## 3 Net 接口 +## Net 接口 1. 在 menuconfig 中打开 ```Enable BSD Socket I/O ``` 就可以使用标准 Socket 进行编程 -### 4 Filesystem 接口 +## Filesystem 接口 1. 在 menuconfig 中打开```Enable POSIX file system and I/O``` 配置具体的 POSIX 函数接口 2. 按照需求打开需要的功能,例如 设备I/O,AIO,MMAN 之类的功能 -### 5 其他接口 +## 其他 POSIX 接口 - 其他接口也是一样,开打对应的 menuconfig 选项,即可使用对应头文件,及提供的函数来编程。整个过程不需要 rt-thread 的头文件参与,使编译出的代码运行在 RT-Thread 操作系统之上。 +其他接口也是一样,开打对应的 menuconfig 选项,即可使用对应头文件,及提供的函数来编程。整个过程不需要 rt-thread 的头文件参与,使编译出的代码运行在 RT-Thread 操作系统之上。 ## 特别注明:标准输入输出函数 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 index a39a925..4acbaca 100644 --- 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 @@ -51,7 +51,7 @@ Std1003.1c-1995)标准里,该标准定义了一套 C 程序语言的类型、 在 RT-Thread 中使用 POSIX API 接口包括几个部分:libc(例如 newlib),filesystem,pthread 等。 -![image-20220419112531014](../figure/menuconfig_pthread.png) +![image-20220419112531014](../figures/menuconfig_pthread.png) 1. 在 menuconfig 中打开 pthread ,设定支持得最大 pthread 数目 2. 在用户代码中,即可使用 ```pthread.h, sche.h ``` 这些 pthread 提供的头文件来编程 @@ -2713,7 +2713,7 @@ poll改变了文件描述符集合的描述方式,使用了pollfd结构而不 ## timer 函数 -![image-20220419112531014](../figure/menuconfig_timer.png) +![image-20220419112531014](../figures/menuconfig_timer.png) 1. 在 menuconfig 中打开 timer 即可 2. 在用户代码中,即可使用 ``` ``` 这些 timer 提供的头文件来编程 -- Gitee From 909dc6cd0bf739fbd72688d2c5cc95dde24a2f17 Mon Sep 17 00:00:00 2001 From: liuxianliang Date: Fri, 29 Apr 2022 11:56:34 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E3=80=90=E6=9B=B4=E6=96=B0=E3=80=91POSIX?= =?UTF-8?q?=20=E6=96=87=E6=A1=A3=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rt-thread-standard/_sidebar.md | 8 +- .../libc/posix/introduction.md | 34 +- .../libc/posix/posix-clock.md | 0 .../libc/posix/posix-delay.md | 0 .../libc/posix/posix-file.md | 112 +++ .../libc/posix/posix-ipc-message.md | 291 +++++++ .../libc/posix/posix-ipc-pipe.md | 0 .../libc/posix/posix-ipc-semaphore.md | 326 ++++++++ .../libc/posix/{pse51.md => posix-pthread.md} | 731 +----------------- .../libc/posix/posix-timer.md | 29 + 10 files changed, 770 insertions(+), 761 deletions(-) create mode 100644 rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-clock.md create mode 100644 rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-delay.md create mode 100644 rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-file.md create mode 100644 rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-ipc-message.md create mode 100644 rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-ipc-pipe.md create mode 100644 rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-ipc-semaphore.md rename rt-thread-version/rt-thread-standard/programming-manual/libc/posix/{pse51.md => posix-pthread.md} (72%) create mode 100644 rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-timer.md diff --git a/rt-thread-version/rt-thread-standard/_sidebar.md b/rt-thread-version/rt-thread-standard/_sidebar.md index e2913a3..52391d6 100644 --- a/rt-thread-version/rt-thread-standard/_sidebar.md +++ b/rt-thread-version/rt-thread-standard/_sidebar.md @@ -60,8 +60,12 @@ - [编译器配平层](/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/dlmodule.md) + - [POSIX FILE](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-file.md) + - [POSIX Pthread](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-pthread.md) + - [POSIX Timer](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-timer.md) + - [POSIX-IPC semaphore](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-ipc-semaphore.md) + - [POSIX-IPC messages](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-ipc-messages.md) + - [POSIX 动态模块](/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) - [AT 命令](/rt-thread-version/rt-thread-standard/programming-manual/at/at.md) 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 eaa1b32..5512aa5 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 @@ -33,36 +33,12 @@ RT-Thread 针对 PSE51 的标准规定的头文件及对应接口,提供了大 1. 在 menuconfig 中打开 ```Enable BSD Socket I/O ``` 就可以使用标准 Socket 进行编程 +更加详细的 Socket 编程内容,可以到**网络组件**下查看。 + +[函数介绍](../../sal/sal.md## BSD Socket API 介绍) +[简单例程](../../../tutorial/qemu-network/README.md) + ## Filesystem 接口 1. 在 menuconfig 中打开```Enable POSIX file system and I/O``` 配置具体的 POSIX 函数接口 2. 按照需求打开需要的功能,例如 设备I/O,AIO,MMAN 之类的功能 - -## 其他 POSIX 接口 - -其他接口也是一样,开打对应的 menuconfig 选项,即可使用对应头文件,及提供的函数来编程。整个过程不需要 rt-thread 的头文件参与,使编译出的代码运行在 RT-Thread 操作系统之上。 - -## 特别注明:标准输入输出函数 - -如果需要读写标准输入输出,调用以下函数/功能,请开启 `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-clock.md b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-clock.md new file mode 100644 index 0000000..e69de29 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 0000000..e69de29 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 0000000..7ed40ed --- /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 0000000..f4e64c3 --- /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 0000000..e69de29 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 0000000..3387d7a --- /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/libc/posix/pse51.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/libc/posix/pse51.md rename to rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-pthread.md index 4acbaca..3c6fef5 100644 --- a/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/pse51.md +++ b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-pthread.md @@ -1,5 +1,3 @@ -# POSIX-PSE51 接口 - ## Pthreads 简介 POSIX Threads 简称 Pthreads,POSIX 是 “Portable Operating System @@ -1359,623 +1357,7 @@ 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; -} -``` +## Pthread 高级函数功能 ## 线程高级编程 @@ -2629,114 +2011,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不同的地方。 - - -## 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/posix-timer.md b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-timer.md new file mode 100644 index 0000000..94d04e9 --- /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 -- Gitee From 496266c89dd86d011ee7d8a9ec981714473585cd Mon Sep 17 00:00:00 2001 From: liuxianliang Date: Fri, 29 Apr 2022 12:10:33 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E3=80=90=E4=BF=AE=E6=94=B9=E3=80=91?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC=E9=93=BE=E6=8E=A5=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rt-thread-version/rt-thread-standard/_sidebar.md | 2 +- .../programming-manual/libc/posix/introduction.md | 4 ++-- .../programming-manual/libc/posix/posix-file.md | 2 +- .../programming-manual/libc/posix/posix-pthread.md | 3 --- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/rt-thread-version/rt-thread-standard/_sidebar.md b/rt-thread-version/rt-thread-standard/_sidebar.md index 52391d6..70c7144 100644 --- a/rt-thread-version/rt-thread-standard/_sidebar.md +++ b/rt-thread-version/rt-thread-standard/_sidebar.md @@ -64,7 +64,7 @@ - [POSIX Pthread](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-pthread.md) - [POSIX Timer](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-timer.md) - [POSIX-IPC semaphore](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-ipc-semaphore.md) - - [POSIX-IPC messages](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-ipc-messages.md) + - [POSIX-IPC messages](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-ipc-message.md) - [POSIX 动态模块](/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) 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 5512aa5..8559e04 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 @@ -35,8 +35,8 @@ RT-Thread 针对 PSE51 的标准规定的头文件及对应接口,提供了大 更加详细的 Socket 编程内容,可以到**网络组件**下查看。 -[函数介绍](../../sal/sal.md## BSD Socket API 介绍) -[简单例程](../../../tutorial/qemu-network/README.md) +[函数介绍](../../sal/sal.md) +[简单例程](../../../tutorial/qemu-network/tcpclient/tcpclient) ## Filesystem 接口 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 index 7ed40ed..07efce2 100644 --- 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 @@ -4,7 +4,7 @@ ![文件管理常用函数](../../filesystem/figures/fs-mg.png) -具体的文件系统函数已经在[虚拟文件系统](../../filesystem/filesystem.md##文件管理)中实现,可以点此[跳转](../../filesystem/filesystem.md##文件管理) +具体的文件系统函数已经在[虚拟文件系统](../../filesystem/filesystem.md)中实现,可以点此[跳转](../../filesystem/filesystem.md) ## select 函数 diff --git a/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-pthread.md b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-pthread.md index 3c6fef5..94aafac 100644 --- a/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-pthread.md +++ b/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-pthread.md @@ -1356,9 +1356,6 @@ int rt_application_init() } ``` - -## Pthread 高级函数功能 - ## 线程高级编程 本章节会对一些很少使用的属性对象及相关函数做详细介绍。 -- Gitee From 4189eb823b890317973103d78020be7be2fd617f Mon Sep 17 00:00:00 2001 From: liuxianliang Date: Fri, 29 Apr 2022 15:54:25 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E3=80=90=E4=BF=AE=E6=94=B9=E3=80=91?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rt-thread-version/rt-thread-standard/_sidebar.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/rt-thread-version/rt-thread-standard/_sidebar.md b/rt-thread-version/rt-thread-standard/_sidebar.md index 70c7144..9c5889e 100644 --- a/rt-thread-version/rt-thread-standard/_sidebar.md +++ b/rt-thread-version/rt-thread-standard/_sidebar.md @@ -59,13 +59,13 @@ - [概况](/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) - - [POSIX FILE](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-file.md) - - [POSIX Pthread](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-pthread.md) - - [POSIX Timer](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-timer.md) - - [POSIX-IPC semaphore](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-ipc-semaphore.md) - - [POSIX-IPC messages](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/posix-ipc-message.md) - - [POSIX 动态模块](/rt-thread-version/rt-thread-standard/programming-manual/libc/posix/dlmodule.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) - [AT 命令](/rt-thread-version/rt-thread-standard/programming-manual/at/at.md) -- Gitee