-
2014-01-02 14:13:17
在Qt的众多与众不同的特点中,信号(Signal)/槽(Slot)机制是Qt的一个中心特征并且也许是Qt与其它工具包的最大不相同的部分。信号和槽主要用于对象之间的通讯。
信号与插槽机制提供了对象间通信机制,它易于理解和使用,并完全被Qt图形设计器所支持。
图形用户接口的应用需要对用户的动作做出响应。例如,当用户点击了一个菜单项或是工具栏的按钮时,应用程序会执行某些代码。大部分情况下,我们希望不同类型的对象之间能够进行通信。程序员必须把事件和相关代码联系起来,这样才能对事件做出响应。以前的工具开发包使用的事件响应机制是已崩溃的,不够健壮的,同时也不是面向对象的。Trolltech已经创立了一种新的机制,叫做“信号与插槽”。信号与插槽是一种强有力的对象间通信机制,它完全可以取代原始的回调和消息映射机制;信号与插槽是迅速的,类型安全的,健壮的,完全面向对象并用c++来实现的一种机制。在以前,当我们使用回调函数机制来把某段响应代码和一个按钮的动作相关联时,我们通常把那段响应代码写成一个函数,然后把这个函数的地址指针传给按钮,当那个按钮被按下时,这个函数就会被执行。对于这种方式,以前的开发包不能够确保回调函数被执行时所传递进来的函数参数就是正确的类型,因此容易造成进程崩溃,另外一个问题是,回调这种方式紧紧的绑定了图形用户接口的功能元素,因而很难把开发进行独立的分类。Qt的信号与插槽机制是不同的。Qt的窗口在事件发生后会激发信号。例如一个按钮被点击时会激发一个“clicked”信号。程序员通过建立一个函数(称作一个插槽),然后调用connect函数把这个插槽和一个信号连接起来,这样就完成了一个事件和响应代码的连接。信号与插槽机制并不要求类之间互相知道细节,这样就可以相对容易的开发出代码可重用的类。信号与插槽机制是类型安全的,它以警告的方式报告类型错误,而不会使系统产生崩溃。例如,如果一个退出按钮的clicked()信号被链接到了一个应用的退出函数-插槽quit()。那么一个用户点击退出键俺就的clicke()信号被连接到一个应用的退出函数-插槽quit()。那么一个用户点击退出键将使应用程序终止运行。上述的链接过程用代码写出来就是这样
connect(button,SIGNAL(clicke()),qApp,SLOT(quit()))
我们可以在Qt应用程序的执行过程中增加或是减少信号与插槽的链接。信号与插槽的实现扩展了C++的语法,同时也完全利用了C++面向对象的特征。信号与插槽可以被重载或者重新实现,他们可以定义为类的公有,私有或是保护成员。
信号:当对象的内部状态发生改变,信号就被发射,在某些方面对于对象代理或者所有者也许是很有趣的。只有定义了一个信号的类和他的子类才能发射这个信号。
例如,一个列表框同时发射highlighted()和activated()这两个信号。绝大多数对象也许只对activated这个信号感兴趣,但是有时想知道列表框中的那个条目在当前是高亮的。如果两个不同的类对同一个信号感兴趣,你可以把这个信号和这两个对象链接起来。当一个信号被发射,它所连接的槽会被立即执行,就像一个普通函数调用一样。信号/槽机制完全不依赖与任何一种图形用户界面的事件回路。当所有的槽都返回后emit也就返回。
如果几个槽被链接到一个信号,当信号被发射时,这些槽就会被任意顺序一个接一个地执行。
槽:当一个和槽链接的信号被发射的时候,这个槽被调用。槽也是普通的C++函数并且可以像他们一样被调用:他们唯一的特点就是他们可以被信号链接。槽的参数不能含有默认值,并且和信号一样,为了槽的参数而使用自己特定的类型是很不明智的。
因为槽就是普通成员函数,但却有一点非常有意思的东西,他们也和普通成员函数一样有访问权限。一个槽的访问权限决定了谁可以和它相连:
一个publicslots:包含了任何信号都可以相连的槽。这对于组件编程来说非常有用:你生成了许多对象,它们互相并不知道,把他们的信号与槽链接起来,这样信息就可以正确地传递,并且就像一个铁路模型,把它打开然后让他跑起来。
一个protectedslots:包含了之后这个类和他的子类的信号才能链接的槽。这就是说这些槽只是类的一部分,而不是它和外界的接口
一个privateslots:包含了之后这个类本身的信号可以链接的槽。这就是说它和这个类是非常紧密的,甚至它的子类都没有获得链接权利这样的新人
你也可以把槽定义为虚的,这在实践中被发现也是非常有用的。
connect(object1,signal1,object2,slot1)
connect(object1,signal1,object2,slot2)
object1 object2
signal1----------------------------------------------------- signal1
signal2------------------------------------------- | signal2
slot1 | |----------------slot1
slot2 |---------------------------slot2
信号与槽的机制是非常有效的,但是它不像“真正的”回调那样快。信号与槽稍微有些慢,这是因为他们所提供的灵活性,尽管在实际应用中这些不同可以被忽略。通常,发射一个和槽相连的信号,大约只比直接调用那些非虚函数调用的接受器慢十倍。这是定位链接对象所需的开销,可以安全地重复所有的链接(例如在发射期间检查并发接收器是否被破坏)并且可以按一般的方式安排任何参数。当是个非虚函数调用听起来很多时,举个例子来说,时间开销只不过比任何一个“new”或者"delete"操作要少些。当你执行一个字符串、矢量或者列表操作时,需要“new”或者“delete”,信号和槽仅仅对一个完整函数调用的时间开销中的一个非常小的部分负责。无论何时你在一个槽中使用一个系统调用和间接地调用超过十个函数的时间是相同的。在一台i585-500机器上,你每秒钟可以法神2,000,000个左右连接到一个接收器上的信号,或者发射1,200,000个左右链接到两个接受器的信号。信号和槽机制的简单性和灵活性对于时间的开销来说是非常值得的,你的用户甚至察觉不出来。
更多相关内容 -
Handler实现线程间通信机制的原理
2016-10-31 20:49:33文档详细介绍了Handler实现线程间通信过程中源码的实现机制,并对其中的Looper,MessageQueue,Message对象的初始化及sendMessage()通过sendMessageDelayed()和sendMessageAtTime()过程的操作机流程 -
进程间通信机制有哪些?
2016-08-14 20:09:05进程间的通信方式: 1.管道(pipe)及有名管道(named pipe): 管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。 有名...进程间的通信方式:
1.管道(pipe)及有名管道(named pipe): 管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
2.信号(signal):
信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。 3.消息队列(message queue): 消息队列是消息的链接表,它克服了上两种通信方式中信号量有限的缺点,具有写权限得进程可以按照一定得规则向消息队列中添加新信息;对消息队列有读权限得进程则可以从消息队列中读取信息。 其基本思想是:根据”生产者-消费者”原理,利用内存中公用消息缓冲区实现进程之间的信息交换.
内存中开辟了若干消息缓冲区,用以存放消息.每当一个进程向另一个进程发送消息时,便申请一个消息缓冲区,并把已准备好的消息送到缓冲区,然后把该消息缓冲区插入到接收进程的消息队列中,最后通知接收进程.接收进程收到发送里程发来的通知后,从本进程的消息队列中摘下一消息缓冲区,取出所需的信息,然后把消息缓冲区不定期给系统.系统负责管理公用消息缓冲区以及消息的传递.
一个进程可以给若干个进程发送消息,反之,一个进程可以接收不同进程发来的消息.显然,进程中关于消息队列的操作是临界区.当发送进程正往接收进程的消息队列中添加一条消息时,接收进程不能同时从该消息队列中到出消息:反之也一样.
4.共享内存(shared memory): 可以说这是最有用的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。 这种通信模式需要解决两个问题:第一个问题是怎样提供共享内存;第二个是公共内存的互斥关系则是程序开发人员的责任。
5.信号量(semaphore): 主要作为进程之间及同一种进程的不同线程之间得同步和互斥手段。 6.套接字(socket); 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。 用法:
1 管道
它包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者用于运行于同一台机器上的任意两个进程间的通信。
1.1 无名管道由pipe()函数创建:
#include <unistd.h>
int pipe(int filedis[2]);//
参数filedis返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输入。- //下面的例子示范了如何在父进程和子进程间实现通信。
- #define INPUT 0
#define OUTPUT 1
void main()
{
int file_descriptors[2];
/*定义子进程号 */
pid_t pid;
char buf[256];
int returned_count;
/*创建无名管道*/
pipe(file_descriptors);
/*创建子进程*/
if((pid = fork()) == -1) {
printf("Error in fork\n");
exit(1);
}
/*执行子进程*/
if(pid == 0) {
printf("in the spawned (child) process...\n");
/*子进程向父进程写数据,关闭管道的读端*/
close(file_descriptors[INPUT]);
write(file_descriptors[OUTPUT], "test data", strlen("test data"));
exit(0);
}else{
/*执行父进程*/
printf("in the spawning (parent) process...\n");
/*父进程从管道读取子进程写的数据,关闭管道的写端*/
close(file_descriptors[OUTPUT]);
returned_count = read(file_descriptors[INPUT], buf, sizeof(buf));
printf("%d bytes of data received from spawned process: %s\n",
returned_count, buf);
}
}
1.2 有名管道可由两种方式创建
方式一:mkfifo("myfifo","rw");
方式二:mknod myfifo p
生成了有名管道后,就可以使用一般的文件I/O函数如open、close、read、write等来对它进行操作。
/* 进程一:读有名管道*/
#include <stdio.h>
#include <unistd.h>
void main()
{
FILE * in_file;
int count = 1;
char buf[80];
in_file = fopen("mypipe", "r");
if (in_file == NULL) {
printf("Error in fdopen.\n");
exit(1);
}
while ((count = fread(buf, 1, 80, in_file)) > 0)
printf("received from pipe: %s\n", buf);
fclose(in_file);
}
/* 进程二:写有名管道*/
#include <stdio.h>
#include <unistd.h>
void main()
{
FILE * out_file;
int count = 1;
char buf[80];
out_file = fopen("mypipe", "w");
if (out_file == NULL) {
printf("Error opening pipe.");
exit(1);
}
sprintf(buf,"this is test data for the named pipe example\n");
fwrite(buf, 1, 80, out_file);
fclose(out_file);
}
2 消息队列
消息队列用于运行于同一台机器上的进程间通信,它和管道很相似,是一个在系统内核中用来保存消息的队列,它在系统内核中是以消息链表的形式出现。消息链表中节点的结构用msg声明。
事实上,它是一种正逐渐被淘汰的通信方式,我们可以用流管道或者套接口的方式来取代它,所以,我们对此方式也不再解释,也建议读者忽略这种方式。
3 共享内存
共享内存是运行在同一台机器上的进程间通信最快的方式,因为数据不需要在不同的进程间复制。通常由一个进程创建一块共享内存区,其余进程对这块内存区进行 读写。得到共享内存有两种方式:映射/dev/mem设备和内存映像文件。前一种方式不给系统带来额外的开销,但在现实中并不常用,因为它控制存取的将是 实际的物理内存,
首先要用的函数是shmget,它获得一个共享存储标识符。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int flag);
这个函数有点类似大家熟悉的malloc函数,系统按照请求分配size大小的内存用作共享内存。
当共享内存创建后,其余进程可以调用shmat()将其连接到自身的地址空间中。void *shmat(int shmid, void *addr, int flag);
shmid为shmget函数返回的共享存储标识符,addr和flag参数决定了以什么方式来确定连接的地址,函数的返回值即是该进程数据段所连接的实际地址,进程可以对此进程进行读写操作。使用共享存储来实现进程间通信的注意点是对数据存取的同步,必须确保当一个进程去读取数据时,它所想要的数据已经写好了。通常,信号量被要来实现对共享存 储数据存取的同步,另外,可以通过使用shmctl函数设置共享存储内存的某些标志位如SHM_LOCK、SHM_UNLOCK等来实现。
4 信号量
信号量又称为信号灯,它是用来协调不同进程间的数据对象的,而最主要的应用是前一节的共享内存方式的进程间通信。本质上,信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况。一般说来,为了获得共享资源,进程需要执行下列操作:
(1) 测试控制该资源的信号量。
(2) 若此信号量的值为正,则允许进行使用该资源。进程将信号量减1。
(3) 若此信号量为0,则该资源目前不可用,进程进入睡眠状态,直至信号量值大于0,进程被唤醒,转入步骤(1)。
(4) 当进程不再使用一个信号量控制的资源时,信号量值加1。如果此时有进程正在睡眠等待此信号量,则唤醒此进程。#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int flag);
struct sem {
short sempid;/* pid of last operaton */
ushort semval;/* current value */
ushort semncnt;/* num procs awaiting increase in semval */
ushort semzcnt;/* num procs awaiting semval = 0 */
- }
key是前面讲过的IPC结构的关键字,flag将来决定是创建新的信号量集合,还是引用一个现有的信号量集合。nsems是该集合中的信号量数。如果是创建新 集合(一般在服务器中),则必须指定nsems;如果是引用一个现有的信号量集合(一般在客户机中)则将nsems指定为0。
semctl函数用来对信号量进行操作。
int semctl(int semid, int semnum, int cmd, union semun arg);
不同的操作是通过cmd参数来实现的,在头文件sem.h中定义了7种不同的操作,实际编程时可以参照使用。
semop函数自动执行信号量集合上的操作数组。int semop(int semid, struct sembuf semoparray[], size_t nops);
semoparray是一个指针,它指向一个信号量操作数组。nops规定该数组中操作的数量。
#include <stdio.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
void main()
{
key_t unique_key; /* 定义一个IPC关键字*/
int id;
struct sembuf lock_it;
union semun options;
int i;
unique_key = ftok(".", 'a'); /* 生成关键字,字符'a'是一个随机种子*/
/* 创建一个新的信号量集合*/
id = semget(unique_key, 1, IPC_CREAT | IPC_EXCL | 0666);
printf("semaphore id=%d\n", id);
options.val = 1; /*设置变量值*/
semctl(id, 0, SETVAL, options); /*设置索引0的信号量*/
/*打印出信号量的值*/
i = semctl(id, 0, GETVAL, 0);
printf("value of semaphore at index 0 is %d\n", i);
/*下面重新设置信号量*/
lock_it.sem_num = 0; /*设置哪个信号量*/
lock_it.sem_op = -1; /*定义操作*/
lock_it.sem_flg = IPC_NOWAIT; /*操作方式*/
if (semop(id, &lock_it, 1) == -1) {
printf("can not lock semaphore.\n");
exit(1);
}
i = semctl(id, 0, GETVAL, 0);
printf("value of semaphore at index 0 is %d\n", i);
/*清除信号量*/
semctl(id, 0, IPC_RMID, 0);
}
可以使用系统调用semget()创建一个新的信号量集,或者存取一个已经存在的信号量集:
intsemget(key_t key,int nsems,int semflg);下面是一个打开和创建信号量集的程序:intopen_semaphore_set(key_t keyval,int numsems)
{
intsid;
if(!numsems)
return(-1);
if((sid=semget(mykey,numsems,IPC_CREAT|0660))==-1)
{
return(-1);
}
return(sid);
- }
系统调用:semop();
调用原型:int semop(int semid,struct sembuf*sops,unsign ednsops);返回值:0,如果成功。-1,如果失败:errno=E2BIG(nsops大于最大的ops数目)
系统调用:semctl();
原型:int semctl(int semid,int semnum,int cmd,union semunarg);
返回值:如果成功,则为一个正数。如果失败,则为-1:errno=EACCESS(权限不够)
-
QT进程间通信详细介绍及QProcess机制分析
2015-07-15 15:59:42(1)QT内部对象间通信 在图形用户界面编程中,经常需要将一个窗口部件的变化通知给窗口的其它部件使其产生相应的变化。对于这种内部对象间的通信,QT主要采用了信号和槽的机制。这种机制是QT区别于其他G1、QT通信机制
为了更好的实现QT的信息交互,在QT系统中创建了较为完善的通信机制。QT的通信可分为QT内部通信和外部通信两大类。对于这两类通信机制及应用场合做如以下分析:
(1)QT内部对象间通信
在图形用户界面编程中,经常需要将一个窗口部件的变化通知给窗口的其它部件使其产生相应的变化。对于这种内部对象间的通信,QT主要采用了信号和槽的机制。这种机制是QT区别于其他GUI工具的核心机制。在大部分的GUI工具中,通常为可能触发的每种行为通过定义回调函数来实现。这种回调函数是一个指向函数的指针,在进行函数回调执行时不能保证所传递的函数参数类型的正确性,因此容易造成进程的崩溃。
在 QT 中, 信号 和 槽 的机制取代了这种繁杂的、易崩溃的对象 通信 机制。 信号 是当对象状态改变时所发出的。 槽 是用来接收发射的 信号 并响应相应事件的类的成员函数。信号和槽的连接是通过connect()函数来实现的。例如,实现单击按钮终止应用程序运行的代码 connect(button , SIGNAL(clicked()) , qApp , SLOT(quit()) );实现过程就是一个button被单击后会激发clicked 信号 ,通过connect()函数的连接qApp会接收到此信号并执行槽函数quit()。在此过程中,信号的发出并不关心什么样的对象来接收此信号,也不关心是否有对象来接收此 信号 , 只要对象状态发生改变此信号就会发出。此时槽也并不知晓有什么的信号与自己相联系和是否有信号与自己联系,这样信号和槽就真正的实现了程序代码的封装,提 高了代码的可重用性。同时,信号和槽的连接还实现了类型的安全性,如果类型不匹配,它会以警告的方式报告类型错误,而不会使系统产生崩溃。
(2)QT与外部设备间通信
QT与外部通信主要是将外部发来的消息以事件的方式进行接收处理。外部设备将主要通过socket与QT应用程序进行连接。在此,以输入设备与QT应用程序的通信为例说明QT与外部通信的原理。
在QT的应用程序开始运行时,主程序将通过函数调用来创建并启动qwsServer服务器,然后通过socket建立该服务器与输入硬件设备的连 接。服务器启动后将会打开鼠标与键盘设备,然后将打开的设备文件描述符fd连接到socket上。等到QT应用程序进入主事件循环时,事件处理程序将通过 Linux系统的select函数来检测文件描述符fd的状态变化情况以实现对socket的监听。如果文件描述符fd状态改变,说明设备有数据输入。此 时,事件处理程序将会发出信号使设备输入的数据能及时得到QT应用程序的响应。数据进入服务器内部就会以事件的形式将数据放入事件队列里,等待QT客户应 用程序接收处理。处理结束后再将事件放入请求队列里,通过服务器将事件发送到相应硬件上,完成外部输入设备与QT应用程序的整个通信过程。
2、 QProcess机制分析
QProcess类通常是被用来启动外部程序,并与它们进行通信的。QProcess是把外部进程看成是一个有序的I/O设备,因此可通过 write()函数实现对进程标准输入的写操作,通过read(),readLine()和getChar()函数实现对标准输出的读操作。
(1) QProcess通信机制
QT可以通过QProcess类实现前端程序对外部应用程序的调用。这个过程的实现首先是将前端运行的程序看成是QT的主进程,然后再通过创建主进 程的子进程来调用外部的应用程序。这样QProcess的通信机制就抽象为父子进程之间的通信机制。QProcess在实现父子进程间的通信过程中是运用 Linux系统的无名管道来实现的,因此为了能更加清楚的说明QProcess的通信机制,在此首先介绍关于无名管道实现父子进程间的通信机制。
无名管道是一种只能够在同族父子之间通信,并且在通信过程中,只能从固定的一端写,从另一端读的单向的通信方式。该无名管道是通过调用pipe()函数而创建的。创建代码如下:
-
#include
<unistd.h> int pipe(int fd[2]) ; 返回:若成功则为0,若出错则为-1
创建后经参数fd返回两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。经过fork()函数创建其子进程后,子进程将拥有与父进程相 同的两个文件描述符。如果想要实现父进程向子进程的通信则关闭父进程的读端fd[0],同时关闭子进程的写端fd[1]。这样就建立了从父进程到子进程的 通信连接。
由于无名管道的单向通信性,所以如果要应用无名管道实现父子进程之间的双向通信则至少需要应用双管道进行通信。QProcess类的通信原理就是利 用多管道实现了父子进程之间的通信。然而对于外部运行的应用程序大都是通过标准输入而读得信息,通过标准输出而发送出信息,因此只通过建立管道并不能完成 内外进程?之间的通信。要解决此问题,就如该模块开始时所说,QProcess是把外部进程看成是一个I/O设备,然后通过对I/O设备的读写来完成内外 进程的通信。
在QProcess中父子进程之间是通过管道连接的,要实现子进程能从标准输入中读得父进程对管道的写操作,同时父进程能从管道中读得子进程对标准 输出或标准容错的写操作,就要在子进程中将管道的读端描述符复制给标准输入端,将另外管道的写端描述符复制给标准输出端和标准容错端,即实现管道端口地址 的重定向。这样子进程对标准输入、标准输出及标准容错的操作就反应到了管道中。
QProcess在正常渠道模式下具体实现共用了五个无名管道进行通信。五个管道的描述符分别用 childpipe[2],stdinChannelpipe[2],stdoutChannelpipe[2],stderrChannelpipe[2] 和deathpipe[2]五个数组来保存。deathpipe指代的管道会用在消亡的子进程与父进程之间。当子进程准备撤销时会发送一个表示该子进程消 亡的字符给父进程来等待父进程进行处理。stdinChannelpipe,stdoutChannelpipe和stderrChannelpipe所 指代的管道分别与标准输入,标准输出和标准容错进行绑定,实现了与外部程序的通信。childpipe指代的管道主要是为父子进程之间的通信而建立的。
如果在管道中有新数据写入,就会通知相应进程去读。另外图2是QProcess在正常渠道模式下的通信原理图,如果是在融合渠道模式下,将没有容错 管道,此时原理图中将没有第一个管道,也就不会有管道描述符。同时,标准容错端和标准输出端将共同挂接到子进程的stdoutChannelpipe的写 端,来实现内外进程的通信。
(2) QProcess应用方式
由于QProcess类实现了对底层通信方式较为完善的封装,因此利用QProcess类将更为方便的实现对外部应用程序的调用。在此,通过在QT界面中调用外部mplayer的例子来简单说明QProcess的应用方式。
-
const
QString mplayerPath("/mnt/yaffs/mplayer");
-
const
QString musicFile("/mnt/yaffs/music/sound.mp3");
-
QProcess*
mplayerProcess=new QProcess();
QStringList args;
-
args<<"-slave";
-
args<<"-quiet";
-
args
<< "-wid";
-
-
args
<< "-af volume=10"
-
args
-
args<<musicFile;
- mplayerProcess->setProcessChannelMode(QProcess::MergedChannels);
- mplayerProcess
->start(mplayerPath,args);
- 第一行指明了所要调用的外部应用程序mplayer的位置。
- 第二行指明了所要播放的声音文件及目录路径。
- 第三行创建一个指向类
QProcess的指针。
- 第四到第九行指定mplayer参数,具体参数可以查看
maplayer参数介绍
。
- -slave参数表示打开slave模式. 这用来将MPlayer作为其它程序的后端. MPlayer将从他的标准输入读取简单命令行, 而不再截获键盘事件. SLAVE模式协议部分将解释其语法。
- -quiet显示较少的输出和状态信息
。
-
- -wid可以为mplayer指定输出窗口。
-
第十一行为启动外部应用程序mplayer。内核中管道及通信环境的建立都是在此步中完成的。
mplayer在slave模式下运行会自动从标准输入中读取信息并执行。由QProcess的通信原理可知,管道的读端描述符 stdinChannelpipe[0]复制给了标准输入,即标准输入的描述符也为stdinChannelpipe[0],因此按照标准输入的描述符去 读信息就是到stdinChannelpipe所对应的管道中读取信息。所以如果想在QT的主进程中发送命令使mplayer退出,只需在主程序中向 stdinChannelpipe[1]端写入命令quit就可以,执行语句为myProcess->write(”quit ”);(此处的 write()函数为QProcess类的成员函数,具体实现就是向stdinChannelpipe[1]端写入信息)
(3)QProcess的发展及分析
QProcess类伴随着QT/Embedded的发展逐渐趋于完善。在QTE2及其更前版本中还没有QProcess类,如果想实现与外部应用程 序的通信,必须要自己实现对管道或socket的建立与重定向。到了QTE3版本,就实现了对QProcess类的封装。在QTE3的版本 中,QProcess类的实现是通过应用socket来建立主进程与外部应用程序之间通信的。通信原理与图3所示基本相同,只是将图中的管道描述符改为是 socket的描述符即可。QT主程序在建立成对socket描述符时需要调用Linux系统函数socketpair()。在生成的成对socket描 述符之间可以实现父子进程之间的双向通信,即无论是socket的0套接口还是1套接口都可进行读写。
但为了避免出现通信过程中父子进程对同一个socket的争夺,例如,在子进程还未将父进程发送的信息全部读出时,子进程又要求将自己产生的数据返 回给父进程。如果父子进程双向通信只用一个socket来完成,就会出现父子进程发送的信息混乱情况。因此,对于QProcess的实现仍然必须通过多个 socket来共同完成。
由上面的描述可知,尽管socket有双向通信功能,但在实现QProcess过程中只是利用socket实现了单向通信功能。因此既浪费了对资源 的利用又增加了系统的开销。为了解决此问题,QTE4版本将QProcess的通信连接方式由socket改为了只能实现单向通信的无名管道来实现。通信 原理就是以上3.1 QProcess通信机制中所描述的。
3、其它通信方式
除了上面介绍的无名管道和socket通信方式外,一般操作系统中常用的进程间通信机制也都可以用于QT系统内部不同进程之间的通信,如消息队列、共享内存、信号量、有名管道等机制。其中信号量机制在QT中已经重新进行了封装;有些机制则可以直接通过操作系统的系统调用来实现。另外,如果我们只是想通过管道或socket来实现较简单的外部通信,也可以重新创建管道或socket来实现自己要求的功能。例如,还是在QT主程序中调用外部mplayer。如果我们只是想在QT主程序中控制mplayer,而不要求得到mplayer输出的信息。则可以按照以下方式来实现:
-
const char* mplayerPath = "/mnt/yaffs/mplayer";
const char* musicFile = "/mnt/yaffs/music/sound.mp3";
const char* arg[5];
arg[0] = mplayerPath;
arg[1] = "-slave";
arg[2] = "-quiet";
-
arg[3]
= musicFile;
-
arg[4]
= NULL;
-
int
fd[2],pid;
if(pipe(fd)<0)
printf("creating pipe is error ");
else while((pid=fork())<0);
if(pid==0)
{
::close(fd[1]);
::dup2(fd[0],STDIN_FILENO);
-
execvp(arg[0],(const*
char*)arg);
}
else
- {
::close(fd[0]);
- }
第1到8行与前面QProcess类实现调用mplayer一样,是用来指明mplayer运行时参数的。第10行是创建一个管道。第12行是创建一个子进程。15,20行是关闭父子进程中没用的管道描述符。此时可结合图2.1和图2.2来理解从父进程到子进程通信环境的建立。第16行是把子进程的读端与标准输入绑定,以便mplayer能够接收到父进程发出的命令。17行就是从子进程中调用外部mplayer的实现。此时,程序执行后,mplayer就可以运行起来。如果想在QT主程序中通过发送命令使mplayer退出,就在管道的写端写入命令"quit"就可以。实现语句为write(fd[1], "quit",strlen("quit"));
该例子说明了QT通信方式运用的灵活性,可以根据实际情况进行应用。同时该例子的实现方式正是利用了QProcess类实现的机制,因此可以结合这个例子更加深刻的理解QProcess类的实现机制。
- -quiet显示较少的输出和状态信息
。
- -slave参数表示打开slave模式. 这用来将MPlayer作为其它程序的后端. MPlayer将从他的标准输入读取简单命令行, 而不再截获键盘事件. SLAVE模式协议部分将解释其语法。
- 第四到第九行指定mplayer参数,具体参数可以查看
maplayer参数介绍
。
- 第三行创建一个指向类
QProcess的指针。
- 第二行指明了所要播放的声音文件及目录路径。
- 第一行指明了所要调用的外部应用程序mplayer的位置。
- mplayerProcess
->start(mplayerPath,args);
-
#include
-
利用Linux的共享内存通信机制实现两个进程间的通信
2019-08-05 21:09:404)利用Linux的共享内存通信机制实现两个进程间的通信 编写程序sender,它创建一个共享内存,然后等待用户通过终端输入一串字符,并将这串字符通过共享内存发送给receiver;最后,它等待receiver的应答,收到应答...0. 相关博客
实现一个模拟的shell_ 一只博客-CSDN博客_操作系统实验模拟shell
https://blog.csdn.net/qq_42276781/article/details/98521603实现一个管道通信程序_ 一只博客-CSDN博客_实现一个管道通信程序
https://blog.csdn.net/qq_42276781/article/details/98523996利用Linux的消息队列通信机制实现两个线程间的通信_ 一只博客-CSDN博客_杭电操作系统实验三
https://blog.csdn.net/qq_42276781/article/details/90672038
1. 利用Linux的共享内存通信机制实现两个进程间的通信
编写程序sender,它创建一个共享内存,然后等待用户通过终端输入一串字符,并将这串字符通过共享内存发送给receiver;最后,它等待receiver的应答,收到应答消息后,将接收到的应答信息显示在终端屏幕上,删除共享内存,结束程序的运行。编写receiver程序,它通过共享内存接收来自sender的消息,将消息显示在终端屏幕上,然后再通过该共享内存向sender发送一个应答消息“over”(老师告知可以省略这步操作),结束程序的运行。选择合适的信号量机制实现两个进程对共享内存的互斥及同步使用。
2. 原代码
无bug,可正常运行
3. 图片识别结果
有bug,无法直接运行,调试改bug过程中可以加深代码理解
test4.c
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<semaphore.h> #include<fcntl.h> #include<sys/shm.h> sem_t *w;//定义写的信号量 int main(){ pid_t pid; int shm_id,x; char *shm_addr; char *name="writer"; char buff [256]; //初始化有名信号量 w=sem_open(name,O_CREAT,0666,1); //以下两行代码用以确保Write的初值为1,因为笔者在调试代码期间,有时Write的初值不为1 //如有需要可以使用这两行代码 /*sem_getvalue(w,&x); if(x==0) sem_post(w);*/ shm_id=shmget(IPC_PRIVATE,256,0666); if(shm_id<0){ perror("shmget error"); exit(1); } pid=fork();//创建子进程 if(pid<0){//创建失败 perror("fork error"); exit(1); } else if(pid==0){//当前运行子进程 while(1){ sem_wait(w);//申请写的权限 //把共享内存区对象映射到调用进程的地址空间 //函数原型void *shmat(int shmid, const void *shmaddr, int shmflg) //具体参数含义参见https://baike.baidu.com/item/shmat shm_addr=shmat(shm_id,0,0); if(shm_addr==(void *)-1){//映射失败 perror("child shmat error"); sem_post(w); exit(1); } strcpy(buff,shm_addr);//将共享内存中的信息拷贝至buff printf("child receive:%s",buff ); //断开共享内存连接 //函数原型int shmdt(const void *shmaddr) //具体参数含义参见https://baike.baidu.com/item/shmat if(shmdt(shm_addr)<0){ perror("child shmdt");//断开连接失败,返回提示 sem_post(w);//释放写的权限 exit(1); } sem_post(w); sleep(2); } } else while(1){//当前运行父进程 sem_wait(w); printf("parent send:"); shm_addr=shmat(shm_id,(void *)0,0); if(shm_addr==(void *)-1){ perror("parent shmat error"); sem_post(w); exit(1); } fgets(buff,256,stdin);//读取键盘的一行输入到buff if(!strncmp(buff,"exit",4)){ sem_post(w); if (shmctl(shm_id,IPC_RMID,NULL)==-1){ perror("shmctl :IPC_ RMID"); sem_post(w); exit(1); } else{ printf("-------end--------\n"); sem_post(w); exit(0); } } else{ //将buff写入共享内存 strncpy(shm_addr,buff,strlen(buff)); //断开共享内存的连接 if(shmdt(shm_addr)<0){ perror("parent shmdt"); sem_post(w); exit(1); } sem_post(w); sleep(3); } } return 0; }
4. 代码及实验报告获取
关注公众号,回复“进程管理”即可获取代码和实验报告
-
操作系统之进程通信:高级通信机制四大类
2021-08-25 21:02:39目前,高级通信机制可归结为四大类:共享存储器系统、管道通信系统、消息传递系统以及客户机-服务器系统。 共享存储器系统 在共享存储器系统中,相互通信的进程共享某些数据结构或共享存储区,进程间能够通过这些... -
Binder进程间通信机制(图文解析)
2017-12-19 01:33:16前言本来想洋洋洒洒写一篇形象生动的Binder原理的文章,再配上我这个灵魂画手的图画,但是再经过了1个多星期的学习后,我决定了,我自首,我这太年少了 我,太懵懂了,我没成想这Binder机制这么厉害!作为一名... -
Java多线程编程-(4)-线程间通信机制的介绍与使用
2017-10-10 18:25:52上一篇:Java多线程编程-(1)-线程安全和锁Synchronized概念Java多线程编程-(2)-可重入锁以及Synchronized的其他基本特性Java多线程编程-(3)-线程本地ThreadLocal的介绍与使用线程间通信简介我们知道线程是操作... -
Android多线程通信机制
2018-04-10 11:32:40在Android中我们把UI线程外的线程成为工作线程。我们不能再主线程中做...Androiod提供了两种线程间通信方式:一种是AsyncTask机制,另一种是Handler机制。 1.线程间通信方式之AsyncTask机制: AsyncTask异步任务... -
Android消息机制 & Android线程间通信机制
2018-03-06 14:38:05前言:由于Android系统本身决定了其自身的单线程模型结构。在日常的开发过程中,我们又不能把所有的工作都交给主线程去处理...接下来,我们从Android线程间通信机制和Android消息机制两个方面对以上内容进行介绍。 ... -
核间通信机制分析
2018-11-20 09:31:00核间通信的主要目标是:充分利用硬件提供的机制,实现高效的CORE间通信;给需要CORE间通信的应用程序提供简洁高效的编程接口。 根据所使用的硬件特性,核间通信可能的实现机制有: 1) Mailbox中断; 2) 基于共享... -
进程间通信机制
2016-08-31 21:13:25Win32 API允许多个进程访问同一文件映射对象,各个进程在它自己的地址空间里接收内存的指针。通过使用这些指针,不同进程就可以读或修改文件的内容,实现了对文件中数据的共享。 应用程序有三种方 -
对象之间的调用机制 通信方式
2015-10-18 16:51:261.直接方法调用 2.Target-Action @selector() ...1.判断通信对象是一对一的还是一对多的(一般一对一的是1,2,5,一对多是3,4,6) 2.对象之间的耦合度,是强耦合还是松耦合(尽量让所有模块解 -
进程间通信和线程间通信
2020-08-18 20:26:22线程间通信 进程和线程的区别 程序只是一组指令的有序集合,它本身没有任何运行的含义,它只是一个静态的实体。而进程则不同,它是程序在某个数据集上的执行。进程是一个动态的实体,它有自己的生命周期。它因... -
一篇文章带你了解kubernetes各组件间的通信机制
2019-09-12 18:45:31因此,在了解各组件通信前,必须先了解list-watch机制在kubernetes的应用。 List-watch是k8s统一的异步消息处理机制,list通过调用资源的list API罗列资源,基于HTTP短链接实现;watch则是调用资源的watch API... -
linux基础——linux进程间通信(IPC)机制总结
2016-07-23 21:33:12在linux下的多个进程间的通信机制叫做IPC(Inter-Process Communication),它是多个进程之间相互沟通的一种方法。在linux下有多种进程间通信的方法:半双工管道、命名管道、消息队列、信号、信号量、共享内存、内存... -
进程间通信(IPC机制)精炼详解
2018-04-09 16:45:28IPC不是Android所独有的,任何一个操作系统都需要有相应的IPC机制,比如Windows上可以通过剪贴板、管道和邮槽等来进行进程间通信,而Linux上可以通过命名共享内容、信号量等来进行线程间通信。对于Android来说,... -
pixhawk通信机制
2016-08-10 16:02:11其实uORB在概念上等同于posix里面的命名管道(named pipe),它本质上是一种进程间通信机制。由于PX4实际使用的是NuttX实时ARM系统,因此uORB实际上相当于是多个进程(驱动级模块)打开同一个设备文件,多个进程(驱动... -
深入理解(15)java多线程之Lock对象实现同步以及线程间通信
2019-05-01 22:45:15Lock对象简介 这里为什么说Lock对象哪?Lock其实是一个接口,在JDK...我们知道synchronized关键字可以实现线程间的同步互斥,从JDK1.5开始新增的ReentrantLock类能够达到同样的效果,并且在此基础上还扩展了很多实... -
linux进程及进程间同步通信机制
2019-05-19 20:00:49套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。 (1) 信号 ---- 信号机制是UNIX为进程中断处理而设置的。它只是一组预定义的值,因此不能用于信息交换,仅用于进程中断... -
C++回调函数作为通信机制
2019-05-18 17:05:17类之间的通信, 不同的平台有各自的解决方案, 比如windows有MFC, 这里面就包含大量的回调函数机制, 保证类之间的通信. 这里, 实现一个自定义的回调函数机制, 进行基础的通信. 问题背景: 有一个服务器的类, 服务器有... -
Android8.0.0-r4的Binder进程间通信机制
2018-03-23 07:59:47Android8.0.0-r4的Binder进程间通信机制 Binder是Android系统中进程间通讯(IPC)的一种方式,也是Android系统中最重要的特性之一。Android中的四大组件Activity,Service,Broadcast,ContentProvider,不同的App... -
Android进程间和线程间通信方式
2021-01-17 13:22:41客户端和服务端建立连接之后即可不断传输数据,比较适合实时的数据传输 二、Android线程间通信方式 一般说线程间通信主要是指主线程(也叫UI线程)和子线程之间的通信,主要有以下两种方式: 1.AsyncTask机制 ... -
Android开发知识(四)Android进程间Binder通信机制的源码分析(下)
2017-07-26 17:51:33虽然Android系统是基于Linux内核,但是 它的进程间通信方式并没有完全继承自Linux,它拥有自己独特的通信方式–Binder。通过Binder我们可以进行不同应用与进程之间的相互通信以及远程方法调用。 -
【Android】进程间通信——Binder
2022-04-10 09:54:33内存机制:mmap 虚拟进程地址空间(vm_area_struct)和虚拟内核地址空间(vm_struct)都映射到同一块物理内存空间。当Client端与Server端发送数据时,Client(作为数据发送端)先从自己的进程空间把IPC通信数据copy_from... -
Android开发知识(三)Android进程间Binder通信机制的源码分析(上)
2017-07-24 16:29:23AIDL,全称名为:Android Interface Definition Language...它是安卓中一种跨进程通信的实现方式,使得不同进程不同应用之间可以保持通信。 本篇内容为基础使用篇,下面将写一个例子,来实现不同应用进程之间的通信。 -
Linux进程间通信-线程间通信
2021-05-08 23:29:12Linux作为一种新兴的操作系统,几乎支持所有的Unix下常用的进程间通信方法:管道、消息队列、共享内存、信号量、套接口。1、管道管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘... -
进程间通信机制(管道、信号、共享内存/信号量/消息队列)、线程间通信机制(互斥锁、条件变量、posix匿名...
2013-09-16 19:43:24在Linux 中,管道是一种使用非常频繁的通信机制。从本质上说,管道也是一种文件,但它又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题,具体表现如下所述。 • 限制管道的大小。实际上,管道是一... -
最高效的进(线)程间通信机制--eventfd
2016-12-08 00:03:45我们常用的进程(线程)间通信机制有管道,信号,消息队列,信号量,共享内存,socket等等,其中主要作为进程(线程)间通知/等待的有管道pipe和socketpair。线程还有特别的condition。 今天来看一个liunx较新的系统... -
进程间通信的机制有哪些
2012-09-03 15:16:14进程间通信机制 1 文件映射 文件映射(Memory-Mapped Files)能使进程把文件内容当作进程地址区间一块内存那样来对待。因此,进程不必使用文件I/O操作,只需简单的指针操作就可读取和修改文件的内容。 ...