当前位置: 首页 > news >正文

Linux:进程入门(进程与程序的区别,进程的标识符,fork函数创建多进程)

往期文章:《Linux:深入了解冯诺依曼结构与操作系统》

Linux:深入理解冯诺依曼结构与操作系统-CSDN博客

目录

1. 概念

2. 描述进程

3. 深入理解进程的本质

4. 进程PID

4.1 指令获取PID

4.2 geipid函数获取PID

4.3 kill指令终止进程

4.4 进程信息文件夹

(1)exe

(2)cwd

5. 进程PPID

5.1 getppid函数获取PPID

5.2 fork函数创建子进程

6. 创建多进程

7. fork函数如何返回两个值


1. 概念

进程(Process)是指一个正在执行的程序实例。在操作系统中,进程是资源分配和调度的基本单位。可执行程序与进程的区别:可执行程序是一组指令的集合,它存储在磁盘上,而进程则是程序在执行时的一个实例,它存在于内存中

2. 描述进程

操作系统也称之为Operating System,缩写就是OS。

操作系统笼统的分为内核(kernel)和外壳程序。内核有进程管理、内存管理、文件管理和驱动管理。操作系统管理任何对象,都是对其属性进行管理,遵守先描述,再组织的原则。

因此,操作系统管理进程,相当于管理它的属性,会把进程属性放在一个叫做进程信息控制块的数据结构当中,就是进程属性的集合。这也称为PCB(process control block),Linux操作系统下的PCB是task_struct

下面是task_struct结构体中包含的内容:

  • 标示符: 描述本进程的唯一标示符,用来区别其他进程。
  • 状态: 任务状态,退出代码,退出信号等。
  • 优先级: 相对于其他进程的优先级。
  • 程序计数器: 程序中即将被执行的下一条指令的地址。
  • 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。
  • 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
  • I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
  • 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
  • 其他信息

3. 深入理解进程的本质

如下图,进程的前身就是一个可执行程序,它主要包含代码和数据,且在硬盘当中。可执行程序在windows系统中一般以.exe结尾,在Linux系统中一般没有特定的后缀结尾,且通常不跟后缀。

当一个可执行程序启动运行时,其代码与数据将被载入内存之中。在这一过程之前,操作系统内核会创建一个名为task_struct的结构体对象,该对象详细记录了进程的各种属性。这些属性包括但不限于进程标识符(pid)、状态信息、优先级等。特别地,memptr指针负责记录内存中myexe程序的起始地址,确保进程能够正确地访问和执行程序代码。

进程 = 内核数据结构(task_struct) + 程序的代码和数据

当多个可执行程序同时运行时,task_struct结构体内部采用特定的数据结构,将所有进程串联起来。设想task_struct中包含一个指向下一个结构体的指针,那么将存在一个结构体指针list,它指向链表中的第一个task_struct对象。随着新进程的创建,它们的task_struct对象会被前一个对象的next指针串联起来,从而在操作系统中形成了一个调度队列

CPU的寄存器并不直接存储程序的代码和数据,而是通过list指针定位到相应的task_struct对象,并将其信息载入寄存器。因此,当CPU调度内存中的myexe进程时,实际上是在操作该进程的task_struct对象,进而间接执行程序代码。

我们可以用一个类比来理解这个过程:假设张三正在求职,他提交了一份简历给公司的人力资源部门(HR)。这份简历包含了张三的个人详细信息、实习经历和项目经验,这些信息集合就如同进程的task_struct结构体,记录了张三的“属性”。当HR收到众多简历并逐一审核时,求职者本人对于这一过程是一无所知的。这与CPU调度进程的情形相似,CPU并不直接运行内存中的程序,而是通过操作task_struct结构体对象来间接实现对进程的调度。

4. 进程PID

4.1 指令获取PID

不管是在windows系统中,你双击某个应用,还是在Linux系统中,你执行某些指令或者运行某个可执行程序,当这些程序跑起来都叫做进程。不过进程一般分为两种,第一种是执行完就退出的进程,像Linux系统下的ls pwd等指令;第二种是执行完一直不退出,直到用户关闭,像window系统中的QQ,微信等应用,这种进程叫做常驻型进程。

下面写一份C语言代码,在main函数使用while循环搞个死循环,内部是每隔一秒钟打印一句话。sleep函数的作用是使当前进程暂停执行指定的秒数,该函数的原型通常在<unistd.h>头文件中定义

