上一章节:
九、C++速通秘籍—类和函数-CSDN博客https://blog.csdn.net/weixin_36323170/article/details/147017358?spm=1001.2014.3001.5502
本章节代码
cpp/processEx.cpp · CuiQingCheng/cppstudy - 码云 - 开源中国https://gitee.com/cuiqingcheng/cppstudy/blob/master/cpp/processEx.cpp
目录
上一章节:
本章节代码
一、引言
深入分析多进程:从原理到跨平台实践
二、明白高并行/高并发,解锁高性能体系的密钥
三、进程与进程状态切换:进程生命周期的蜕变
1、进程的定义
1.1、一个可执行文件与进程的关系
2、进程状态
进程常见的状态分别:
状态切换
四、多进程在 Linux 和 Windows 体系中的实现与进程间通信
1、Linux 体系
1.1、常见实现方式在 Linux 中,创建新进程最常用的方式是利用
1.2、常见利用场景:
2、windows体系
3、进程间通信(5种)
五、进程的特殊用法
1、保卫进程:配景运行的冷静保卫者
1.1、原理
1.2、Linux 体系
Linux 实现示例
1.3、Windows 实现示例
2、唯一进程
六、总结
下一章节
一、引言
深入分析多进程:从原理到跨平台实践
在软件开发的弘大版图中,多进程技术犹如一座桥梁,横跨在提升体系性能与处置惩罚复杂任务的两岸。无论是高并发的服务器场景,还是对资源进行高效管理的桌面应用,多进程都扮演着不可或缺的角色。本文从多进程实现高并发的原理出发,逐步探索多进程在 Linux 和 Windows 体系下的运作机制。
二、明白高并行/高并发,解锁高性能体系的密钥
在讲解多进程之前,先让我们明白“什么是并行?什么是并发?并了解二者的区别”
并行的前提是多核CPU,指的是多个任务同时在多个核心上同时执行。如下图:
并发执行是指体系可以或许同时处置惩罚多个任务,但这些任务并不一定在同一时候执行,而是通过快速切换来模仿同时执行的效果。
多进程正是利用了并发和并行的上风来实现高并发。以 Web 服务器为例,当大量用户同时访问网站时,服务器可以为每个哀求创建一个新的进程来处置惩罚。在单核 CPU 环境下,操纵体系通过快速切换这些进程,使得它们看起来像是在同时执行。而在多核 CPU 环境下,差别的进程可以被分配到差别的核心上并行执行,大大提高了体系的吞吐量。
三、进程与进程状态切换:进程生命周期的蜕变
1、进程的定义
进程是操纵体系进行资源分配和调度的根本单位。简单来说,进程包罗了正在运行的程序的代码、数据以及执行上下文等信息。
1.1、一个可执行文件与进程的关系
一个可执行文件(程序/软件)可以对应多个进程,也可以只对应一个进程,这取决于程序的设计和利用方式。一个可执行文件最少要有一个进程。
当你多次启动同一个可执行文件时,操纵体系会为每次启动创建一个新的进程。例如,你可以同时打开多个记事本程序(假设记事本程序的可执行文件是notepad.exe),每个记事本窗口都对应一个独立的进程。这些进程固然都源自同一个可执行文件,但它们在内存中是相互独立的,拥有各自的内存空间和执行上下文,彼此之间互不干扰。另外,有些程序在设计上会主动创建多个进程来完成差别的任务。例如,浏览器通常会为每个标签页创建一个独立的进程,如许可以提高浏览器的稳定性和性能。当一个标签页瓦解时,不会影响其他标签页的正常运行。
2、进程状态
进程在整个执行周期中存在多种状态;
进程常见的状态分别:
1、停当(Ready):进程已经准备好执行,等待CPU调度。就像运动员已经站在起跑线上,等待发令枪响;
2、运行(Running):进程正在CPU上执行。这是进程最活泼的阶段,犹如运动员正在尽力奔跑;
3、阻塞(Blocked):进程因等待某个变乱(如 I/O 操纵完成、信号量获取等)而暂时无法执行。比如运动员在比赛中等待接力棒。
4、制止(Terminated):进程已经完成执行或因异常而制止。就好像运动员完成了末了冲刺。
状态切换
进程状态的切换由操纵体系内核负责。当一个运行状态的进程需要等待 I/O 操纵完成时,内核会将其状态切换为阻塞状态,并将 CPU 资源分配给其他停当状态的进程。当 I/O 操纵完成后,内核会将该进程的状态切换为停当状态,等待再次调度。在抢占式调度体系中,内核还会根据进程的优先级,在符合的机遇将运行状态的进程切换为停当状态,以便让优先级更高的进程得到 CPU 资源。
下面是一个设置进程优先级的代码,基于Windows体系:
- /**
- * 多进程实践,这里是在windows系统下
- */
- #include <iostream>
- #include <windows.h>
- int main() {
- // 获取当前进程的句柄
- HANDLE hProcess = GetCurrentProcess();
- // 设置进程优先级为高
- if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS)) {
- std::cout << "Process priority has been set to high." << std::endl;
- } else {
- // 获取错误代码
- DWORD errorCode = GetLastError();
- std::cerr << "Failed to set process priority. Error code: " << errorCode << std::endl;
- return 1;
- }
- // 这里可以添加其他需要执行的任务代码
- return 0;
- }
复制代码 四、多进程在 Linux 和 Windows 体系中的实现与进程间通信
1、Linux 体系
1.1、常见实现方式在 Linux 中,创建新进程最常用的方式是利用
fork()函数。
fork()函数会创建一个与父进程几乎完全相同的子进程,子进程会继承父进程的大部分资源,包罗文件描述符、环境变量等。下面是一个简单的示例:
- #include <stdio.h>
- #include <unistd.h>
- int main() {
- pid_t pid;
- pid_t pid2;
- // 获取当前父进程Id
- pid = getpid();
- printf("before fork: pid = %d\n",pid);//fork之前获取当前进程的pid
- pid_t retFork = fork(); // 创建子进程
- pid2 = getpid();
- if (retFork == -1) {
- perror("fork failed"); // 子进程创建失败
- return -1;
- } else if (pid != pid2) {
- // 子进程
- printf("I am the child process. My PID is %d\n", getpid());
- } else {
- // 父进程
- printf("I am the parent process. My PID is %d\n", getpid());
- }
- return 0;
- }
复制代码
1.2、常见利用场景:
一个父进程盼望复制本身,使父、子进程同时执行差别的代码段。这在网络服务进程中是常见的——父进程等待客户端的服务哀求。当这种哀求到达时,父进程调用fork,使子进程处置惩罚此哀求。父进程则继续等待下一个服务哀求到达。
代码示例:
- #include <stdio.h>
- #include <sys/types.h>
- #include <unistd.h>
-
- int main()
- {
- pid_t pid;
- int data;
-
- while(1)
- {
- {// 此处可以改成监听网络中信号或者外部请求
- printf("please input a data\n");//父进程一直等待客户的消息
- scanf("%d",&data);
- }
- if(data == 1)//收到数据为1就创建一个子进程来处理客户的消息,父进程继续等待其他客户的消息
- {
- pid = fork();
- if(0 == pid)//在子进程里处理
- {
- while(1)//这里假设子进程处理时间比较长
- {
- printf("do net request,pid = %d\n",getpid());//子进程打印自己的pid
- sleep(3);//延时一会
- }
- }
- }
- else
- {
- printf("wait,do nothing\n");//收到的数据不是1则继续等待
- }
- }
- return 0;
- }
复制代码
2、windows体系
在 Windows 中,创建新进程利用CreateProcess()函数。该函数不仅可以创建新进程,还可以指定新进程的启动参数、安全属性等。下面是一个简单的示例:
- #include <windows.h>
- #include <stdio.h>
- int main() {
- // 创建进程
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
- ZeroMemory(&si, sizeof(si));
- si.cb = sizeof(si);
- ZeroMemory(&pi, sizeof(pi));
-
- if (!CreateProcess(
- NULL,
- TEXT("processEx.exe"), // 调用可执行程序
- NULL,
- NULL,
- FALSE,
- 0,
- NULL,
- NULL,
- &si,
- &pi))
- {
- printf("CreateProcess failed: %d\n", GetLastError());
- return 1;
- }else{
- printf("CreateProcess succeed: %d\n", pi.dwProcessId);
- }
- // 等待子进程结束
- // WaitForSingleObject(pi.hProcess, INFINITE);
- CloseHandle(pi.hProcess);
- CloseHandle(pi.hThread);
- return 0;
- }
复制代码
3、进程间通信(5种)
管道(Pipe):管道是一种半双工的通信方式,数据只能单向活动,有匿名管道/定名管道之分,匿名管道用于有亲缘关系的进程,定名管道可用于恣意进程。匿名管道通常用于父子进程之间的通信。匿名管道利用pipe()函数创建,定名管道利用mkfifo()函数创建。用于简单数据通报,且实时性高的场景。
消息队列(Message Queue):以消息链表情势存在于内核中,可降服信号通报信息少、管道数据无格式和缓冲区受限等标题。一样平常用于多对多进程间通信,并且对实时性要求不高的异步场景下。例如,一个日志记载进程和多个业务进程之间的通信,业务进程将日志信息发送到消息队列,日志记载进程从队列中取出消息并进行记载(该方式是linux体系中独有的)。
共享内存(Shared Memory):共享内存允许差别进程访问同一块物理内存,无需数据复制,是最快的 IPC 方式,但需要同步和互斥操纵。通过shmget()、shmat()等函数实现。一样平常用于多个进程需要频繁共享大量数据,例如数据库体系中多个进程共享数据库索引信息,对实时性要求较高的应用,如视频处置惩罚、音频处置惩罚等,共享内存可以淘汰数据传输的延迟。
信号量(Semaphore):信号量用于实现进程间的同步和互斥。在 Linux 中,可以利用semget()、semop()等函数操纵信号量。适用于进程间资源互斥,以及进程同步,用于协调多个进程的执行顺序,确保一个进程在另一个进程完成某个操纵后再继续执行。
前面四个都在一个主机上的操纵。
套接字(Socket):可用于差别主机之间的进程通信,也可用于同一主机上的进程通信,支持多种协议。网络通信中服务器和客户端之间的通信,分布式体系中各个节点间通信;
五、进程的特殊用法
1、保卫进程:配景运行的冷静保卫者
1.1、原理
保卫进程是一种在配景运行的特殊进程,它独立于控制终端,通常在体系启动时主动启动,并一直运行直到体系关闭。保卫进程的重要作用是执行一些需要恒久运行的任务,如体系日志记载、定时任务执行等。
1.2、Linux 体系
创建保卫进程通常需要以下步骤:
(1)、利用fork()函数创建一个子进程,然后父进程退出,如许可以使子进程脱离控制终端。
(2)、利用setsid()函数创建一个新的会话,使子进程成为新会话的领导者,从而脱离原有的控制终端。
(3)、改变当前工作目录为根目录,防止占用其他文件体系。
(4)、重设文件权限掩码,防止继承的文件权限影响保卫进程的运行。
(5)、关闭不需要的文件描述符,防止资源泄漏。
Linux 实现示例
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- int main() {
- pid_t pid = fork();
- if (pid == -1) {
- perror("fork");
- return 1;
- } else if (pid != 0) {
- // 父进程退出
- exit(0);
- }
- // 创建新会话
- if (setsid() == -1) {
- perror("setsid");
- return 1;
- }
- // 改变工作目录
- if (chdir("/") == -1) {
- perror("chdir");
- return 1;
- }
- // 重设文件权限掩码
- umask(0);
- // 关闭标准输入、输出和错误输出
- close(STDIN_FILENO);
- close(STDOUT_FILENO);
- close(STDERR_FILENO);
- // 守护进程主体,例如记录系统日志
- while (1) {
- // 模拟日志记录
- int fd = open("/var/log/daemon.log", O_WRONLY | O_CREAT | O_APPEND, 0644);
- if (fd != -1) {
- write(fd, "Daemon is running\n", 16);
- close(fd);
- }
- sleep(10);
- }
- return 0;
- }
复制代码 1.3、Windows 实现示例
在 Windows 中,可以利用服务来实现类似保卫进程的功能。下面是一个简单的服务示例:
- #include <windows.h>
- #include <stdio.h>
- SERVICE_STATUS serviceStatus;
- SERVICE_STATUS_HANDLE serviceStatusHandle;
- VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
- VOID WINAPI ServiceCtrlHandler(DWORD fdwControl);
- int main(int argc, char **argv) {
- SERVICE_TABLE_ENTRY serviceTable[] = {
- {TEXT("MyService"), ServiceMain},
- {NULL, NULL}
- };
- if (!StartServiceCtrlDispatcher(serviceTable)) {
- printf("StartServiceCtrlDispatcher failed: %d\n", GetLastError());
- }
- return 0;
- }
- VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv) {
- serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
- serviceStatus.dwCurrentState = SERVICE_START_PENDING;
- serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
- serviceStatus.dwWin32ExitCode = 0;
- serviceStatus.dwServiceSpecificExitCode = 0;
- serviceStatus.dwCheckPoint = 0;
- serviceStatus.dwWaitHint = 0;
- serviceStatusHandle = RegisterServiceCtrlHandler(TEXT("MyService"), ServiceCtrlHandler);
- if (serviceStatusHandle == NULL) {
- return;
- }
- // 标记服务已启动
- serviceStatus.dwCurrentState = SERVICE_RUNNING;
- SetServiceStatus(serviceStatusHandle, &serviceStatus);
- // 服务主体,例如记录系统日志
- while (serviceStatus.dwCurrentState == SERVICE_RUNNING) {
- // 模拟日志记录
- FILE *fp = fopen("C:\\Logs\\ServiceLog.txt", "a");
- if (fp != NULL) {
- fprintf(fp, "Service is running\n");
- fclose(fp);
- }
- Sleep(10000);
- }
- }
- VOID WINAPI ServiceCtrlHandler(DWORD fdwControl) {
- switch (fdwControl) {
- case SERVICE_CONTROL_STOP:
- serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
- SetServiceStatus(serviceStatusHandle, &serviceStatus);
- // 执行清理操作
- serviceStatus.dwCurrentState = SERVICE_STOPPED;
- SetServiceStatus(serviceStatusHandle, &serviceStatus);
- break;
- default:
- break;
- }
- }
复制代码 2、唯一进程
这里用来制止在一台设备上进程多开。
六、总结
多进程技术作为操纵体系和软件开发的核心技术之一,为我们提供了强大的工具来构建高性能、高可靠性的应用程序。通过深入明白多进程的原理和跨平台实现,我们可以或许更好地应对复杂的业务需求,为用户提供更优质的服务。无论是 Linux 还是 Windows 体系,多进程技术都在不断演进,为软件的高效执行提供了强有力的基础。
下一章节
十一、C++速通秘籍—多线程-CSDN博客https://blog.csdn.net/weixin_36323170/article/details/147055932?spm=1001.2014.3001.5501
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |