技术演进中的开发沉思-5:window编程系列-作业

上一个章节讲了进程,把Windows 系统就像是一座庞大而繁忙的城市,进程则是城市里形形色色的居民。有的居民安静本分,按部就班地完成自己的工作;有的居民则活力充沛,时不时就想 “大闹一番”。在这座城市里,如果没有合理的管理,很容易陷入混乱。而 Windows 作业,就像是城市里的 “社区管理员”,它的首要任务就是对进程这个 “居民” 施加限制,维护城市的秩序。

1、对作业中的进程施加限制

在 Windows 系统中,作业就像一个 “虚拟社区”,被纳入作业的进程就是这个社区的 “住户”。为了让社区能够和谐稳定地运行,“管理员”(作业)需要对住户(进程)的行为做出各种限制。比如,限制进程能够使用的 CPU 时间,就好比规定一个家庭每天只能使用一定量的水电,避免过度消耗资源,影响其他 “住户”。再比如,限制进程能够访问的内存大小,这就如同限制每个家庭的居住面积,防止某个进程因为过度占用内存,导致系统内存紧张,运行缓慢。

我记得有一次,我们在开发一个大型的数据处理程序时,程序中包含了多个子进程。由于没有合理限制这些进程的资源使用,在数据量较大时,整个系统变得异常卡顿。后来,我们引入了 Windows 作业,为每个进程设定了 CPU 和内存的使用上限。就像给狂奔的野马套上了缰绳,程序运行一下子变得稳定有序,系统也恢复了往日的流畅。以下是使用 C++ 代码对作业中的进程施加限制的示例:

#include <windows.h>

#include <stdio.h>

int main() {

// 创建作业对象

HANDLE hJob = CreateJobObject(NULL, NULL);

if (hJob == NULL) {

printf("CreateJobObject failed. Error: %d\n", GetLastError());

return 1;

}

// 设置作业的基本限制信息

JOBOBJECT_BASIC_LIMIT_INFORMATION jbli = {0};

jbli.PerProcessUserTimeLimit.QuadPart = 100000000; // 限制每个进程的用户CPU时间为1秒(100000000 100ns单位)

jbli.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_TIME;

JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {0};

jeli.BasicLimitInformation = jbli;

jeli.ProcessMemoryLimit = 1024 * 1024 * 100; // 限制进程内存为100MB

if (!SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli))) {

printf("SetInformationJobObject failed. Error: %d\n", GetLastError());

CloseHandle(hJob);

return 1;

}

// 这里可以创建新进程并将其加入作业中,此处省略创建进程部分代码

// 关闭作业句柄

CloseHandle(hJob);

return 0;

}

这种对进程施加限制的能力,让 Windows 作业成为了程序稳定运行的坚实保障。

2、 将进程放入作业中

把进程放入作业,就像是将散落的居民组织进社区。在 Windows 系统的编程世界里,这一过程需要通过特定的 API 函数来实现。当我们创建一个作业对象时,就相当于建立了一个新的 “社区”。然后,通过一系列操作,将进程 “邀请” 进这个社区。

这让我想起刚做项目经理的时候,负责组建一个新的开发团队。团队成员就像是一个个进程,来自不同的地方,有着不同的工作习惯和能力。我需要将他们整合到一起,明确各自的职责和工作范围,就如同把进程放入作业中,让他们在一个统一的框架下协作。每一次成功地将进程纳入作业,都像是成功地说服一个成员加入团队,看着他们开始在新的环境中发挥作用,那种成就感油然而生。下面是将进程放入作业的 C++ 代码示例:

#include <windows.h>

#include <stdio.h>

