《嵌入式系统程序设计》第七周学习总结

《嵌入式系统程序设计》第七周学习总结

教材学习总结

一.有名管道 1.有名管道它可以使互不相关的两个进程实现彼此通信。 该管道可以通过路径名来指出,并且在文件系统中是可见的。 FIFO 是严格地遵循先进先出规则的,对管道及 FIFO 的读总是从开始处返回数据,对它们的写则把数据添加到末尾,它们不支持如 lseek()等文件定位操作。 2.有名管道的创建可以使用函数 mkfifo(),该函数类似文件中的 open()操作,可以指定管道的路径和打开的模式。 3.阻塞打开和非阻塞打开 (1)对于读进程。 若该管道是阻塞打开,且当前 FIFO 内没有数据,则对读进程而言将一直阻塞到有数据写入。 若该管道是非阻塞打开,则不论 FIFO 内是否有数据,读进程都会立即执行读操作。即如果 FIFO内没有数据,则读函数将立刻返回 0。 (2)对于写进程。 若该管道是阻塞打开,则写操作将一直阻塞到数据可以被写入。 若该管道是非阻塞打开而不能写入全部数据,则读操作进行部分写入或者调用失败 4.mkfifo()函数 mkfifo()函数语法

| |
| -------- | :----------------😐
|所需头文件 |#include <sys/types.h> #include <sys/state.h>|
|函数原型 |int mkfifo(const char *filename,mode_t mode)|
|函数传入值| filename:要创建的管道|
|函数传入值| mode:O_RDONLY:读管道 O_WRONLY:写管道 O_RDWR:读写管道 O_NONBLOCK:非阻塞|
|函数传入值| mode:O_CREAT:如果该文件不存在,那么就创建一个新的文件,并用第三个参数为其设置权限|
|函数传入值| mode:O_EXCL:如果使用 O_CREAT 时文件存在,那么可返回错误消息。这一参数可测试文件是否存在|
|函数返回值|成功: 0 出错:-1|
5.消息队列
消息队列的实现包括创建或打开消息队列、添加消息、读取消息和控制消息队列这 4 种操作。其中创建或打开消息队列使用的函数是 msgget(),这里创建的消息队列的数量会受到系统消息队列数量的限制;添加消息使用的函数是 msgsnd()函数,它把消息添加到已打开的消息队列末尾;读取消息使用的函数是msgrcv(),它把消息从消息队列中取走,与 FIFO 不同的是,这里可以指定取走某一条消息;最后控制消息队列使用的函数是 msgctl(),它可以完成多项功能。

6.msgsnd()函数

| |
| -------- | :----------------😐
|所需头文件 |#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h>|
|函数原型 |int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)|
|函数传入值| msqid:消息队列的队列 ID|
|函数传入值| msgp:指向消息结构的指针。|
|函数传入值| msgsz:消息正文的字节数(不包括消息类型指针变量)|
|函数传入值| msgflg:IPC_NOWAIT 若消息无法立即发送(比如:当前消息队列已满),函数会立即返回|
|函数传入值| msgflg:0: msgsnd 调阻塞直到发送成功为止|
|函数返回值|成功: 0 出错:-1|
7.msgrcv()函数

| |
| -------- | :----------------😐
|所需头文件 |#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h>|
|函数原型 |int msgrcv(int msgid, void *msgp, size_t msgsz, long int msgtyp, int msgflg)|
|函数传入值| msqid:消息队列的队列 ID|
|函数传入值| msgp:消息缓冲区, 同于 msgsnd()函数的 msgp|
|函数传入值| msgsz:消息正文的字节数(不包括消息类型指针变量)|
|函数传入值|msgtyp:0:接收消息队列中第一个消息|
|函数传入值|大于 0:接收消息队列中第一个类型为 msgtyp 的消息|
|函数传入值|小于 0:接收消息队列中第一个类型值不小于 msgtyp 绝对值且类型值又最小的消息|
|函数传入值|msgflg:MSG_NOERROR:若返回的消息比 msgsz 字节多,则消息就会截短到 msgsz 字节,且不通知消息发送进程|
|函数传入值|msgflg:IPC_NOWAIT 若在消息队列中并没有相应类型的消息可以接收,则函数立即返回|
|函数传入值|msgflg:0: msgsnd()调用阻塞直到接收一条相应类型的消息为止|
|函数返回值|成功: 0 出错:-1|

8.msgget()函数

| |
| -------- | :----------------😐
|所需头文件 |#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h>|
|函数原型 |int msgget(key_t key, int msgflg)|
|函数传入值| key:消息队列的键值,多个进程可以通过它访问同一个消息队列,其中有个特殊值 IPC_PRIVATE。它用于创建当前进程的私有消息队列|
|函数传入值| msgflg:权限标志位|
|函数返回值|成功:消息队列 ID 出错:-1|

9.msgctl()函数