当程序运行起来,会不断打印“hello linux!”。

 这时我们再打开一个Xshell程序,就可以在不打扰上面进程的情况下输入新指令并运行。

 我们可以使用ps指令来查看进程信息。其中head -1表示拿到进程信息的第一行,grep加某些进程名,表示拿到该进程名那行信息。你会发现COMMAND这列中含有grep指令,这是因为gerp myproc本身就含有myproc,也会打印出来。想要去掉它,grep后加上-v grep,就是忽略grep相关的文本信息。

  • -a:显示所有终端下的进程。
  • -x:显示没有控制终端的进程(在后台运行)。
  • -j:显示与作业控制相关的信息。

我们观察上图,第一行中有许多属性名词,其中PID表示进程的唯一标识符,PPID表示父进程的标识符。myproc进程pid是4340。如果我们按下Ctrl+C按键,会中断该进程。

 当再次启动进程时,使用ps指令获取进程信息,pid值为6336。你会发现同样都是myproc的进程,pid值发生改变。因为系统使用一个累加的计数器维护进程的PID值,在你启动进程时,操作系统也不断在请求任务。所以,PID值出现变化且不连续是很正常的。

4.2 geipid函数获取PID

getpid是一个系统调用级函数,可以获取进程的id值。需要包含两个头文件,分别是unistd.h和sys/types.h。其中pid_t其实本质就是long int类型,只不过被typedef封装了一下。

使用getpid函数不用频繁获取,只需获取一次,因为一个进程启动之后,id值不会改变的,除非重新启动一次。当myproc程序执行起来后,可以看到进程id值是8042。我们使用ps指令获取myproc进程的信息,会发现pid值也是8042。

4.3 kill指令终止进程

如果你想要终止一个进程,可以在键盘上按下Ctrl + C的按键,发出终止信号。或者使用kill指令,使用kill -l可以查看kill指令的选项,其中9对应的选项就是终止进程。

终止进程操作如上图所示,输入kill + -9 + 进程id值,在另外一恶搞Xshell程序中可以看到myproc进程停止,显示了Killed信息。

4.4 进程信息文件夹

(1)exe

我们刚刚通过ps指令可以获取进程的一部分信息。在Linux操作系统下,一切皆文件。我们使用ls指令查看根目录,会发现有个proc目录。

proc就是process(进程)的缩写,再使用ls指令查看该目录,可以发现许多以数字命名的目录,这些就是某个进程的id值,里面存放的就是该进程的相关信息。

运行myproc程序后,生成一个pid值为19775的进程,使用ls命令查看/proc路径下的文件,其中就有名为19775文件夹。该文件夹存放的就是该进程的详细信息。

我们查看/proc/19775路径下的文件,会发现里面有许多内容。今天需要认识一下exe和cwd。进程是被某个可执行程序启动,exe存放的就是该可执行程序的路径

当我们再打开一个Xshell程序,进入到myproc的目录下,删除该可执行程序,你会发现进程还是在运行。因为进程已经被加载到内存当中,删除硬盘的可执行程序不会造成影响。但是再次查看/proc/19775路径下的文件,会发现exe显示该路径下的可执行程序已被删除。

(2)cwd

cwd全称是current working directory,意思是当前工作目录。我们在C语言中创建一个文件,如果没有指定绝对路径,默认生成在当前路径下。下面我们写个代码验证一下。

运行myproc程序,过几秒终止进程。查看当前目录,会发现多了一个file.txt文件。所以使用fopen新建一个文件时,不是你以为的相对路径,而是拿到cwd再加上你输入的文件名,形成一个绝对路径。还有什么方式可以证明上面的说法呢?

我们可以使用一个系统调用函数chdir,它的作用就是改变当前进程的工作目录。我们在myproc.c中使用chdir函数,修改当前工作目录为home目录下的普通用户,也就是我正在使用的用户。如果修改到根目录或者家目录,无法新建文件夹,因为普通用户没有写权限,只有超级用户才可以新建文件或者目录。

当我们启动myproc程序后,立即查看/proc/24404中内容,会发现cwd变成了普通用户的目录。再使用ls命令查看工作目录,发现有file.txt文件。这就验证了在代码中新建文件,如果不写绝对路径,会在输入的文件名前加上当前工作目录。

5. 进程PPID

5.1 getppid函数获取PPID

ppid是指parent process id,即某个进程的父进程id值。

其中getppid函数,是获取某个进程的父进程id值。

 修改一下myproc.c中的代码,获取一下父进程id,并打印出来。

 当我们启动myproc程序,运行一会后,终止该程序,再启动该程序,重复三次。你会发现它们的父进程id值都是19496。

