Qt进程
这篇博客主要关于在Qt中使用进程的相关内容,包含有Qt自带的class类QProcess和使用C语言fork出来的进程,两种创建进程的方式。
QProcess
这里主要包含使用QProcess的启动方式以及同步进程相关的API
启动外部程序分为两种方式:
- 阻塞式 execute()
QProcess *pCaller = new QProcess();
// 阻塞调用 当前窗口处理结束关闭之后 主窗口才能操作
pCaller->execute("/home/gsk/Qt/build-Demo-child1-Desktop_Qt_5_12_3_GCC_64bit-Debug/Demo-child1");
- 非阻塞式 start() 和 startDetached()
QProcess *pCaller = new QProcess();
// 非阻塞调用 当前窗口的执行状态不影响主窗口的操作
pCaller->startDetached("/home/gsk/Qt/build-Demo-child1-Desktop_Qt_5_12_3_GCC_64bit-Debug/Demo-child1", QStringList());
pCaller->start("/home/gsk/Qt/build-Demo-child1-Desktop_Qt_5_12_3_GCC_64bit-Debug/Demo-child1");
非阻塞式调用分为两种:
(1)一体式: start()
void QProcess::start(const QString & program, const QStringList & arguments, OpenMode mode = ReadWrite)
外部程序启动后,将随主程序的退出而退出。
(2)分离式:startDetached
void QProcess::startDetached(const QString & program, const QStringList & arguments, const QString & workingDirectory = QString(), qint64 * pid = 0
外部程序启动后,当主程序退出时并不退出,而是继续运行。
这里需要注意在获取processId进程ID的时候,由于在非阻塞式分离式启动线程的方法中,有一个参数默认设置为0,因此,获取该方法产生的进程ID是均是0.
Synchronous Process API (同步进程API)
QProcess提供了一系列的函数以提到事件循环来完成同步操作:
(1)waitForStarted(): 阻塞,直到外部程序启动
(2)waitForReadyRead(): 阻塞,直到输出通道中的新数据可读
(3)waitForBytesWritten() : 阻塞,直到输入通道中的数据被写入
(4)waitForFinished(): 阻塞,直到外部程序结束果在主线程(QApplication::exec())中调用这些函数,可能会造成当前用户界面不响应。
实例程序
// Qt进程调用可执行程序
void MainWindow::on_pBtn_Process_clicked()
{
QProcess *pCaller = new QProcess();
// 阻塞调用 当前窗口处理结束关闭之后 主窗口才能操作
// pCaller->execute("/home/gsk/Qt/build-Demo-child1-Desktop_Qt_5_12_3_GCC_64bit-Debug/Demo-child1");
// 非阻塞调用 当前窗口的执行状态不影 影响主窗口的操作
pCaller->start("/home/gsk/Qt/build-Demo-child1-Desktop_Qt_5_12_3_GCC_64bit-Debug/Demo-child1");
// startDetached
// pCaller->startDetached("/home/gsk/Qt/build-Demo-child1-Desktop_Qt_5_12_3_GCC_64bit-Debug/Demo-child1", QStringList());
// printf("pCaller->processId() = %d\n", getpid());
llPid[iPidCnt++] = pCaller->processId();
}
fork子进程
fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。点击这里有更详细关于fork()函数的解释。
实例程序
// C进程 调用可执行程序
int MainWindow::set_cpu(int iCpuID)
{
cpu_set_t stMask;
CPU_ZERO(&stMask);
CPU_SET(iCpuID, &stMask);
return sched_setaffinity(0, sizeof(cpu_set_t), &stMask);
}
void MainWindow::on_pBtn_OK_clicked()
{
pid_t stPid;
stPid = fork();
set_cpu(0);
if(stPid < 0){
printf("创建子进程失败!\n");
return ;
}
if(stPid == 0){
set_cpu(1);
int iErr = execl("/home/gsk/Qt/build-Demo-child1-Desktop_Qt_5_12_3_GCC_64bit-Debug/Demo-child1", "/home/gsk/Qt/build-Demo-child1-Desktop_Qt_5_12_3_GCC_64bit-Debug/Demo-child1", NULL);
printf("%d\n", iErr);
}
}
获取进程的ID
在QProcess中,通过Qt中的API
processId()
可以获取当前进程的ID(需注意,对于start()
开启的进程,可以通过该API获取进程ID,但对于startDetached()
开启的进程,获取的进程ID是0,关闭该进程,就会关闭系统);在fork产生的进程中可以使用getpid()
获取进程ID使用getppid()
获取该进程父进程的ID。
关于进程0和进程1:
- 所有进程的祖先叫做进程0,idle进程或者swapper进程,它是在Linux的初始化阶段从无到有的创建的内核进程(该进程描述符使用的是静态分配的数据结构,其他进程都是动态分配的)
- 进程0调用start_kernel()函数初始化内核需要的所有数据结构,激活中断,创建一个进程1的内核进程(init进程),进程0和进程1 共享每进程的所有内核数据结构。
- 多处理器中,每个CPU都有自己的进程0。进程0创建的内核进程执行init()函数初始化内核。然后调用execve()系统调用装入可执行程序init.至此,init为一个普通的进程,且拥有每进程内核数据结构。在系统关闭之前,init一直存在,它创建和监控在操作系统外层的执行的所有进程的活动。