《TCP/IP网络编程》阅读笔记--并发多进程服务端的使用

1--并发服务器端

并发服务器端主要有以下三类:

        ① 多进程服务器:通过创建多个进程提供服务;

        ② 多路复用服务器:通过捆绑并统一管理I/O对象提供服务;

        ③ 多线程服务器:通过生成与客户端等量的线程提供服务;

2--进程

2-1--进程的相关概念

进程的相关概念:

        ① 进程的定义如下:占用内存空间的正在运行的程序;

        ② 从操作系统的角度看,进程是程序流的基本单位,若创建多个进程,则操作系统将同时运行;

        ③ 对于 CPU 而言,核的个数与可同时运行的进程数相同;若进程数超过核数,进程将分时使用 CPU 资源;

        ④ 无论进程是如何创建的,所有进程都会从操作系统中分配到进程 ID,其值为大于 2 的整数;

2-2--fork()创建进程

#include <unistd.h>
pid_t fork(void);// 成功时返回进程 ID,失败时返回 -1

        fork() 函数会复制父进程的进程副本给创建的子进程,两个进程都将执行 fork() 函数调用后的语句;具体执行的内容可根据 fork() 函数的返回值进行区分,对于父进程 fork() 函数返回子进程的进程 ID,对于子进程 fork() 函数返回 0;

// gcc fork.c -o fork
// ./fork
#include <stdio.h>
#include <unistd.h>int gval = 10;
int main(int argc, char *argv[]){__pid_t pid;int lval = 20;gval++, lval += 5;pid = fork();if(pid == 0){ // 对于子进程,fork返回0,因此执行以下内容gval += 2, lval += 2;}else{ // 对于父进程,执行以下内容gval -= 2, lval -= 2;}if(pid == 0){// 对于子进程,复制父进程的进程副本,则最初 gval = 11, lval = 25;// 执行 += 2 后,gval = 13, lval = 27;printf("Child Proc: [%d, %d] \n", gval, lval);}else{// 对于父进程,执行 -= 2后,gval = 9, lval = 23;printf("Parent Proc: [%d %d] \n", gval, lval);}return 0;
}

2-3--僵尸进程

        一般进程完成工作后都应被立即销毁,但部分进行由于各种原因导致不能及时销毁,就成为了僵尸进程,其会占用系统中的重要资源;

        终止僵尸进程的两种方式:① 传递参数给 exit 函数并调用 exit 函数;② main 函数中执行 return 语句并返回值;

        向 exit 函数传递的参数值和 main 函数 return 语句的返回值都会传递给操作系统,而操作系统不会立即销毁子进程,直到把这些返回值传递给父进程;这种不会被立即销毁的子进程就是僵尸进程

        此外,操作系统不会主动将返回值传递给父进程;只有父进程主动发起请求时,操作系统才会将子进程的返回值传递给父进程;因此,如果父进程未主动要求获得子进程的结束状态值,操作系统就不会销毁子进程,子进程就一直处于僵尸进程状态;

// gcc zombie.c -o zombie
// ./zombie
#include <stdio.h>
#include <unistd.h>int main(int argc, char *argv[]){__pid_t pid = fork();if(pid == 0){puts("Hi, I am a child process");}else{// 父进程终止时,子进程也会被同时销毁// 本案例通过延缓父进程的终止时间,来让子进程进入僵尸进程状态printf("Child Process ID: %d \n", pid);sleep(30);}if(pid == 0){puts("End child process");}else{puts("End parent process");}return 0;
}

        通过 ps au 可以观测到在父进程睡眠的时间里,子进程成为了僵尸进程(Z+状态);

2-4--wait()和waitpid()销毁僵尸进程

        为了销毁僵尸子进程,父进程必须主动请求获取子进程的返回值;

        父进程调用 wait() 函数 和 waitpid() 函数可以主动获取子进程的返回值;

#include <sys/wait.h>pid_t wait(int* statloc);
// 成功时返回终止的子进程 ID, 失败时返回 -1;
// 子进程的返回值会保存到 statloc 所指的内存空间// WIFEXITED() 子进程正常终止时返回 true
// WEXITSTATUS() 返回子进程的返回值

        父进程调用 wait() 函数时,如果没有已终止的子进程,则父进程的程序将会阻塞,直至有子进程终止来返回值;