我们使用ps指令查看该父进程信息,发现该进程的command(执行的指令)是-bash。其中bash是Linux系统下的命令行解释器,类似于windows系统中的cmd。

因此有个结论,在Linux操作系统启动之后,命令行上执行指令或者执行程序,本质上都是bash的进程创建的子进程,由这些子进程执行我们的代码

5.2 fork函数创建子进程

fork函数也是系统调用函数。函数原型如上,返回值类型是pid_t,不用传参。它的作用是在当前进程下创建一个子进程。

修改myproc.c中的代码,一开始打印一下当前进程的pid和ppid。之后调用fork函数,试着打印子进程的pid和ppid。

运行myproc程序,你会发现打印了三行语句。其中第一行语句是第一个printf函数打印的,pid值为29447的进程应该是myproc启动后的进程它的父进程就是上面提到的bash。

但是下面打印了两行语句,第二行语句的pid值为29447,ppid值是26892,应该是当前程序的进程。最后一行语句中,pid值是29448,ppid值是29447,它的父进程就是myproc程序启动后的进程,说明这是fork函数创建出来的子进程,并且pid值是连续的。

可以得出结论,调用fork函数,当前进程会创建一个子进程,这两个进程会同时执行后面的代码,相当于两个执行流分支。因为这两个进程是连续创建的,它们的pid值连续。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int main()
{printf("I am a process, pid: %d, ppid: %d\n", getpid(), getppid());pid_t id = fork();if (id > 0){while (1){printf("我是父进程,pid: %d, ppid: %d, ret id: %d\n", getpid(), getppid(), id);sleep(1);}}else if (id == 0){while (1){printf("我是子进程,pid: %d, ppid: %d, ret id: %d\n", getpid(), getppid(), id);sleep(1);}}}

 fork函数调用成功,给父进程返回子进程的pid,给子进程返回0。如果调用失败,会设置错误值到errno变量中。

运行结果如上,fork创建进程后,当前进程接收到子进程的id值,子进程接收到0。那么为什么if else语句看起来能同时成立,且有两个返回值?

上面有提到,进程 = 内核数据结构(task_struct) + 程序的代码和数据。那么fork函数创建一个子进程时,操作系统会创建一个task_struct结构体对象。程序中的代码因为是只读的,子进程与父进程共享代码。

而子进程会私有一份数据,因为两个进程间数据互相修改,可能会触发某些条件导致程序崩溃,所以进程之间具有很强的独立性,一个进程崩溃不会影响另外一个进程。这就类似于手机上启动许多应用,如微信,抖音等,如果微信崩溃,不会影响抖音的运行。

因为fork函数创建子进程后,会出现两个执行流,那么此时fork函数返回一个值时,有两个执行流进行返回,并且进程间的数据是各自私有的,那么id变量会有两份。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int g_val = 0;int main()
{printf("I am a process, pid: %d, ppid: %d\n", getpid(), getppid());pid_t id = fork();if(id > 0){while(1){printf("我是父进程,pid: %d, ppid: %d, ret id: %d, g_val: %d\n", getpid(),     getppid(), id, g_val);sleep(1);}}else if(id == 0){while(1){printf("我是子进程,pid: %d, ppid: %d, ret id: %d, g_val: %d\n", getpid(), getppid(), id, g_val);g_val++;sleep(1);}}}

 如上我们可以定义一个全局变量g_val,在父进程中只打印出来,在子进程中不仅打印出来,每次让g_val变量加1。

结果如上,父进程的g_val值一直是0,子进程的g_val值不断变化。这就证明了子进程会私有一份数据。

6. 创建多进程

