卫洪春
(四川文理学院计算机学院,四川达州635000)
摘要:为了实现线程间消息的有效传递,在探讨进程和线程的概念、进程和线程的区别、同步和异步处理机制、同步访问技术、Linux下的软中断等技术的基础上,提出一种实现线程间有效传递消息的方法。该方法可以传递任意类型、大小和格式的消息,并且完全由实现者决定和控制,具有较好的实用价值。
教育期刊网 http://www.jyqkw.com
关键词 :进程;线程;异步处理;同步访问技术;消息传递
中图分类号:TN911?34;TP391 文献标识码:A 文章编号:1004?373X(2015)14?0074?04
收稿日期:2015?02?13
基金项目:国家自然科学基金:基于情感语义的全局均衡智能推荐理论与应用研究(61152003);四川省教育厅项目:达州市中小型医院LIS 系统的设计与实现(15ZB0326);四川文理学院智能物流创新团队支助项目
0 引言
基于Linux操作系统的嵌入式开发和应用已非常广泛,在嵌入式系统或子系统中大部分应用都采用单个CPU,应用层软件开发大多使用多线程开发技术。在同一个功能模块中使用多个线程来实现各个子功能模块,线程间的通信就不可避免。本文研究了一种在同一进程中的2个不同线程间消息传递的实现方法。该方法的优点是实现了消息的透明传递。传递消息与其传递过程无关。在这种方法中,待传递消息的具体内容、构成形式、信息量等都不受限制,可根据实际需要来构建消息。这就能实现信息与载体的完全无关性和分离。利用该方法,待传递的消息可以是字符或整形数据,也可以是满足实际需要的复杂构造型数据,携带的具体信息数据等都不受限制,可根据实际需要进行定义和构造。
1 进程、线程与信号量
进程是具有一定独立功能的程序在一定的内存地址空间中,属于这个程序的数据集合上的一次运行活动,它具有产生、运行、死亡的动态变化的活动过程。在进程产生时,获得了地址空间的系统资源才能运行,这就需要系统给予它所需的资源分配,所以进程是系统进行资源分配的一个独立单位,也是用于组织和管理系统资源的单位。进程将相关的资源组织在一起,这些资源包括:内存地址空间、程序、数据等。将这些资源以进程的形式组织起来,可以使操作系统更容易管理这些资源。
线程是进程的一个执行实体,它必须属于某一个进程。线程是CPU 调度和分派的基本单位,也是进程中能独立运行的基本单位。线程不独占处理CPU之外的系统资源,它与同属一个进程的其他线程共享进程所拥有的这些资源,但只拥有一个线程的进程情况除外。线程基本上只独占在它运行时必不可少的资源(如程序计数器、一组寄存器和栈),但是由于线程比进程更小,拥有的系统资源更少,基本上不拥有系统资源。在线程间切换时,对运行的上下文处理工作就变得更少,故对它的调度所付出的开销就小得多,能更高效地提高系统内多个任务间并发执行的程度[1?5]。
1.1 进程与线程的主要区别
进程和线程存在以下几个方面的主要区别。
(1)地址空间和其他资源:进程独占系统分配给它的地址空间,它有自己的数据空间、代码和数据。不同进程间的这些资源是隔离的,并受到系统的保护,不允许一个进程直接访问2个进程的资源。若需要则通过进程间的通信来实现。而同属于一个进程的多个线程则共享该系统分配给该进程的地址空间、代码和数据,每个线程都可以直接访问这些共享的资源。线程间不可共享的资源是它自己的堆栈、程序计数器及其执行的上下文。CPU资源无论对于进程还是线程都不是独占的,而是共享的。进程不能直接参与CPU 资源的分配和调度,而线程是CPU资源分配和调度的单位,可以直接竞争分配和调度。进程必须依赖它的线程参与到CPU资源的竞争分配和调度中,从而获得CPU资源。多线程主要是为了减少在CPU 调度切换时消耗的时间,充分发挥CPU的效率。
(2)通信:由于进程的地址空间、代码空间和数据都受到保护,相互之间都是透明的。所以要想在进程间进行信息的传递通信,就需要使用IPC来实现。属于一个进程的不同线程间的通信相当容易,直接读/写进程数据段(如全局变量)就可实现通信,通信变简单了,但容易引起通信数据的不完整或不一致性的问题。这就需要在进行线程间通信时,保证通信时的原子操作。
(3)调度和切换:进程进行上下文切换时,需要保存切换出去进程的地址空间、代码空间、数据、当前堆栈和程序计数器等资源信息,还需恢复已调度进程的资源信息。而在同一个进程间的不同线程的上下文切换时,仅需要保存切换出去的线程的堆栈和程序计数器,恢复调度过的线程的相关资源。因线程不需要维护和管理数据空间和运行空间,在进行切换时,需要保存和恢复的内容就比进程少得多,所以线程上下文切换比进程上下文切换要快得多,线程更能提高CPU的利用率。
1.2 同步与异步处理
同步处理,就是共同实现完成某一特定功能的多个进程或线程间存在严格的运行次序,且运行的结果及返回具有依赖性。这些进程或线程之间是相互配合、协助与合作的关系。在这一特定功能的协作尚未完成之前,是不会响应其他的请求。
异步处理,就是多个进程或线程间不存在运行次序问题和依赖关系。他们的运行不需要相互协同和配合,而是各自独立运行。在这种处理方式下,就会产生同步访问的问题。
同步访问过程所面临的问题是:共享数据或代码段被多个线程并发或同时访问,会出现共享数据的不完整性和不一致性。导致功能错误或无法实现功能,或引起异常甚至系统崩溃。保证线程对共享数据的原子操作,以达到时刻维护数据完成性和一致性的目的,就是使用同步访问技术。
1.3 同步访问技术
(1)临界区:是指一段不可重入的代码,对于一个线程必须对临界区执行原子操作,即要么不执行临界区的代码,要么在执行临界区代码时一次性执行完成且中间不得被其他线程执行。在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图访问临界区,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进入临界区的线程离开,临界区在被释放后,其他线程才可以抢占。
(2)互斥量:采用互斥对象机制。只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问。互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享。
(3)信号量:其是一个互斥的资源,在任何时刻只能有一个线程拥有它。当信号量被一个线程持有后,其他线程就获取不到该信号量的持有权,而被挂起等待,一直等待持有这个信号量的线程将他释放出来,再进行竞争这个信号量的持有权。信号量保证了在某一时间段内只有一个线程访问临界区,而其他需要访问的线程进行等待。保证了线程对临界区的原子操作。
(4)事件:通过通知的方式来保持线程的同步,可以方便地实现对多个线程的异步处理操作。
1.4 Linux下的软中断信号signal
软中断信号(signal,简称为信号)用来通知一个线程,有其他线程向它发送了异步事件,需要它进行适当的处理。信号只是用来通知某线程发生了什么事件,并不会给该线程传递任何有关该事件的数据,线程对收到的各种信号如何处理,由该线程自己决定。不过在Linux的进程中处理方法通常有3种:第1种是类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处理;第2种是忽略某个信号,对该信号不做任何处理,就象未发生过一样;第3种是对该信号的处理保留系统的默认值,这种缺省操作对大部分的信号而言是进程终止。进程通过系统调用signal来指定进程对某个信号的处理行为。在进程表的表项中有一个软中断信号域,该域的每位对应一个信号。当有信号发送给进程时,对应位置位。由此可以看出,进程对不同的信号可以同时保留,但对于同一个信号,进程在处理之前并不知道来了多少个[6?10]。
1.5 不可靠信号与可靠信号
不可靠信号:当向一个进程发送singal 时,当进程还没有处理该信号(此时被称作pending,未决信号)或正在调用信号处理函数时,进程又收到了一个同样的信号,kernel会把第2个信号丢弃,或者与一个信号合并,只对这个信号处理1次,而不是收到这个信号多少次就处理多少次。不可靠的信号是指1~31的信号,这些都是从Unix系统中保留下来的。
可靠信号是指不论信号是否相同,都不会丢弃或合并,信号进行排队处理,信号不丢失,收到多少次就会处理多少次。SIGRTMIN~SIGRTMAX都是可靠信号[11]。
2 线程间消息实现方法
消息传递方法实现原理:使用与1个进程中的2个线程间的消息和参数传递,它由2部分组成,一部分是把待发送的信息内容放到指定的共享内存空间中,然后使用Linux 下的signal 信号向接收线程发异步信号;另一部分是接收线程收到信号后,到指定的所属进程内存空间获取具体的消息内容,从而实现线程间的异步通信,且可以传递较多的数据和参数。由于在线程间使用共享的内存空间用于存放传递消息的内容;所以在存放和提取消息内容时,需要使用前述的线程间同步与互斥机制,保证消息内容在传递过程中不被破坏和保持数据的完整性。在线程间实现消息通告方式采用Linux 的signal方式,即软件中断方式。它是异步事件,由操作系统的内核实现。采用异步通告方式,是为了充分利用系统资源,特别是CPU资源。
2.1 信号的选择
在上述论述中已经论述了信号分为不可靠信号和可靠信号2种。为了保证消息被安全、可靠地传递给对方,就必须选择可靠信号。由于大部分可靠信号都有专门的用途,并且在这种消息实现中,所起的作用仅是异步方式通知对方有消息来了,并不指特定的消息。具体是什么消息,可通过获取到的消息的内容获知,选择用于应用程序的SIGUSER1即可。传递具体消息的内容、格式和大小,在资源够用的前提下,没有任何限制,有极大的灵活性,只要保证消息封装和消息提取保持一致即可[12]。
2.2 消息存放的位置
消息存放的位置有2种处理方式,固定位置和动态位置。
固定位置方式:发送和接收消息的线程都知道消息存放的地址,发送消息的线程就将具体封装好的消息放到这个固定的内存空间中;接收线程收到信号后,就到这个固定的内存空间来获取消息。
动态位置方式:由发送消息的线程在发送消息之前确定消息内容存放在共享内存空间位置,将消息内容存放在这个空间之后,向接收线程发送带有消息位置参数信号。接收线程在收到信号时提取出信息的位置参数,然后到该位置获取消息的内容。Linux下的signal在进程间是不能带有参数的,但在线程间可以带有一个整型参数。在这里所带的整型参数是一个地址,即线程共享内存中的地址。线程都可以访问共享内存空间,所以可以使用。
3 消息传递过程
在图1 所示的消息传递示意图中,进程P 中包含4 个线程,实现线程4向线程1消息传递。线程4是消息的发送者,线程1是消息的接收者。首先,线程4将欲传递的消息内容沿着①的方向放在进程p的内存空间的地址a处;然后,线程4沿着②的方向向线程1发放sig?nal信号,通知线程1,有消息发送给它,通知他去共享内存空间的地址a处获取消息;最后,线程1收到信号后,到地址a处提取消息,然后进行消息处理,完成了一次消息传递过程。
这种消息传递方法是满足线程1和线程4间异步并发的运行,线程4在向线程1发送消息时,不需要知道线程1的当前运行状态,只在它需要向线程1发消息的任何时刻发送即可。使得传递的消息的类型和所带参数个数以及格式等都与消息传递过程无关,只与系统资源的限制有关。这也对2个线程进行相互隔离,屏蔽了消息的产生和消息处理的具体过程。
4 结语
在用户空间的应用程序中,一个进程由多个线程来实现一个功能模块中的不同子模块的功能,实现各个子功能模块的线程间即相互独立,又相互关联。在他们之间,都处于同一个进程内部,进行通信是不可避免的。使用消息方法既能保证线程间的异步并发和同步协作,又能充分利用系统的资源。在实际应用中,该方法能有效降低线程间的耦合性,明确各个线程的职责和范围[13?17],降低软件实现的复杂度,具有较强的现实作用和意义,为线程间进行消息交互提供了较好的参考方法和思路。
教育期刊网 http://www.jyqkw.com
参考文献
[1] 眭俊华,刘慧娜,王建鑫,等.多核多线程技术综述[J].计算机应用,2013,33(1):239?242.
[2] 骆斌,费翔林.多线程技术的研究与应用[J].计算机研究与发展,2000,37(4):407?412.
[3] 闫伟,叶建栲.多线程技术在Android 手机开发中的应用[J].信息通信,2012(1):46?46.
[4] 施惠丰,袁道华.基于多核的多线程程序优化研究[J].计算机技术与发展,2010,20(6):70?73.
[5] TANENBAUM A S.现代操作系统[M].陈向群,马洪兵,译.北京:机械工业出版社,2009.
[6] 毛德操,胡希明.Linux内核源代码情景分析[M].杭州:浙江大学出版社,2001.
[7] STEVENS W R,RAGO Stephen A.Unix环境高级编程[M].尤晋元,张亚英,戚正伟,译.北京:人民邮电出版社,2006.
[8] 王文义,武华北.Linux 中进程间信号通信机制的分析及其应用[J].计算机工程与应用,2005(3):108?109.
[9] 屈志强,乔静,胡珊珊.Linux 信号在进程控制中的应用[J].计算机与现代化,2010(6):150?152.[10] 郑尚志,赵小龙,昌杰.Linux信号机制的分析与研究[J].科技资讯,2008(11):98?100.
[11] 陈毅东,李堂秋,郑旭玲.Linux 不可靠信号与可靠信号的比较实验[J].厦门大学学报:自然科学版,2002,41(6):720?725.
[12] 冉鹏,颜纪迅.分时分区操作系统互斥信号量的设计与分析[J].计算机技术与发展,2013,23(1):43?46.
[13] 刘文峰,李程远,李善平.嵌入式Linux操作系统的研究[J].浙江大学学报:工学版,2004,38(4):448?452.
[14] MCCONNELL Steve.代码大全[M].金戈,汤凌,陈硕,等译.北京:电子工业出版社,2006.
[15] 肖竟华,陈岚.Linux 内存管理实现的分析与研究[J].计算机技术与发展,2007,17(2):187?189.
[16] 王兆文,蒋泽军,陈进朝.一种提高Linux 内存管理实时性的设计方案[J].计算机工程,2014,40(9):291?294.
[17] 段哲.嵌入式系统通信机制的研究与应用[J].舰船电子工程,2010,30(7):81?83.
作者简介:卫洪春(1972—),男,四川达县人,讲师,硕士。研究方向为软件工程、数字媒体技术。