// gcc wait.c -o wait
// ./wait
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>int main(int argc, char* argv[]){int status;__pid_t pid = fork();if(pid == 0){return 3; // 第一个子进程返回3}else{printf("Child PID: %d \n", pid); // 第一个子进程的 IDpid = fork(); // 创建第二个子进程if(pid == 0){exit(7); // 第二个子进程返回7}else{printf("Child PID : %d \n", pid); // 第二个子进程的 IDwait(&status); // 主动请求获取子进程的返回值if(WIFEXITED(status)){printf("Chile send one: %d \n", WEXITSTATUS(status));}wait(&status); // 主动请求获取子进程的返回值if(WIFEXITED(status)){printf("Child send two: %d \n", WEXITSTATUS(status));}sleep(30); // 这时候父进程选择睡眠,子进程也不会成为僵尸进程}}return 0;
}

        wait() 函数会引起程序阻塞,而 waitpid() 函数不会引起阻塞;

#include <sys/wait.h>pid_t waitpid(pid_t pid, int* statloc, int options);
// 成功时返回终止的子进程的ID(或0),失败时返回-1
// pid 表示等待终止的目标子进程的 ID,传递 -1 时与 wait() 相同,即可以等待任意子进程终止
// statloc 存放子进程返回结果的地址空间
// options 设置为 WNOHANG 时,即使没有终止的子进程,父进程也不会进入阻塞状态,而是返回 0 并结束函数
// gcc waitpid.c -o waitpid
// ./waitpid
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>int main(int argc, char *argv[]){int status;__pid_t pid = fork();if(pid == 0){sleep(15);return 24;}else{// 没有终止的子进程时,返回0,则一直循环调用waitpid()// 直到有终止的子进程来跳出循环while(!waitpid(-1, &status, WNOHANG)){sleep(3);puts("sleep 3sec.");}if(WIFEXITED(status)){printf("Child send %d \n", WEXITSTATUS(status));}return 0;}
}

3--信号处理

        上述父进程调用 wait() 函数会阻塞,而调用 waitpid() 函数也必须不断调用(因为不知道子进程何时终止),这也同样会影响父进程的工作效率;

        通过信号处理机制,可以解决上述问题;信号表示在特定事件发生时由操作系统向进程发送(通知)的消息

        因此可以通过注册信号,当子进程终止时,让操作系统将子进程终止的消息发送给父进程,这时候父进程才请求获取子进程的返回值;

3-1--signal()函数

#include <signal.h>
void (*signal(int signo, void (*func)(int)))(int);// 第一个参数 signo 表示特殊情况信息
// 第二个参数表示特殊情况发生后,要调用的函数的地址值(指针)// 常见特殊情况
// 1. SIGALRM 表示已到调用 alarm 函数注册的时间
// 2. SIGINT 表示遇到了 CTRL+C 的情况
// 3. SIGCHLD 表示遇到了子进程终止的情况#include <unistd.h>
unsigned int alarm(unsigned int seconds);
// 返回 0 或以秒为单位的距离 SIGALRM 信号发生的所剩时间(即还剩下多长时间就会发生 SIGALRM 信号时间)
// 经过 seconds 秒后会发生 SIGALRM 信号事件

        发生信号事件时,将会唤醒由于调用 sleep 函数而进入阻塞状态的进程;即:即使还没到 sleep 函数规定的事件也会被强制唤醒,而进程一旦唤醒后就不会再进入睡眠状态;

// gcc signal.c -o signal
// ./signal#include <stdio.h>
#include <unistd.h>
#include <signal.h>void timeout(int sig){if(sig == SIGALRM){puts("Time out!");}alarm(2);
}void keycontrol(int sig){if(sig == SIGINT){puts("CTRL+C pressed");}
}int main(int argc, char *argv[]){int i;signal(SIGALRM, timeout);signal(SIGINT, keycontrol);alarm(2);for(i = 0; i < 3; i++){puts("wait...");sleep(100); // 不会真的睡眠 100s,因为alarm函数会产生SIGALRM信号事件,从而唤醒进程}return 0;
}

3-2--sigaction()函数

        sigaction() 函数的功能类似于 signal() 函数,但 sigaction() 更稳定;因为 signal() 函数在不同操作系统中可能存在区别,但 sigaction() 在不同 UNIX 系统中完全相同;

#include <signal.h>int sigaction(int signo, const struct sigaction* act, struct sigaction* oldact);
// 成功时返回0,失败时返回 -1
// signo 用于传递信号信息
// act 对应于 signo 的信号处理函数
// oldact 获取之前注册的信号处理函数的指针,不用时传递0struct sigaction{void (*sa_handler)(int); // 信号处理函数的指针sigset_t sa_mask; // 初始化为0int sa_flags; // 初始化为0
}
// gcc sigaction.c -o sigaction
// ./sigaction#include <stdio.h>
#include <unistd.h>
#include <signal.h>void timeout(int sig){if(sig == SIGALRM){puts("Time out!");}alarm(2);
}int main(int argc, char* argv[]){int i;struct sigaction act;act.sa_handler = timeout;sigemptyset(&act.sa_mask); // 调用sigemptyset()将sa_mask的所有位初始化为0act.sa_flags = 0; // sa_flags也初始化为0sigaction(SIGALRM, &act, 0);alarm(2);for(int i = 0; i < 3; i++){puts("wait...");sleep(100);}return 0;
}

