从QProcess说开来(二)

本文详细解析了QProcess作为QIODevice派生类的实现细节,包括如何通过QIODevice的常规方法(如open、read、write、close)进行进程间的输入输出操作。同时,介绍了Unix和Windows系统下启动进程的不同API函数(如fork、execve、CreateProcess)及其与QProcess::startDetached的关系。文章最后讨论了进程终止的相关函数(kill、terminate)及其在QProcess中的应用。

延续前面从QProcess说开来(一)的名字,换个角度继续学习。

QIODevice派生类

QProcess作为QIODevice的派生类,实现角度上看,它必须要重新实现下面两个成员函数:

  • readData()
  • writeData()

而后,按照QIODevice的常规用法,我们

  • 调用QIODevice::open()打开设备
  • 使用QIODevice::read()/QIODevice::write()读写
  • 使用QIODevice::close()关闭

实际上,我们的常规用法是:

常用代码

其调用父类成员

QProcess::start()

QIODevice::open()

QProcess::readAllStandardError()

QIODevice::readAll()

QProcess::readAllStandardOutput()

QProcess::write()

QIODevice::write()

QProcess::close()

QIODevice::close()

除此以外,QProcess还有静态成员函数可用:

  • QProcess::execute() 启动一个进程,然后等待该进程结束。
  • QProcess::startDetached() 启动一个进程,然后使其和当前进程脱离进程的父子关系。

不过:这两种用法应该和QIODevice的提供的功能关系不大了。

启动

我只有ubuntu和windows系统,下面的内容也就不会超出这两个系统的范围。 dbzhang800 20110116

Unix

系统的2个api函数与此有关:

fork()

用来创建新进程

linux下的api函数clone()有类似功能,似乎主要用于构建线程

execve()

用来执行新程序

它还有5个马甲(库函数)
execl() execv()
execlp() execvp()
execle()

这个execve()对我们使用QProcess应该比较重要,因为我们启动参数都是要传递给它的:

int execve(const char *filename, char *const argv[], char *const envp[]);

filename

如果包含“/”,则视为路径名;否则按照PATH环境变量指定的目录进行搜索。filename可以是解释器文件(首行是#!/usr/bin/python这种)

argv

命令行参数列表

envp

环境变量列表。比如:有时可能会考虑像Windows那样将当前路径也加入PATH环境变量

而 QProcess::startDetached() 在Unix下创建的是一个孤儿进程:

  • 当前进程P下 fork() 得到子进程 C1
  • C1下 fork() 得到 子子进程 CC1
  • C1自愿结束,CC1调用execve执行外部程序且与进程P脱离关系

在Unix下,还有system()、popen()等库函数也可以用来启动外部程序。

Windows

和Unix下完全不同,Windows进程创建的api函数是一个带有众多参数的CreateProcess

BOOL CreateProcess(
  PCTSTR pszApplicationName,
  PTSTR pszCommandLine,
  PSECURITY_ATTRIBUTES psaProcess,
  PSECURITY_ATTRIBUTES psaThread,
  BOOL bInheritHandles,
  DWORD fdwCreate,
  PVOID pvEnvironment,
  PCTSTR pszCurDir,
  PSTARTUPINFO psiStartInfo,
  PPROCESS_INFORMATION ppiProcInfo);

看着就头大,看个小例子吧:

STARTUPINFO si = {sizeof(si)};
PROCESS_INFORMATION pi;
TCHAR commandLine[] = TEXT("notepad.exe");

CreateProcess(NULL, commandLine, NULL, NULL,
FALSE, 0, NULL, NULL, &si, &pi);

只看一个单纯和启动有关的(即和前面的fork/execve参数对应的):

  • PCTSTRpszApplicationName

按照Windows核心编程一书中说法,99%的情况下,我们都是直接传递一个NULL给它。它存在的原本目的是为了支持Windows的posix子系统。

如果指定了这个参数:程序名后缀必须指定;而且不在当前工作路径下的话,必须指定全路径。

注:Qt在WindowCE下的实现在使用这个参数。

  • PTSTRpszCommandLine

如果前一个参数为NULL,则该字符串参数中第一个空格(被双引号括住的不算)前的内容则为应该程序的程序名。

如果程序名不包含路径,则按照下列顺序查找:

  • 当前进程的程序文件的所在目录
  • 当前进程的工作目录
  • Windows的system32目录
  • Windows目录
  • 环境变量PATH中的目录

注意,这是一个字符串。而在unix下,是一个字符串的列表。所在当参数中包含空格等东西时,在Windows下总是需要特殊处理。

  • PVOIDpvEnvironment

环境变量

  • PCTSTRpszCurDir

指定工作目录,这个前面没提到。因为在unix下,分成fork()/execve()两个阶段,在fork()之后直接调用chdir设置进程的工作目录即可。

同样,看一下 QProcess::startDetached()的情况:

BOOLbInheritHandles

QProcess::startDetached()

FALSE

父进程CreateProcess后立即调用CloseHandle关闭持有的子进程的线程和进程句柄

QProcess::start()

TRUE

进程终止

看完启动,简单看看终止(termination)。

在C1X和C++11标准之前,C/C++标准中没有线程相关的东西。于是和进程终止相关的函数比较简单:

  • main 函数中的 return
  • exit()
  • _Exit()
  • abort()

接下来也不考虑多线程的情况(因为我理不太清)。这样一来,只用简单考虑:

  • 进程自愿结束
  • 其他进程要求该进程结束

自愿结束的我们一般不关心,所以只能后一个。在QProcess中,则对应:

  • QProcess::kill()
  • QProcess::terminate()
  • QProcess::close()

其中 QProcess::close() 调用 QProcess::kill()

unix

在unix下,这是通过kill发送信号实现的

int kill(pid_t pid, int sig);

QProcess::kill()

发送 ::kill(pid, SIGKILL)

QProcess::terminate()

发送 ::kill(pid, SIGTERM)

进程结束后,会向其父进程发送 SIGCHLD 信号。父进程调用wait函数来获取进程该状态

pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);

windows

Windows下进程终止的api函数是

BOOL TerminateProcess(HANDLE hProcess, UINT  fuExitCode);

QProcess::kill()调用是该函数。而QProcess::terminate()采用的却是另一个东西

  • 采用 EnumWindows() 枚举进程内各个顶级窗口

  • 对各个窗口使用 PostMessage() 发送 WM_CLOSE 消息

  • 使用 PostThreadMessage() 对其主线程发送 WM_CLOSE 消息


<think> 我们正在讨论QProcess的finished信号的参数明。根据引用内容,finished信号在子进程结束时发出,并带有两个参数:退出码(exitCode)和退出状态(exitStatus)。 引用[1]指出:void QProcess::finished(int exitCode, QProcess::ExitStatus exitStatus) 引用[2]明:finish()信号(即finished信号)将进程的退出代码和退出状态作为参数提供。 引用[3]和引用[4]进一步解释了这两个参数的含义,并且提到如何使用connect来连接这个信号。 因此,我们需要详细明这两个参数: 1. exitCode:整数类型,表示进程的退出代码。在正常退出时,这个值由进程的main函数返回或通过exit()函数设置。如果进程崩溃或异常终止,则这个值可能没有意义(此时应关注exitStatus)。 2. exitStatus:枚举类型QProcess::ExitStatus,表示进程的退出状态。它有两个可能的值: - QProcess::NormalExit(值为0):表示进程正常退出,exitCode有效。 - QProcess::CrashExit(值为1):表示进程崩溃(例如由于信号终止),此时exitCode通常是非零或者无效的。 此外,引用[4]还提到,当进程完成后,QProcess的缓冲区仍然是完整的,可以读取数据。 我们还可以明如何使用这个信号: 由于finished信号被重载了,因此在连接时需要使用特定的语法来指定参数类型。例如,使用静态转换来指定连接的是带有两个参数(int和ExitStatus)的版本。 下面我们整理一个回答: </think> ### QProcess::finished信号的参数明 #### 一、信号原型明 `QProcess::finished`信号在子进程结束时触发,其完整函数签名如下: ```cpp void QProcess::finished(int exitCode, QProcess::ExitStatus exitStatus); ``` 该信号包含两个关键参数:[^1][^2] --- #### 、参数详细解析 1. **`exitCode`(退出码)** - **类型**:`int` - **含义**: - 表示子进程的退出状态码 - 正常退出时,对应进程`main()`函数的返回值或`exit()`的调用参数 - 程序崩溃或异常终止时,此值可能无效(需结合`exitStatus`判断) - **示例**: - `return 0;` → `exitCode=0` - `exit(255);` → `exitCode=255` 2. **`exitStatus`(退出状态)** - **类型**:`QProcess::ExitStatus`枚举 - **枚举值**: | 值 | 常量 | 含义 | |---|---|---| | **0** | `QProcess::NormalExit` | 进程正常退出,`exitCode`有效 | | **1** | `QProcess::CrashExit` | 进程崩溃(如段错误、被强制终止),`exitCode`通常无效 | - **判断逻辑**: ```cpp if (exitStatus == QProcess::NormalExit) { qDebug() << "正常退出,代码:" << exitCode; } else { qDebug() << "进程崩溃!"; } ``` --- #### 三、关键行为特性 1. **信号触发时机** - 子进程结束**后**立即触发(无论正常退出或崩溃)[^2] - 此时`QProcess`状态变为`NotRunning` 2. **数据缓冲区完整性** 信号触发时,进程的**输出缓冲区保持完整**,仍可读取数据(即使进程已退出)[^4] 3. **重载处理** 由于存在多个重载版本,连接信号时需显式指定参数类型: ```cpp connect(process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), [=](int code, QProcess::ExitStatus status) { // 处理逻辑 } ); ``` [^3][^4] --- #### 四、典型应用场景 ```cpp QProcess *proc = new QProcess; proc->start("myapp"); // 连接finished信号 connect(proc, &QProcess::finished, [=](int code, QProcess::ExitStatus status){ if (status == QProcess::CrashExit) { qCritical() << "进程崩溃! 重启中..."; proc->start(); // 自动重启 } else { qInfo() << "退出码:" << code; } }); ``` 此例实现了进程崩溃时的自动重启机制,展示了参数的实际应用[^1][^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值