一种Linux系统下的线程通信方法

2017-11-01 17:14
计算机应用与软件 2017年10期
关键词:数据项线程指向

王 天 伟

(天津通博视源技术有限公司 天津 300000)

一种Linux系统下的线程通信方法

王 天 伟

(天津通博视源技术有限公司 天津 300000)

提出一种结合数据和资源管理的线程通信方法,从数据结构上对消息进行定义和描述,并在此基础上设计和实现具备消息供给/回收策略的消息工厂。每个消息的内部会维护一个栈式数据结构,用于存放需要传递给其他线程的数据;然后基于POSIX消息队列实现用于传递消息的邮箱。发送线程从消息工厂取出一条消息并通过对数据栈进行压栈操作将待发送数据提交给消息,然后将消息邮递到邮箱里,接收线程从邮箱中取出消息并将数据依次从消息的数据栈中弹出,并在数据处理完成后将消息返还给消息工厂。

多线程通信 消息工厂 邮箱

0 引 言

在嵌入式应用领域,越来越多的系统设计方案采用了基于操作系统的软件架构,将诸如任务调度、硬件资源管理、图形界面等方面的功能交由操作系统管理。而应用软件设计本身只需关注与系统数据流相关的业务逻辑和资源调度即可。Linux系统是一个开源的操作系统,底层驱动越来越完善,在各个行业也都有比较成熟的开源软件库,因而基于Linux系统的开发具有软件开发资源丰富、系统成本低、可定制程度高等特点。多线程常常是软件设计中不可回避的问题,线程任务规划、线程之间的同步和数据交互方式往往是实现过程中的一个难点,尤其会对后期的调试和维护带来重大的影响。本文从基于数据流的角度出发,提出了一种Linux系统下的多线程通信方法,并对其实现细节进行了详细的论述。

1 消息与消息工厂

一般出于对系统成本的考虑,应用软件可使用的物理资源往往是有限的,因而在设计的过程中必须要考虑对与数据相关资源的回收和重复使用的问题。此外,对于数据发送线程来说其职能只是将数据发送出去,并不关心数据接收线程拿到数据会以何种方式进行处理(直接进行数据处理或是转发给其他线程)以及数据处理完成的时刻。而且还可能存在多个线程同时(多核处理器)对同一数据进行处理的情况。各线程对数据的处理时间可能各不相同,因而较难确定释放资源的时间,所以需要实现一种通知机制,保证应用软件能够捕获任意消息所携带的数据被处理完成的时刻,以便进一步进行相关处理。这里借用工厂模式设计思想设计一个具备消息供给/回收机制的消息工厂,其功能如下:

1) 能够实时提供消息给发送线程使用,发送线程得到消息后可向消息中添加数据项;

2) 工厂的供给能力(支持的最大消息数)及消息可携带的数据项的数量可配置;

3) 接收线程可实时将消息归还给消息工厂以实现对消息的回收,同时保证工厂在回收消息期间能够自动释放与消息内各数据项相关联的资源;

4) 各资源释放完成后发出通知,告知消息所携带的数据已被处理完成,以便应用程序作相关处理。

1.1 消息的定义

定义消息的数据结构:

struct message

{

__s32 id;

__s32 prio;

struct msg_ops *ops;

__s32 (*cb_finish)(void *msg);

__s32 *cb_finish_arg;

void **resource;

void (**release)(void *data);

struct msg_payload *payload;

__s32 payload_num;

__s32 cur_payload_idx;

void *node;

};

结构中各成员含义如表1所示。

表1 消息结构描述

续表1

结构struct msg_ops用于描述对消息中数据的操作方法,其定义如下:

struct msg_ops

{

__s32 (*push)(void *msg, void *data,

__u32 len,

void (*release)(void *resource),

void *resource);

void* (*pop)(void *msg, __s32 *len);

__s32 (*count)(void *msg);

};

表2 对消息中数据的操作方法

结构struct msg_payload用于描述一个数据项,其定义如下:

struct msg_payload

{

void *data;

__s32 len;

};

表3 消息中数据项

1.2 消息工厂的定义和初始化

