十、C++速通秘籍—多进程

打印 上一主题 下一主题

主题 1389|帖子 1389|积分 4167

上一章节:

九、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体系:
  1. /**
  2. *  多进程实践,这里是在windows系统下
  3. */
  4. #include <iostream>
  5. #include <windows.h>
  6. int main() {
  7.     // 获取当前进程的句柄
  8.     HANDLE hProcess = GetCurrentProcess();
  9.     // 设置进程优先级为高
  10.     if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS)) {
  11.         std::cout << "Process priority has been set to high." << std::endl;
  12.     } else {
  13.         // 获取错误代码
  14.         DWORD errorCode = GetLastError();
  15.         std::cerr << "Failed to set process priority. Error code: " << errorCode << std::endl;
  16.         return 1;
  17.     }
  18.     // 这里可以添加其他需要执行的任务代码
  19.     return 0;
  20. }   
复制代码
四、多进程在 Linux 和 Windows 体系中的实现与进程间通信

1、Linux 体系

1.1、常见实现方式在 Linux 中,创建新进程最常用的方式是利用

fork()函数。
fork()函数会创建一个与父进程几乎完全相同的子进程,子进程会继承父进程的大部分资源,包罗文件描述符、环境变量等。下面是一个简单的示例:
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. int main() {
  4.     pid_t pid;
  5.     pid_t pid2;
  6.     // 获取当前父进程Id
  7.     pid = getpid();
  8.     printf("before fork: pid = %d\n",pid);//fork之前获取当前进程的pid
  9.     pid_t retFork = fork(); // 创建子进程
  10.     pid2 = getpid();
  11.     if (retFork == -1) {
  12.         perror("fork failed"); // 子进程创建失败
  13.         return -1;
  14.     } else if (pid != pid2) {
  15.         // 子进程
  16.         printf("I am the child process. My PID is %d\n", getpid());
  17.     } else {
  18.         // 父进程
  19.         printf("I am the parent process. My PID is %d\n", getpid());
  20.     }
  21.     return 0;
  22. }
复制代码

1.2、常见利用场景:

一个父进程盼望复制本身,使父、子进程同时执行差别的代码段。这在网络服务进程中是常见的——父进程等待客户端的服务哀求。当这种哀求到达时,父进程调用fork,使子进程处置惩罚此哀求。父进程则继续等待下一个服务哀求到达。
代码示例:
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <unistd.h>
  4. int main()
  5. {
  6.     pid_t pid;
  7.     int data;
  8.     while(1)
  9.     {
  10.          {// 此处可以改成监听网络中信号或者外部请求
  11.              printf("please input a data\n");//父进程一直等待客户的消息
  12.              scanf("%d",&data);   
  13.          }
  14.          if(data == 1)//收到数据为1就创建一个子进程来处理客户的消息,父进程继续等待其他客户的消息
  15.          {
  16.              pid = fork();
  17.              if(0 == pid)//在子进程里处理
  18.              {
  19.                  while(1)//这里假设子进程处理时间比较长
  20.                  {
  21.                           printf("do net request,pid = %d\n",getpid());//子进程打印自己的pid
  22.                           sleep(3);//延时一会
  23.                  }
  24.              }
  25.          }
  26.          else
  27.          {
  28.                printf("wait,do nothing\n");//收到的数据不是1则继续等待
  29.          }
  30.      }
  31.     return 0;
  32. }
复制代码

2、windows体系

        在 Windows 中,创建新进程利用CreateProcess()函数。该函数不仅可以创建新进程,还可以指定新进程的启动参数、安全属性等。下面是一个简单的示例:
  1. #include <windows.h>
  2. #include <stdio.h>
  3. int main() {
  4.         // 创建进程
  5.         STARTUPINFO si;
  6.         PROCESS_INFORMATION pi;
  7.         ZeroMemory(&si, sizeof(si));
  8.         si.cb = sizeof(si);
  9.         ZeroMemory(&pi, sizeof(pi));
  10.    
  11.         if (!CreateProcess(
  12.             NULL,
  13.             TEXT("processEx.exe"), // 调用可执行程序
  14.             NULL,
  15.             NULL,
  16.             FALSE,
  17.             0,
  18.             NULL,
  19.             NULL,
  20.             &si,
  21.             &pi))
  22.         {
  23.             printf("CreateProcess failed: %d\n", GetLastError());
  24.             return 1;
  25.         }else{
  26.             printf("CreateProcess succeed: %d\n", pi.dwProcessId);
  27.         }
  28.         // 等待子进程结束
  29.         // WaitForSingleObject(pi.hProcess, INFINITE);
  30.         CloseHandle(pi.hProcess);
  31.         CloseHandle(pi.hThread);
  32.     return 0;
  33. }