#include <iostream>
#include <unistd.h>
#include <vector>
#include <sys/types.h>
using namespace std;const int num = 10;void SubProcessRun()
{while(true){cout << "I am a sub process, pid: "<< getpid() << " ,ppid: "<<getppid()<<endl;sleep(5);}
}int main()
{   vector<pid_t> childproc;for(int i = 0; i < num; i++){pid_t id = fork();if (id == 0){//子进程SubProcessRun();}//走到这里,只能是父进程执行childproc.push_back(id);}//父进程遍历所有子进程的pidcout << "我的所有子进程:";for(auto child: childproc){cout<<child << " ";}cout << endl;sleep(10);while(true){cout << "我是父进程,pid: "<< getpid()<<endl;sleep(1);}return 0;
}

使用一个for循环,使用fork创建子进程。每个子进程再调用SubProcessRun函数,死循环不断打印pid值。 使用vector数组存储子进程的pid值,然后遍历打印出来。父进程也执行个死循环,不退出。

运行该程序,就可以一次性创建多个连续进程。

7. fork函数如何返回两个值

fork函数为什么会返回两个返回值?

因为fork函数内部再返回值之前,会先创建子进程,创建进程会先在内核中创建task_struct结构体对象,用于管理进程的属性,然后代码共享,数据拷贝父进程的。这时,子进程已经开始运行,那么就会有两个执行流,最后返回值也是代码,就会被父子进程各自返回一次。

不过还有一个问题没有弄清楚,return语句返回的是一个值。为什么两个执行流返回时,这个值就变成两个不同的值?这是后面会解决的问题。


创作充满挑战,但若我的文章能为你带来一丝启发或帮助,那便是我最大的荣幸。如果你喜欢这篇文章,请不吝点赞、评论和分享,你的支持是我继续创作的最大动力!

相关文章:

Linux:进程入门(进程与程序的区别,进程的标识符,fork函数创建多进程)

往期文章&#xff1a;《Linux&#xff1a;深入了解冯诺依曼结构与操作系统》 Linux&#xff1a;深入理解冯诺依曼结构与操作系统-CSDN博客 目录 1. 概念 2. 描述进程 3. 深入理解进程的本质 4. 进程PID 4.1 指令获取PID 4.2 geipid函数获取PID 4.3 kill指令终止进程 …...

索尼MDR-M1:超宽频的音频盛宴,打造沉浸式音乐体验

在音乐的世界里&#xff0c;每一次技术的突破都意味着全新的听觉体验。 索尼&#xff0c;作为音频技术的先锋&#xff0c;再次以其最新力作——MDR-M1封闭式监听耳机&#xff0c;引领了音乐界的新潮流。 这款耳机以其超宽频播放和卓越的隔音性能&#xff0c;为音乐爱好者和专…...

【Linux】线程的概念

一、线程的概念 1.1 什么是线程 在一个程序里的一个执行路线就叫做线程&#xff0c;更准确的定义是&#xff1a;线程是“一个进程内部的控制序列”一切进程至少都有一个执行线程线程在进程内部运行&#xff0c;本质是在进程地址空间内运行在Linux系统中&#xff0c;在CPU眼中…...

centos7.9环境下mysql8数据库双机互备环境部署

为了实现mysql数据库的高可用性,数据库采用双机互备方式部署。双机互备能够避免单点故障造成的系统故障,由于两个节点都可以进行读写,同时也可以提高整个系统的数据读写并发性能。 1. 数据库安装 centos7安装mysql8 community 服务器IP:192.168.76.84 服务器IP:192.16…...

git 报错git: ‘remote-https‘ is not a git command. See ‘git --help‘.

报错内容 原因与解决方案 第一种情况&#xff1a;git路径错误 第一种很好解决&#xff0c;在环境变量中配置正确的git路径即可&#xff1b; 第二种情况 git缺少依赖 这个情况&#xff0c;网上提供了多种解决方案。但如果比较懒&#xff0c;可以直接把仓库地址的https改成ht…...

mysql学习教程,从入门到精通,SQL GROUP BY 子句(31)

1、SQL GROUP BY 子句 当然&#xff01;在SQL中&#xff0c;GROUP BY 子句用于将结果集中的多个记录组合成一个摘要记录。通常&#xff0c;它用于结合聚合函数&#xff08;如 COUNT(), SUM(), AVG(), MAX(), MIN() 等&#xff09;来计算每个组的汇总信息。以下是一个详细的例子…...

pip 和 conda 的安装区别

在决定使用 pip 和 conda 安装包时&#xff0c;了解这两个包管理器之间的主要区别非常重要。以下是细分&#xff1a; 1. 区别 1.1. Package Management System 包裹管理系统 Pip: &#xff1a; Primarily used for Python packages. 主要用于 Python 包。 Installs package…...

大学生就业招聘:Spring Boot系统的架构分析

大学生就业招聘系统的设计与实现 摘要 随着信息互联网信息的飞速发展&#xff0c;大学生就业成为一个难题&#xff0c;好多公司都舍不得培养人才&#xff0c;只想要一专多能之人才&#xff0c;不愿是承担社会的责任&#xff0c;针对这个问题开发一个专门适应大学生就业招聘的网…...

线段树模板

文章目录 线段树练习题目线段树概念区间维护辅助函数创建线段树 &#xff1a;build修改线段树 &#xff1a;modify查询线段树&#xff1a;query 全部代码 线段树 练习题目 洛谷题单 【模板】线段树 1 【模板】线段树 2 开关 扶苏的问题 线段树概念 线段树是一种高级数据结构&a…...

【TypeScript】知识点梳理(三)

#void前面提到了代表空&#xff0c;但有个特殊情况&#xff0c;是空不是空&#xff0c;细谈是取舍&#xff0c;但我们不深究hhh# 代码示例&#xff1a; type func () > voidconst f1: func function() {return true; } 定义了空&#xff0c;返回非空值&#xff0c;理论…...

题解:SP1741 TETRIS3D - Tetris 3D

这是一道二维线段树&#xff08;树套树&#xff09;标记永久化的模版题 前置知识点&#xff08;来自董晓算法&#xff09; 好&#xff0c;现在开始我们的分析&#xff1a; 题意简述&#xff1a; 在一个二维平面内&#xff0c;有给定的坐标&#xff0c;在这个坐标范围内加上…...

EWSTM8 IAR for STM8 软件分享

1. 软件简介 EWSTM8&#xff0c;即 IAR for STM8&#xff0c;全称为 IAR Embedded Workbench for STM8&#xff0c;它是 IAR ARM 嵌入式工作台之一&#xff0c;用于开发 STM8。IAR 有多个不同名的版本&#xff0c;对应不同的开发对象。 EWSTM8最新版本为V3.11&#xff08;202…...

非机动车检测数据集 4类 5500张 电动三轮自行车 voc yolo

非机动车检测数据集 4类 5500张 电动三轮自行车 voc yolo 非机动车检测数据集介绍 数据集名称 非机动车检测数据集 (Non-Motorized Vehicle Detection Dataset) 数据集概述 该数据集专为训练和评估基于YOLO系列目标检测模型&#xff08;包括YOLOv5、YOLOv6、YOLOv7等&#x…...

Chromium 中JavaScript FileReader API接口c++代码实现

FileReader 备注&#xff1a; 此特性在 Web Worker 中可用。 FileReader 接口允许 Web 应用程序异步读取存储在用户计算机上的文件&#xff08;或原始数据缓冲区&#xff09;的内容&#xff0c;使用 File 或 Blob 对象指定要读取的文件或数据。 文件对象可以从用户使用 <…...

k8s 中微服务之 MetailLB 搭配 ingress-nginx 实现七层负载

目录 1 MetailLB 搭建 1.1 MetalLB 的作用和原理 1.2 MetalLB功能 1.3 部署 MetalLB 1.3.1 创建deployment控制器和创建一个服务 1.3.2 下载MealLB清单文件 1.3.3 使用 docker 对镜像进行拉取 1.3.4 将镜像上传至私人仓库 1.3.5 将官方仓库地址修改为本地私人地址 1.3.6 运行清…...

南昌网站建设让你的企业网站更具竞争力

南昌网站建设让你的企业网站更具竞争力 在当今竞争激烈的市场环境中&#xff0c;一个高质量的网站不仅是企业形象的展示平台&#xff0c;更是吸引客户、提升业绩的重要工具。南昌作为江西的省会城市&#xff0c;互联网产业的蓬勃发展为企业网站建设提供了良好的机遇。 首先&am…...

【重学 MySQL】五十三、MySQL数据类型概述和字符集设置

【重学 MySQL】五十三、MySQL数据类型概述和字符集设置 MySQL数据类型概述MySQL字符集设置注意事项 MySQL数据类型概述 MySQL是一个流行的关系型数据库管理系统&#xff0c;它支持多种数据类型&#xff0c;以满足不同数据处理和存储的需求。理解并正确使用这些数据类型对于提高…...

《计算机原理与系统结构》学习系列——计算机的算数运算(上)

系列文章目录 目录 ALU行波进位加法器超前进位加法器整数运算加减法乘法无符号数相乘N位乘法数的工作流程N位乘法器改进&#xff1a;硬件资源更快速的乘法 MIPS中的乘法除法 32位除法器流程除法器改进 更快速的除法 MIPS中的除法总结 ALU ALU功能&#xff1a;对a&#xff0c;…...

如何在华为云服务器查看IP地址,及修改服务器登录密码!!!

1.在华为云服务器查看IP地址 (1).第一步&#xff1a; 先找到控制台 (2).第二步&#xff1a; 点击华为云Flexus云服务 (3)第三步&#xff1a; 找到公网IP&#xff0c;就找到华为云服务器IP地址啦。 注意&#xff1a;在操作以上步骤的前提是要已注册华为云账号及购买云服务器…...

JAVA并发编程高级——JDK 新增的原子操作类 LongAdder

LongAdder 简单介绍 前面讲过,AtomicLong通过CAS提供了非阻塞的原子性操作,相比使用阻塞算法的同步器来说它的性能已经很好了,但是JDK开发组并不满足于此。使用AtomicLong 时,在高并发下大量线程会同时去竞争更新同一个原子变量,但是由于同时只有一个线程的CAS操作会成功,…...

常见的基础系统

权限管理系统支付系统搜索系统报表系统API网关系统待定。。。 Java 优质开源系统设计项目 来源&#xff1a;Java 优质开源系统设计项目 | JavaGuide 备注&#xff1a;github和gitee上可以搜索到相关项目...

在 window 系统下安装 Ubuntu (虚拟机)

文章目录 零、Ubuntu 和 Vmware workstation 资源一、下载 Ubuntu二、下载 Vmware Workstation Pro三、安装 Vmware Workstation Pro四、创建虚拟机五、配置 Ubuntu 零、Ubuntu 和 Vmware workstation 资源 如果觉得自己下载 Ubuntu 和 Vmware workstation 麻烦&#xff0c;也…...

鸿蒙开发(NEXT/API 12)【访问控制应用权限管控概述】程序访问控制

默认情况下&#xff0c;应用只能访问有限的系统资源。但某些情况下&#xff0c;应用存在扩展功能的诉求&#xff0c;需要访问额外的系统数据&#xff08;包括用户个人数据&#xff09;和功能&#xff0c;系统也必须以明确的方式对外提供接口来共享其数据或功能。 系统通过访问…...

(10)MATLAB莱斯(Rician)衰落信道仿真1

文章目录 前言一、莱斯分布随机变量二、仿真代码与结果1.仿真代码2.仿真结果画图 后续 前言 首先给出莱斯衰落信道模型&#xff0c;引入了莱斯因子K&#xff0c;并给出莱斯分布的概率密度函数公式。然后导出莱斯分布随机变量的仿真表示式&#xff0c;建立MATLAB仿真代码&#…...

什么是重卡充电桩?

有什么广告&#xff1f;没有广告&#xff0c;纯纯的介绍。 在政策与市场双重驱动下&#xff0c;充电桩市场已经开启加速模式&#xff0c;行业的火苗越烧越旺。同时&#xff0c;随着新能源重卡的广泛普及&#xff0c;重卡充电桩也迎来了新的发展机遇。 此种背景下 &#xff0c…...

模拟实现消息队列(基于SpringBoot实现)

提要&#xff1a;此处的消息队列是仿照RabbitMQ实现&#xff08;参数之类的&#xff09;&#xff0c;实现一些基本的操作&#xff1a;创建/销毁交互机&#xff08;exchangeDeclare&#xff0c;exchangeDelete&#xff09;&#xff0c;队列&#xff08;queueDeclare&#xff0c;…...

C语言:预编译过程的剖析

目录 一.预定义符号和#define定义常量 二.#define定义宏 三.宏和函数的对比 四、#和##运算符 五、条件编译 在之前&#xff0c;我们已经介绍了.c文件在运行的过程图解&#xff0c;大的方面要经过两个方面。 一、翻译环境 1.预处理&#xff08;预编译&#xff09; 2.编译 3…...

算法——单调栈

单调栈&#xff1a; 保持栈内的元素始终递增或递减。 单调递增 待处理数组{1,5,2,5,7,2,8} public void sameyIncrease(int[] nums) {Stack<Integer> stack new Stack<>();for(int i 0; i < nums.length; i) {//当栈空的时候可以直接进栈或者要进栈的数大于…...

LeetCode讲解篇之695. 岛屿的最大面积

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们遍历二维矩阵&#xff0c;如果当前格子的元素为1进行深度优先搜索&#xff0c;将搜索过的格子置为0&#xff0c;防止重复访问&#xff0c;然后对继续深度优先搜索上下左右中为1的格子 题解代码 func maxAr…...

招联2025校招内推倒计时

【投递方式】 直接扫下方二维码&#xff0c;或点击内推官网https://wecruit.hotjob.cn/SU61025e262f9d247b98e0a2c2/mc/position/campus&#xff0c;使用内推码 igcefb 投递&#xff09; 【招聘岗位】 后台开发 前端开发 数据开发 数据运营 算法开发 技术运维 软件测试 产品策…...