用一个全局链表描述消息工厂,定义如下:

struct list_head

{

struct list_head *next;

struct list_head *prev;

void *owner;

};

其中owner指向消息,next指向工厂中下一条消息的node成员(见struct message的定义),实际上node的类型是struct list_head;prev指向工厂中上一条消息的node成员。

消息工厂是由一个struct list_head类型的根节点和若干条struct message类型的消息组成的,根节点的prev指向根节点本身,根节点的next指向消息工厂内第一条消息;每条消息内部有存在一个node节点,其prev指向工厂中上一个消息节点(对于第一条消息,其prev指向根节点),next指向工厂中下一个消息节点(对于最后一条消息,其next指向根节点)。

消息工厂内数据组织关系如图1所示。

图1 消息工厂数据组织关系

定义函数message_factory_create实现(创建消息工厂):

__s32 message_factory_create(__u32 msg_num,

__u32 payload_num)

参数:

msg_num:工厂支持的消息的个数;

payload_num:消息内数据项的个数。

成功返回0,失败返回-1。

创建消息工厂将依次完成以下工作:

1) 创建根节点(以下称其为“全局链表”),并将其prev和next赋值为根节点的首地址;

2) 创建msg_num个消息(为struct message分配内存),为每个消息创建可以容纳payload_num个数据项的数据栈;

3) 创建两个指针数组,分别用于存放与payload_num个数据项相对应的资源及其释放方法;

4) 将各个消息依次加入到全局链表中。

上述过程的执行流程如图2所示。

图2 创建消息工厂

1.3 供给消息

定义函数msg_factory_provide(生产消息):

struct message *msg_factory_provide (__s32 id,

__s32 prio)

参数:

id:邮箱id(指定消息将要进入的邮箱);

prio:指定消息在邮箱中的优先级别,接收线程总是先取得最高优先级的消息。

执行成功返回一条消息,否则返回空指针。

该函数从全局链表头取出一个节点给发送线程,发送线程根据业务逻辑,向其中加入数据项。

1.4 回收消息

定义函数msg_factory_recycle(回收消息):

void message_factory_recycle(struct message *msg)

参数:

msg:待回收的消息。

该函数将消息对应的节点加到全局链表尾,接收线程在完成对消息处理后通过该函数实现对消息的回收。其流程如图3所示。

图3 消息回收流程

2 邮 箱

邮箱用于暂存消息,发送线程和接收线程之间通过邮箱进行数据交互,发送线程对从消息工厂中得到的消息进行加工,即将需要传递给接收线程的数据压入消息的数据栈中,同时指定与各数据项相关联的待释放资源及其释放方法,然后将消息发送到邮箱中;接收线程收到消息后依次将数据从数据栈中弹出,获得各数据的首地址和有效字节长度,进而进行相应的数据处理,当所有数据处理完成后调用函数msg_factory_recycle将消息归还给消息工厂。

这里基于POSIX消息队列设计了邮箱模块,具备以下功能:

1) 创建邮箱时可指定其名称和可暂存的最大消息数;

2) 向邮箱发送消息时可提供超时时间,以保证当邮箱满时,能够及时释放待发送数据相关资源能;

3) 当邮箱为空时接收线程阻塞;

4) 邮箱里有消息时,接收线程总是先得到当前优先级最高的消息。

2.1 创建邮箱

定义函数mailbox_create(创建邮箱):

__s32 mailbox_create(char *name, __s32 msg_num_max)

参数:

name:邮箱名称;

msg_num_max:邮箱内能够存放的消息数;

执行成功返回新创建邮箱的id。

2.2 从邮箱接收消息

定义函数mailbox_pend(接收消息):

__s32 mailbox_pend(struct message *msg)

参数:

msg:存放接收消息;

邮箱为空时,接收线程会阻塞,直到有消息到达。

2.3 向邮箱发送消息

定义函数mailbox_post(发送消息),其原型如下:

__s32 mailbox_post(struct message *msg,

__u32 timeout)

参数:

msg:待发送消息

timeout:超时时间(单位:us)