| |
| -------- | :----------------😐
|所需头文件 |#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h>|
|函数原型 |int msgctl (int msgqid, int cmd, struct msqid_ds *buf )|
|函数传入值| msqid:消息队列的队列 ID|
|函数传入值| cmd:命令参数 IPC_STAT:读取消息队列的数据结构 msqid_ds,并将其存储在buf 指定的地址中|
|函数传入值| cmd:命令参数 IPC_SET:设置消息队列的数据结构 msqid_ds 中的 ipc_perm 域(IPC 操作权限描述结构)值。这个值取自 buf 参数|
|函数传入值| cmd:命令参数 IPC_RMID:从系统内核中删除消息队列|
|函数传入值| buf:描述消息队列的 msgqid_ds 结构类型变量|
|函数返回值|成功: 0 出错:-1|

教材课后延伸

一.信号
1.一个完整的信号生命周期可以分为 3 个重要阶段,这 3 个阶段由 4 个重要事件来刻画的:信号产生、信号在进程中注册、信号在进程中注销、执行信号处理函数,如图 8.6 所示。相邻两个事件的时间间隔构成信号生命周期的一个阶段
2.用户进程对信号的响应3 种方式。
(1)忽略信号,即对信号不做任何处理,但是有两个信号不能忽略,即 SIGKILL 及 SIGSTOP。
(2)捕捉信号,定义信号处理函数,当信号发生时,执行相应的自定义处理函数。
(3)执行缺省操作, Linux 对每种信号都规定了默认操作。
3.信号处理函数
(1)使用 signal()函数处理时,只需要指出要处理的信号和处理函数即可

| |
| -------- | :----------------😐
|所需头文件 |#include <signal.h>|
|函数原型 |void (signal(int signum, void (handler)(int)))(int)|
|函数传入值| signum:指定信号代码|
|函数传入值|handler:SIG_IGN:忽略该信号
|函数传入值| SIG_DFL:采用系统默认方式处理信号|
|函数传入值| 自定义的信号处理函数指针||
|函数返回值|成功:以前的信号处理配置 出错:-1|
二.信号集函数组
1.创建信号函数
①sigemptyset():将信号集合初始化为空。
②sigfillset():将信号集合初始化为包含所有已定义的信号的集合。
③sigaddset():将指定信号加入到信号集合中去。
④sigdelset():将指定信号从信号集合中删除。
⑤sigismember():查询指定信号是否在信号集合之中。
注册信号处理函数主要用于决定进程如何处理信号。这里要注意的是,信号集里的信号并不是真正可以处理的信号,只有当信号的状态处于非阻塞状态时才会真正起作用。因此,首先使用 sigprocmask()函数检测并更改信号屏蔽字(信号屏蔽字是用来指定当前被阻塞的一组信号,它们不会被进程接收),然后使用 sigaction()函数来定义进程接收到特定信号之后的行为。检测信号是信号处理的后续步骤,因为被阻塞的信号不会传递给进程,所以这些信号就处于“未处理”状态(也就是进程不清楚它的存在)。
三.信号量
1.信号量是用来解决进程之间的同步与互斥问题的一种进程之间通信机制,包括一个称为信号量的变量和在该信号量下等待资源的进程等待队列,以及对信号量进行的两个原子操作(PV 操作)。其中信号量对应于某一种资源,取一个非负的整型值。信号量值指的是当前可用的该资源的数量,若它等于 0 则意味着目前没有可用的资源。
2.PV操作
P 操作:如果有可用的资源(信号量值>0),则占用一个资源(给信号量值减去一,进入临界区代码) ;如果没有可用的资源(信号量值等于 0),则被阻塞到,直到系统将资源分配给该进程(进入等待队列,一直等到资源轮到该进程)。
V 操作:如果在该信号量的等待队列中有进程在等待资源,则唤醒一个阻塞进程。如果没有进程等待它,则释放一个资源(给信号量值加一)。
3.Linux 系统中,使用信号量步骤
(1)创建信号量或获得在系统已存在的信号量,此时需要调用 semget()函数。不同进程通过使用同一个信
号量键值来获得同一个信号量。
(2)初始化信号量,此时使用 semctl()函数的 SETVAL 操作。当使用二维信号量时,通常将信号量初始化
为 1。
(3)进行信号量的 PV 操作,此时调用 semop()函数。这一步是实现进程之间的同步和互斥的核心工作部
分。
(4)如果不需要信号量,则从系统中删除它,此时使用 semclt()函数的 IPC_RMID 操作。此时需要注意,
在程序中不应该出现对已经被删除的信号量的操作。

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 5000行 15 20/20
第一周 200/200 2/2 1/1 vi的使用
第二周 1000/1200 3/7 7/8 gcc,gdb,makefile
第三周 500/1700 1/8 4/12 U-Boot
第四周 1500/3200 1/9 6/18 Linux的内核
第五周 1200/4400 1/10 3/21 标准I/O的编程,linux下进程相关的基本系统调用
第六周 1000/5400 1/11 4/25 进程间通讯
第七周 1500/6800 1/12 5/30 有名管道和消息队列

参考资料

  • 《嵌入式Linux应用程序开发标准教程》
posted @ 2017-06-03 16:43  黑麦威士忌  阅读(206)  评论(2编辑  收藏  举报