int main() {

// 创建作业对象

HANDLE hJob = CreateJobObject(NULL, NULL);

if (hJob == NULL) {

printf("CreateJobObject failed. Error: %d\n", GetLastError());

return 1;

}

// 创建进程的启动信息和进程信息结构体

STARTUPINFO si = {sizeof(si)};

PROCESS_INFORMATION pi;

// 要创建的进程命令行,这里以记事本为例

TCHAR szCommandLine[] = TEXT("notepad.exe");

if (!CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {

printf("CreateProcess failed. Error: %d\n", GetLastError());

CloseHandle(hJob);

return 1;

}

// 将进程加入作业中

if (!AssignProcessToJobObject(hJob, pi.hProcess)) {

printf("AssignProcessToJobObject failed. Error: %d\n", GetLastError());

CloseHandle(pi.hProcess);

CloseHandle(pi.hThread);

CloseHandle(hJob);

return 1;

}

// 关闭进程和线程句柄

CloseHandle(pi.hProcess);

CloseHandle(pi.hThread);

// 关闭作业句柄

CloseHandle(hJob);

return 0;

}

3、 终止作业中的所有线程查询作业统计信息

当我们需要对作业进行管理时,终止作业中的所有线程和查询作业统计信息是两项重要的操作。终止作业中的所有线程,就好比社区管理员发现某个 “住户” 出现了严重问题,为了避免影响整个社区,果断采取措施让其停止活动。在程序运行过程中,如果某个进程出现了异常,可能会影响整个系统的稳定性,这时通过作业终止该进程的所有线程,能够迅速解决问题,保障系统安全。

而查询作业统计信息,则像是社区管理员定期对社区进行盘点。了解每个 “住户” 的资源使用情况,比如消耗了多少 CPU 时间、占用了多少内存等。这些统计信息对于优化程序性能、合理分配系统资源至关重要。就像我做部门经理时,需要定期了解每个项目组的工作进度和资源消耗情况,以便做出更合理的决策。通过查询作业统计信息,我们能够清晰地掌握程序运行的 “健康状况”,为后续的优化和改进提供依据。以下是终止作业中所有线程和查询作业统计信息的 C++ 代码示例:


#include <windows.h>

#include <stdio.h>

int main() {

// 创建作业对象

HANDLE hJob = CreateJobObject(NULL, NULL);

if (hJob == NULL) {

printf("CreateJobObject failed. Error: %d\n", GetLastError());

return 1;

}

// 创建进程并加入作业(此处省略创建进程代码,可参考5.2节)

// 终止作业中的所有进程

if (!TerminateJobObject(hJob, 0)) {

printf("TerminateJobObject failed. Error: %d\n", GetLastError());

}

// 查询作业统计信息

JOBOBJECT_BASIC_ACCOUNTING_INFORMATION jbai;

DWORD cb = sizeof(jbai);

if (QueryInformationJobObject(hJob, JobObjectBasicAccountingInformation, &jbai, cb, &cb)) {

printf("Total CPU time used by processes in the job: %I64d 100ns units\n", jbai.TotalUserTime.QuadPart);

} else {

printf("QueryInformationJobObject failed. Error: %d\n", GetLastError());

}

// 关闭作业句柄

CloseHandle(hJob);

return 0;

}

4、作业通知

作业通知功能是 Windows 作业的一项贴心设计,它就像是社区里的 “智能报警器”。当作业中的进程出现特定事件时,比如进程结束、资源使用达到阈值等,作业会及时发出通知。这种通知机制让程序开发者能够实时掌握作业运行的动态,及时做出响应。

我在创业初期,开发了一款实时监控软件。其中就运用了作业通知功能,当被监控的进程出现异常情况时,软件能够第一时间收到作业发出的通知,并及时采取措施,如自动重启进程或向管理员发送警报。这就好比社区里安装了智能监控系统,一旦有异常情况发生,管理员能够立即知晓并处理。作业通知功能让程序变得更加智能和灵活,能够更好地适应复杂多变的运行环境。下面是使用作业通知的 C++ 代码示例,通过等待作业对象的信号来获取通知:


#include <windows.h>

#include <stdio.h>

int main() {

// 创建作业对象

HANDLE hJob = CreateJobObject(NULL, NULL);

if (hJob == NULL) {

printf("CreateJobObject failed. Error: %d\n", GetLastError());

return 1;

}

// 创建进程并加入作业(此处省略创建进程代码,可参考5.2节)

// 设置作业通知,当作业中的任何进程结束时发出通知

JOBOBJECT_NOTIFICATION_INFORMATION jni = {0};

jni.EventMask = JOB_OBJECT_NOTIFY_PROCESS_EXIT;

HANDLE hJobNotification = CreateIoCompletionPort((HANDLE)hJob, NULL, 0, 1);

if (hJobNotification == NULL) {

printf("CreateIoCompletionPort failed. Error: %d\n", GetLastError());

CloseHandle(hJob);

return 1;

}

// 等待作业通知

OVERLAPPED ol = {0};

if (!SetInformationJobObject(hJob, JobObjectNotificationInformation, &jni, sizeof(jni))) {

printf("SetInformationJobObject for notification failed. Error: %d\n", GetLastError());

CloseHandle(hJobNotification);

CloseHandle(hJob);

return 1;

}

printf("Waiting for job notification...\n");

DWORD dwBytes;

LPOVERLAPPED_COMPLETION_PORT lpCompletionPort;

if (GetQueuedCompletionStatus(hJobNotification, &dwBytes, (PULONG_PTR)&lpCompletionPort, &ol, INFINITE)) {

printf("Job notification received!\n");

} else {

printf("GetQueuedCompletionStatus failed. Error: %d\n", GetLastError());

}

// 关闭相关句柄

CloseHandle(hJobNotification);

CloseHandle(hJob);

return 0;

}

5、 job lab 示例程序

为了更好地理解 Windows 作业的实际应用,我们来看一个 job lab 示例程序。这个示例程序就像是一个 “社区生活样板间”,展示了 Windows 作业在实际场景中的各种功能和用法。

在这个 “样板间” 里,我们可以看到如何创建作业、将进程放入作业、对进程施加限制、查询作业统计信息以及利用作业通知功能。每一行代码都像是 “样板间” 里的一件家具,各司其职,共同构建起一个完整的作业管理体系。下面是一个整合了上述多种功能的 job lab 示例 C++ 代码:


#include <windows.h>

#include <stdio.h>

int main() {

// 创建作业对象

HANDLE hJob = CreateJobObject(NULL, NULL);

if (hJob == NULL) {

printf("CreateJobObject failed. Error: %d\n", GetLastError());

return 1;

}

// 设置作业的基本限制信息

JOBOBJECT_BASIC_LIMIT_INFORMATION jbli = {0};

jbli.PerProcessUserTimeLimit.QuadPart = 100000000; // 限制每个进程的用户CPU时间为1秒

jbli.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_TIME;

JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {0};

jeli.BasicLimitInformation = jbli;

jeli.ProcessMemoryLimit = 1024 * 1024 * 100; // 限制进程内存为100MB

if (!SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli))) {

printf("SetInformationJobObject failed. Error: %d\n", GetLastError());

CloseHandle(hJob);

return 1;

}