复制代码

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 实现示例

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <fcntl.h>
  7. int main() {
  8.     pid_t pid = fork();
  9.     if (pid == -1) {
  10.         perror("fork");
  11.         return 1;
  12.     } else if (pid != 0) {
  13.         // 父进程退出
  14.         exit(0);
  15.     }
  16.     // 创建新会话
  17.     if (setsid() == -1) {
  18.         perror("setsid");
  19.         return 1;
  20.     }
  21.     // 改变工作目录
  22.     if (chdir("/") == -1) {
  23.         perror("chdir");
  24.         return 1;
  25.     }
  26.     // 重设文件权限掩码
  27.     umask(0);
  28.     // 关闭标准输入、输出和错误输出
  29.     close(STDIN_FILENO);
  30.     close(STDOUT_FILENO);
  31.     close(STDERR_FILENO);
  32.     // 守护进程主体,例如记录系统日志
  33.     while (1) {
  34.         // 模拟日志记录
  35.         int fd = open("/var/log/daemon.log", O_WRONLY | O_CREAT | O_APPEND, 0644);
  36.         if (fd != -1) {
  37.             write(fd, "Daemon is running\n", 16);
  38.             close(fd);
  39.         }
  40.         sleep(10);
  41.     }
  42.     return 0;
  43. }
复制代码
1.3、Windows 实现示例

在 Windows 中,可以利用服务来实现类似保卫进程的功能。下面是一个简单的服务示例:
  1. #include <windows.h>
  2. #include <stdio.h>
  3. SERVICE_STATUS serviceStatus;
  4. SERVICE_STATUS_HANDLE serviceStatusHandle;
  5. VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
  6. VOID WINAPI ServiceCtrlHandler(DWORD fdwControl);
  7. int main(int argc, char **argv) {
  8.     SERVICE_TABLE_ENTRY serviceTable[] = {
  9.         {TEXT("MyService"), ServiceMain},
  10.         {NULL, NULL}
  11.     };
  12.     if (!StartServiceCtrlDispatcher(serviceTable)) {
  13.         printf("StartServiceCtrlDispatcher failed: %d\n", GetLastError());
  14.     }
  15.     return 0;
  16. }
  17. VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv) {
  18.     serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  19.     serviceStatus.dwCurrentState = SERVICE_START_PENDING;
  20.     serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
  21.     serviceStatus.dwWin32ExitCode = 0;
  22.     serviceStatus.dwServiceSpecificExitCode = 0;
  23.     serviceStatus.dwCheckPoint = 0;
  24.     serviceStatus.dwWaitHint = 0;
  25.     serviceStatusHandle = RegisterServiceCtrlHandler(TEXT("MyService"), ServiceCtrlHandler);
  26.     if (serviceStatusHandle == NULL) {
  27.         return;
  28.     }
  29.     // 标记服务已启动
  30.     serviceStatus.dwCurrentState = SERVICE_RUNNING;
  31.     SetServiceStatus(serviceStatusHandle, &serviceStatus);
  32.     // 服务主体,例如记录系统日志
  33.     while (serviceStatus.dwCurrentState == SERVICE_RUNNING) {
  34.         // 模拟日志记录
  35.         FILE *fp = fopen("C:\\Logs\\ServiceLog.txt", "a");
  36.         if (fp != NULL) {
  37.             fprintf(fp, "Service is running\n");
  38.             fclose(fp);
  39.         }
  40.         Sleep(10000);
  41.     }
  42. }
  43. VOID WINAPI ServiceCtrlHandler(DWORD fdwControl) {
  44.     switch (fdwControl) {
  45.         case SERVICE_CONTROL_STOP:
  46.             serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
  47.             SetServiceStatus(serviceStatusHandle, &serviceStatus);
  48.             // 执行清理操作
  49.             serviceStatus.dwCurrentState = SERVICE_STOPPED;
  50.             SetServiceStatus(serviceStatusHandle, &serviceStatus);
  51.             break;
  52.         default:
  53.             break;
  54.     }
  55. }
复制代码
2、唯一进程

这里用来制止在一台设备上进程多开。
六、总结

        多进程技术作为操纵体系和软件开发的核心技术之一,为我们提供了强大的工具来构建高性能、高可靠性的应用程序。通过深入明白多进程的原理和跨平台实现,我们可以或许更好地应对复杂的业务需求,为用户提供更优质的服务。无论是 Linux 还是 Windows 体系,多进程技术都在不断演进,为软件的高效执行提供了强有力的基础。
下一章节

十一、C++速通秘籍—多线程-CSDN博客
https://blog.csdn.net/weixin_36323170/article/details/147055932?spm=1001.2014.3001.5501








免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

李优秀

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表