3--3--利用信号处理技术消灭僵尸进程

// gcc remove_zombie.c -o remove_zombie
// ./remove_zombie#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>void read_childproc(int sig){int status;pid_t id = waitpid(-1, &status, WNOHANG); // 等待任意子线程结束if(WIFEXITED(status)){ // 判断子线程是否正常终止printf("Remove proc id: %d \n", id);printf("Child send: %d \n", WEXITSTATUS(status)); // 打印子线程的返回值}
}int main(int argc, char *argv[]){pid_t pid;struct sigaction act;act.sa_handler = read_childproc; // 设置信号处理函数sigemptyset(&act.sa_mask);act.sa_flags = 0;sigaction(SIGCHLD, &act, 0); // 调用sigaction(),当遇到子线程结束的信号时,调用信号处理函数pid = fork();if(pid == 0){ // 子线程执行区域puts("Hi! I'm child process");sleep(10);return 12;}else{printf("Child proc id: %d \n", pid);pid = fork();if(pid == 0){ // 另一个子线程执行区域puts("Hi! I'm child process");sleep(10);return 24;}else{int i;printf("Child proc id: %d \n", pid);for(int i = 0; i < 5; i++){puts("wait...");sleep(5);}}}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/72870.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【论文通读】CLIP改进工作综述

CLIP改进工作综述 前言1. 语义分割1.1 Lseg1.2 GroupViT 2. 图像检测2.1 ViLD2.2 GLIP2.3 GLIPv2 3. 图像生成3.1 CLIPasso 4. 视频理解4.1 CLIP4Clip4.2 ActionCLIP 5. 其它领域5.1 CLIP-VIL5.2 AudioCLIP5.3 PointCLIP5.4 DepthCLIP 总结参考链接 前言 CLIP作为多模态对比学…

离散化,树状数组,P5459 [BJOI2016] 回转寿司

P5459 [BJOI2016] 回转寿司 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目描述 酷爱日料的小Z经常光顾学校东门外的回转寿司店。在这里&#xff0c;一盘盘寿司通过传送带依次呈现在小Z眼前。 不同的寿司带给小Z的味觉感受是不一样的&#xff0c;我们定义小Z对每盘寿司…

vue无感刷新

干前端工作也有一小段时间了。这不&#xff0c;研究了下vue项目里的无感刷新。可能有经验的大佬都知道也会敲。大佬就略过吧。 vue无感刷新&#xff0c;大致的原理就是后端返回两个token给前端。&#xff08;一个是请求的token&#xff0c;另一个是到期或者快到期后换取新token…

如何快速清理已经上传到Git仓库的.DS_Store文件

很久以前&#xff0c;发过这样一篇文章《Git全局忽略MacOS系统下的.DS_Store文件》&#xff0c;主要是针对MacOS用户&#xff0c;如何方便的在自己机器中免疫所有.DS_Store文件的误提交。如果有这个需求&#xff0c;且还没有搞过的读者可以通过上面这篇文章学习。 今天想要分享…

递归算法学习——N皇后问题,单词搜索

目录 ​编辑 一&#xff0c;N皇后问题 1.题意 2.解释 3.题目接口 4.解题思路及代码 二&#xff0c;单词搜索 1.题意 2.解释 3.题目接口 4.思路及代码 一&#xff0c;N皇后问题 1.题意 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上…

CSP-J初赛复习大题整理笔记

本篇全是整理&#xff0c;为比赛准备. 在这里插入代码片 #include<cstdio> using namespace std; int n, m; int a[100], b[100];int main() {scanf_s("%d%d", &n, &m);for (int i 1; i < n; i)a[i] b[i] 0;//将两个数组清0&#xff0c;这…

【zookeeper】zookeeper介绍

分布式协调技术 在学习ZooKeeper之前需要先了解一种技术——分布式协调技术。那么什么是分布式协调技术&#xff1f;其实分布式协调技术主要用来解决分布式环境当中多个进程之间的同步控制&#xff0c;让他们有序的去访问某种临界资源&#xff0c;防止造成"脏数据"的…

5分钟生成10条短视频,AI重构电商营销

点击关注 文&#xff5c;姚 悦&#xff0c;编&#xff5c;王一粟 “我们将正式告别过去单一渠道投放的时代&#xff0c;走向一站式跨渠道品效联合经营的全新时代。”9月6日&#xff0c;在2023年其最重要的营销峰会上&#xff0c;淘天集团阿里妈妈市场部总经理穆尔说道。 当天…

SpringMVC常用的三种获取请求参数的方式

在Spring MVC中&#xff0c;可以使用多种方式来获取请求参数。下面我将介绍常用的几种方式&#xff0c;并提供相关的示例代码。 1. 使用RequestParam注解获取请求参数 RequestParam注解用于从请求中获取指定名称的参数值&#xff0c;并将其绑定到方法参数上。如果请求中没有找…

小程序实现摄像头拍照 + 水印绘制

文章标题 01 功能说明02 使用方式 & 效果图2.1 基础用法2.2 拍照 底部定点水印 预览2.3 拍照 整体背景水印 预览 03 全部代码3.1 页面布局 html3.2 业务核心 js3.3 基础样式 css 01 功能说明 需求&#xff1a;小程序端需要调用前置摄像头进行拍照&#xff0c;并且将拍…

OpenCV 06(图像的基本变换)

一、图像的基本变换 1.1 图像的放大与缩小 - resize(src, dsize, dst, fx, fy, interpolation) - src: 要缩放的图片 - dsize: 缩放之后的图片大小, 元组和列表表示均可. - dst: 可选参数, 缩放之后的输出图片 - fx, fy: x轴和y轴的缩放比, 即宽度和高度的缩放比. - …

ChatGPT在虚拟旅游和文化体验中的潜在作用如何?

ChatGPT在虚拟旅游和文化体验中具有潜在的重要作用。虚拟旅游是一种数字化的旅行方式&#xff0c;使人们可以在不离开家门的情况下探索世界各地的地点、文化和景观。ChatGPT可以通过提供信息、交互性和定制化的建议&#xff0c;增强虚拟旅游的体验&#xff0c;同时也可以用于文…

代码随想录31|452. 用最少数量的箭引爆气球,435. 无重叠区间,763.划分字母区间

452. 用最少数量的箭引爆气球 链接地址 class Solution { private:static bool cmp(const vector<int>& a, const vector<int>& b) {return a[0] < b[0];//从小到大排序} public:int findMinArrowShots(vector<vector<int>>& points) {…

stable diffusion实践操作-大模型介绍-SDXL1大模型

系列文章目录 大家移步下面链接中&#xff0c;里面详细介绍了stable diffusion的原理&#xff0c;操作等&#xff08;本文只是下面系列文章的一个写作模板&#xff09;。 stable diffusion实践操作 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生…

java面试题

一、java面试题第一天 1.java的特点 **Java是一门面向对象的编程。**面向对象就是我们将对应的操作都封装成对应的api,就是所谓的方法&#xff0c;提供给别人使用&#xff08;面向过程就是所有的方法&#xff0c;api由自己编写&#xff09; java具有平台独立性和移植性。 jav…

2023-9-8 求组合数(三)

题目链接&#xff1a;求组合数 III #include <iostream> #include <algorithm>using namespace std;typedef long long LL;int p;int qmi(int a, int k) {int res 1;while(k){if(k & 1) res (LL) res * a % p;k >> 1;a (LL) a * a % p;}return res; }…

FAT32文件系统f_mkfs函数详解

1.f_mkfs参数 参数path&#xff1a;要挂载/卸载的逻辑驱动器号;使用设备根路径表示。 参数opt&#xff1a;系统的格式&#xff0c;如图所示&#xff0c;选择FM_FAT32即可&#xff0c;选择其他的可能无法格式化。 参数au&#xff1a;每簇的字节数&#xff0c;以字节为单位&#…

后端面试话术集锦第 十一 篇:mybatis面试话术

这是后端面试集锦第十一篇博文——mybatis面试话术❗❗❗ 1. 介绍下mybatis,说说它的优缺点是什么? Mybatis是一个半ORM(对象关系映射)的持久层框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程…

安全模型中的4个P

引言&#xff1a;在安全模型中&#xff0c;经常会碰到PDR,PPDR&#xff0c;IPDRR&#xff0c;CARTA-PPDR等模型&#xff0c;其中的P&#xff0c;是predict&#xff1f;是prevent&#xff1f;还是protect&#xff1f;还是policy呢&#xff1f; 一、4P字典意思解释 1、predict&a…

使用内网负载机(Linux)执行Jmeter性能测试

一、背景 ​ 在我们工作中有时候会需要使用客户提供的内网负载机进行性能测试&#xff0c;一般在什么情况下我们需要要求客户提供内网负载机进行性能测试呢&#xff1f; 遇到公网环境下性能测试达到了带宽瓶颈。那么这时&#xff0c;我们就需要考虑在内网环境负载机下来执行我们…