// 创建进程并加入作业

STARTUPINFO si = {sizeof(si)};

PROCESS_INFORMATION pi;

TCHAR szCommandLine[] = TEXT("notepad.exe");

if (!CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {

printf("CreateProcess failed. Error: %d\n", GetLastError());

CloseHandle(hJob);

return 1;

}

if (!AssignProcessToJobObject(hJob, pi.hProcess)) {

printf("AssignProcessToJobObject failed. Error: %d\n", GetLastError());

CloseHandle(pi.hProcess);

CloseHandle(pi.hThread);

CloseHandle(hJob);

return 1;

}

CloseHandle(pi.hProcess);

CloseHandle(pi.hThread);

// 设置作业通知,当作业中的任何进程结束时发出通知

JOBOBJECT_NOTIFICATION_INFORMATION jni = {0};

jni.EventMask = JOB_OBJECT_NOTIFY_PROCESS_EXIT;

HANDLE hJobNotification = CreateIoCompletionPort((HANDLE)hJob, NULL, 0, 1);

if (hJobNotification == NULL) {

printf("CreateIoCompletionPort failed. Error: %d\n", GetLastError());

CloseHandle(hJob);

return 1;

}

if (!SetInformationJobObject(hJob, JobObjectNotificationInformation, &jni, sizeof(jni))) {

printf("SetInformationJobObject for notification failed. Error: %d\n", GetLastError());

CloseHandle(hJobNotification);

CloseHandle(hJob);

return 1;

}

// 等待作业通知

OVERLAPPED ol = {0};

printf("Waiting for job notification...\n");

DWORD dwBytes;

LPOVERLAPPED_COMPLETION_PORT lpCompletionPort;

if (GetQueuedCompletionStatus(hJobNotification, &dwBytes, (PULONG_PTR)&lpCompletionPort, &ol, INFINITE)) {

printf("Job notification received!\n");

} else {

printf("GetQueuedCompletionStatus failed. Error: %d\n", GetLastError());

}

// 查询作业统计信息

JOBOBJECT_BASIC_ACCOUNTING_INFORMATION jbai;

DWORD cb = sizeof(jbai);

if (QueryInformationJobObject(hJob, JobObjectBasicAccountingInformation, &jbai, cb, &cb)) {

printf("Total CPU time used by processes in the job: %I64d 100ns units\n", jbai.TotalUserTime.QuadPart);

} else {

printf("QueryInformationJobObject failed. Error: %d\n", GetLastError());

}

// 终止作业中的所有进程

if (!TerminateJobObject(hJob, 0)) {

printf("TerminateJobObject failed. Error: %d\n", GetLastError());

}

// 关闭相关句柄

CloseHandle(hJobNotification);

CloseHandle(hJob);

return 0;

}

通过学习和分析这个示例程序,我们能够更直观地感受到 Windows 作业的强大功能,也能为自己的程序开发提供宝贵的经验和借鉴。

回顾我的 IT 职业生涯,从程序员到创业者,见证了无数技术的兴衰更替。而 Windows 作业,就像是一位默默守护程序世界秩序的老友,在每一个关键的时刻,都发挥着不可或缺的作用。它让我明白,在看似复杂的代码世界里,只要有合理的管理和规划,就能创造出稳定、高效的程序。未来,无论技术如何发展,这种对秩序和规则的追求,都将是我们程序员不断前行的动力源泉。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值