邮箱未满时,函数直接返回,否则发送线程阻塞,若在超时时间到达之前发送出去,函数返回0,否则返回-1。

2.4 Linux内核支持

在进行嵌入式Linux内核的剪裁或移植阶段,需要保证内核中已开启对POSIX消息队列的支持,方法是查看内核编译选项的“General setup->POSIX Message Queues”。

3 结 语

在Linux系统下有很多种方式可以用来实现线程之间的数据交互,比如共享内存、socket、System V IPC或直接使用POSIX消息队列等。对于文中提到的方法,其优点在于能够通过这样一种方式从数据的角度出发,按照对数据处理方式或引用方式的不同将系统功能划分为可独立设计和维护的子功能模块,业务逻辑清晰,而且线程之间的数据传递是“零拷贝”(拷贝的只是数据的首地址)的,所以消息传递带来的时间开销基本上是可以忽略不计的。这一线程通信方法已成功应用于某机器视觉类项目,运行稳定,性能表现良好。此外,文中提到的方法虽然是基于Linux系统进行设计,但也适用于其他操作系统,主要区别在于邮箱的实现,不同的操作系统对于邮箱的“底层”支持不同,需要进行一定的调整或封装工作。

[1] Chris Simmonds.Mastering Embedded Linux Programming[M].Birmingham:Packt Publishing Ltd,2015:247-265.

[2] Alex González.Embedded Linux Projects Using Yocto Project Cookbook[M].Birmingham:Packt Publishing Ltd,2015:185-192.

[3] 史蒂文斯, 拉戈.UNIX环境高级编程[M].3版.北京:人民邮电出版社,2014:533-587.

[4] Steven J M,William C W.Java设计模式[M].2版.北京:电子工业出版社,2012:158-164.

[5] Texas Instruments Incorporated.TMS320C6000 DSP/BIOS 5.x Application Programming Interface (API) Reference Guide[EB/OL].2012.http://www.ti.com/lit/ug/spru403s/spru403s.pdf.

[6] 左飞.C++数据结构原理与经典问题求解[M].北京:电子工业出版社,2008:153-199.

[7] 阎宏.Java与模式[M].北京:电子工业出版社,2002:127-142.

[8] Scott Meyers.Effective C++[M].3版.北京:电子工业出版社,2006:61-75.

[9] Bruce Powel Douglass.C嵌入式编程设计模式[M].北京:机械工业出版社,2012:116-198.

[10] Texas Instruments Incorporated.SYS/BIOS (TI-RTOS Kernel) v6.46 User’s Guide[EB/OL].2016.http://www.ti.com/lit/ug/spruex3q/spruex3q.pdf.

AMETHODOFCOMMUNICATIONBETWEENTHREADSUNDERLINUXSYSTEM

Wang Tianwei

(TianjinTongboshiyuanTechnologyCo.,Ltd.,Tianjin300000,China)

A multi-thread communication method based on data and resource management is proposed. The concept of message is defined by a data structure, on the basis of which a message factory with supply/recycling strategy is designed and realized. First, each message maintained a stack of data structure, which was used to store the data that need to be passed to other threads. Afterwards, the mailbox based on POSIX message queue was implemented for passing messages. The sending thread took a message from the message factory and the data was submitted to the message by pushing onto a data stack. Then the message would be sent to a mailbox. The receiving thread fetched a message from the mailbox and popped the data sequentially from the message stack. At last the message would be returned to the message factory after the data had been processed.

Multi-thread communication Message factory Mailbox

TP3

A

10.3969/j.issn.1000-386x.2017.10.059

2016-09-01。王天伟,硕士,主研领域:工控软件。

猜你喜欢
数据项线程指向
国六柴油车远程排放监测数据项间相关性特征研究*
科学备考新指向——不等式选讲篇
基于相似度的蚁群聚类算法∗
基于C#线程实验探究
基于国产化环境的线程池模型研究与实现
非完整数据库Skyline-join查询*
线程池调度对服务器性能影响的研究*
基于Python的Asterix Cat 021数据格式解析分析与实现
把准方向盘 握紧指向灯 走好创新路
Java的多线程技术探讨