mongodb源码分析session接受客户端find命令过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制。
现在继续研究ASIOSession和connection是怎么接受客户端命令的?
mongo/transport/service_state_machine.cpp核心方法有:
enum class State {Created, // The session has been created, but no operations have been performed yetSource, // Request a new Message from the network to handleSourceWait, // Wait for the new Message to arrive from the networkProcess, // Run the Message through the databaseSinkWait, // Wait for the database result to be sent by the networkEndSession, // End the session - the ServiceStateMachine will be invalid after thisEnded // The session has ended. It is illegal to call any method besides// state() if this is the current state.};void _processMessage()
void _sinkCallback()
void _sourceCallback()
void _sourceMessage()
void _sinkMessage()
void _runNextInGuard()
mongo第一条命令状态转变流程是:State::Created 》 State::Source 》State::SourceWait 》 State::Process 》 State::SinkWait 》 State::Source 》State::SourceWait
状态解释:State::Created, //session刚刚创建,但是还没有接受任何命令
State::Source, //去接受客户端新的命令
State::SourceWait, // 等待客户端新的命令
State::Process, // 将接受的命令发送给mongodb数据库
State:: SinkWait, // 等待将命令的执行结果返回给客户端
mongo/transport/service_state_machine.cpp核心方法循环调用的流程图下:
mongo/transport/service_state_machine.cpp核心_runNextInGuard方法主要判断状态State::Source和State::Process,State::Source主要session等待客户端请求find命令,State::Process将命令发送给mongodb数据库, _runNextInGuard代码如下:
void ServiceStateMachine::_runNextInGuard(ThreadGuard guard) {auto curState = state();dassert(curState != State::Ended);// If this is the first run of the SSM, then update its state to Sourceif (curState == State::Created) {curState = State::Source;_state.store(curState);}// Make sure the current Client got set correctlydassert(Client::getCurrent() == _dbClientPtr);try {switch (curState) {case State::Source:LOG(1) << "conca _runNextInGuard State::Source" ;_sourceMessage(std::move(guard));break;case State::Process:LOG(1) << "conca _runNextInGuard State::Process" ;_processMessage(std::move(guard));break;case State::EndSession:LOG(1) << "conca _runNextInGuard State::EndSession" ;_cleanupSession(std::move(guard));break;default:MONGO_UNREACHABLE;}return;} catch (const DBException& e) {log() << "DBException handling request, closing client connection: " << redact(e);}if (!guard) {guard = ThreadGuard(this);}_state.store(State::EndSession);_cleanupSession(std::move(guard));
}
session等待connection请求,状态转变流程:State::Created 》 State::Source 》State::SourceWait,
void ServiceStateMachine::_sourceMessage(ThreadGuard guard) {invariant(_inMessage.empty());invariant(_state.load() == State::Source);LOG(1) << "conca _sourceMessage State::Source";_state.store(State::SourceWait);LOG(1) << "conca _sourceMessage store State::SourceWait";guard.release();auto sourceMsgImpl = [&] {if (_transportMode == transport::Mode::kSynchronous) {MONGO_IDLE_THREAD_BLOCK;return Future<Message>::makeReady(_session()->sourceMessage());} else {invariant(_transportMode == transport::Mode::kAsynchronous);return _session()->asyncSourceMessage();}};sourceMsgImpl().getAsync([this](StatusWith<Message> msg) {if (msg.isOK()) {_inMessage = std::move(msg.getValue());invariant(!_inMessage.empty());}_sourceCallback(msg.getStatus());});
}
_sourceMessage主要处理状态State::Source 》State::SourceWait,等待session接受消息。_session()->asyncSourceMessage()方法session异步等待客户端发送的find命令消息。
如果有find命令到来则调用_sourceCallback(msg.getStatus());_sourceCallback方法代码如下:
void ServiceStateMachine::_sourceCallback(Status status) {// The first thing to do is create a ThreadGuard which will take ownership of the SSM in this// thread.ThreadGuard guard(this);// Make sure we just called sourceMessage();LOG(1) << "conca _sinkMessage State::SinkWait";dassert(state() == State::SourceWait);auto remote = _session()->remote();if (status.isOK()) {_state.store(State::Process);LOG(1) << "conca _sinkMessage store State::Process";// Since we know that we're going to process a message, call scheduleNext() immediately// to schedule the call to processMessage() on the serviceExecutor (or just unwind the// stack)// If this callback doesn't own the ThreadGuard, then we're being called recursively,// and the executor shouldn't start a new thread to process the message - it can use this// one just after this returns.return _scheduleNextWithGuard(std::move(guard),ServiceExecutor::kMayRecurse,transport::ServiceExecutorTaskName::kSSMProcessMessage);} else if (ErrorCodes::isInterruption(status.code()) ||ErrorCodes::isNetworkError(status.code())) {LOG(1) << "Session from " << remote<< " encountered a network error during SourceMessage: " << status;_state.store(State::EndSession);} else if (status == TransportLayer::TicketSessionClosedStatus) {// Our session may have been closed internally.LOG(1) << "Session from " << remote << " was closed internally during SourceMessage";_state.store(State::EndSession);} else {log() << "Error receiving request from client: " << status << ". Ending connection from "<< remote << " (connection id: " << _session()->id() << ")";_state.store(State::EndSession);}// There was an error receiving a message from the client and we've already printed the error// so call runNextInGuard() to clean up the session without waiting._runNextInGuard(std::move(guard));
}
收到find命令转给mongodb执行find命令,状态转变:State::SourceWait》 State::Process,继续调用_scheduleNextWithGuard 》 schedule 调度 》 _runNextInGuard(上面已经存在,反复调用这个方法)。
现在_runNextInGuard方法里面状态State::Process,所以继续调用的方法是_processMessage执行mongodb数据库命令,接受mongodb数据库返回的数据,_processMessage详细代码如下:
void ServiceStateMachine::_processMessage(ThreadGuard guard) {invariant(!_inMessage.empty());LOG(1) << "conca _processMessage";TrafficRecorder::get(_serviceContext).observe(_sessionHandle, _serviceContext->getPreciseClockSource()->now(), _inMessage);auto& compressorMgr = MessageCompressorManager::forSession(_session());_compressorId = boost::none;if (_inMessage.operation() == dbCompressed) {MessageCompressorId compressorId;auto swm = compressorMgr.decompressMessage(_inMessage, &compressorId);uassertStatusOK(swm.getStatus());_inMessage = swm.getValue();_compressorId = compressorId;}networkCounter.hitLogicalIn(_inMessage.size());// Pass sourced Message to handler to generate response.auto opCtx = Client::getCurrent()->makeOperationContext();// The handleRequest is implemented in a subclass for mongod/mongos and actually all the// database work for this request.DbResponse dbresponse = _sep->handleRequest(opCtx.get(), _inMessage);// opCtx must be destroyed here so that the operation cannot show// up in currentOp results after the response reaches the clientopCtx.reset();// Format our response, if we have oneMessage& toSink = dbresponse.response;if (!toSink.empty()) {invariant(!OpMsg::isFlagSet(_inMessage, OpMsg::kMoreToCome));invariant(!OpMsg::isFlagSet(toSink, OpMsg::kChecksumPresent));// Update the header for the response message.toSink.header().setId(nextMessageId());toSink.header().setResponseToMsgId(_inMessage.header().getId());if (OpMsg::isFlagSet(_inMessage, OpMsg::kChecksumPresent)) {
#ifdef MONGO_CONFIG_SSLif (!SSLPeerInfo::forSession(_session()).isTLS) {OpMsg::appendChecksum(&toSink);}
#elseOpMsg::appendChecksum(&toSink);
#endif}// If the incoming message has the exhaust flag set and is a 'getMore' command, then we// bypass the normal RPC behavior. We will sink the response to the network, but we also// synthesize a new 'getMore' request, as if we sourced a new message from the network. This// new request is sent to the database once again to be processed. This cycle repeats as// long as the associated cursor is not exhausted. Once it is exhausted, we will send a// final response, terminating the exhaust stream._inMessage = makeExhaustMessage(_inMessage, &dbresponse);_inExhaust = !_inMessage.empty();networkCounter.hitLogicalOut(toSink.size());if (_compressorId) {auto swm = compressorMgr.compressMessage(toSink, &_compressorId.value());uassertStatusOK(swm.getStatus());toSink = swm.getValue();}TrafficRecorder::get(_serviceContext).observe(_sessionHandle, _serviceContext->getPreciseClockSource()->now(), toSink);_sinkMessage(std::move(guard), std::move(toSink));LOG(1) << "conca _processMessage _sinkMessage";} else {_state.store(State::Source);_inMessage.reset();LOG(1) << "conca _processMessage store(State::Source)";return _scheduleNextWithGuard(std::move(guard),ServiceExecutor::kDeferredTask,transport::ServiceExecutorTaskName::kSSMSourceMessage);}
}
DbResponse dbresponse = _sep->handleRequest(opCtx.get(), _inMessage)将接受的find命令发送给mongodb数据库,mongodb数据库执行逻辑,返回响应结果。
最后调用_sinkMessage,将mongodb结果数据发送给客户端。状态转变流程:State::Process 》 State::SinkWait
void ServiceStateMachine::_sinkMessage(ThreadGuard guard, Message toSink) {// Sink our response to the clientinvariant(_state.load() == State::Process);LOG(1) << "conca _sinkMessage State::Source";_state.store(State::SinkWait);LOG(1) << "conca _sinkMessage store State::SinkWait";guard.release();auto sinkMsgImpl = [&] {if (_transportMode == transport::Mode::kSynchronous) {// We don't consider ourselves idle while sending the reply since we are still doing// work on behalf of the client. Contrast that with sourceMessage() where we are waiting// for the client to send us more work to do.return Future<void>::makeReady(_session()->sinkMessage(std::move(toSink)));} else {invariant(_transportMode == transport::Mode::kAsynchronous);return _session()->asyncSinkMessage(std::move(toSink));}};sinkMsgImpl().getAsync([this](Status status) { _sinkCallback(std::move(status)); });
}
_session()->asyncSinkMessage(std::move(toSink))发送结果给客户端,成功之后继续调用_sinkCallback。
void ServiceStateMachine::_sinkCallback(Status status) {// The first thing to do is create a ThreadGuard which will take ownership of the SSM in this// thread.ThreadGuard guard(this);LOG(1) << "conca _sinkCallback State::SinkWait";dassert(state() == State::SinkWait);// If there was an error sinking the message to the client, then we should print an error and// end the session. No need to unwind the stack, so this will runNextInGuard() and return.//// Otherwise, update the current state depending on whether we're in exhaust or not, and call// scheduleNext() to unwind the stack and do the next step.if (!status.isOK()) {log() << "Error sending response to client: " << status << ". Ending connection from "<< _session()->remote() << " (connection id: " << _session()->id() << ")";_state.store(State::EndSession);return _runNextInGuard(std::move(guard));} else if (_inExhaust) {_state.store(State::Process);LOG(1) << "conca _sinkCallback store State::Process";return _scheduleNextWithGuard(std::move(guard),ServiceExecutor::kDeferredTask |ServiceExecutor::kMayYieldBeforeSchedule,transport::ServiceExecutorTaskName::kSSMExhaustMessage);} else {_state.store(State::Source);LOG(1) << "conca _sinkCallback store State::Source";return _scheduleNextWithGuard(std::move(guard),ServiceExecutor::kDeferredTask |ServiceExecutor::kMayYieldBeforeSchedule,transport::ServiceExecutorTaskName::kSSMSourceMessage);}
}
session发完消息之后, State::SinkWait 》 State::Source 到此位置这个find命令已经完成,_scheduleNextWithGuard后面继续等待新的命令到来。
上面find命令打印日志如下:
2025-04-24T21:24:02.580+0800 D1 NETWORK [conn1] conca _sourceMessage State::Source
2025-04-24T21:24:02.581+0800 D1 NETWORK [conn1] conca _sourceMessage store State::SourceWait
2025-04-24T21:24:09.957+0800 D1 NETWORK [conn1] conca _sinkMessage State::SinkWait
2025-04-24T21:24:09.957+0800 D1 NETWORK [conn1] conca _sinkMessage store State::Process
2025-04-24T21:24:09.958+0800 D1 NETWORK [conn1] conca func end
2025-04-24T21:24:09.961+0800 D1 NETWORK [conn1] conca guard.release() end
2025-04-24T21:24:09.962+0800 D1 NETWORK [conn1] conca _runNextInGuard State::Process
2025-04-24T21:24:09.963+0800 D1 NETWORK [conn1] conca _processMessage
2025-04-24T21:24:09.964+0800 D1 NETWORK [conn1] conca _processMessage _sep->handleRequest
2025-04-24T21:24:09.965+0800 I SHARDING [conn1] Marking collection db.user as collection version: <unsharded>
2025-04-24T21:24:09.969+0800 I COMMAND [conn1] command db.user appName: "MongoDB Shell" command: find { find: "user", filter: {}, lsid: { id: UUID("7ae50c73-fcc6-4d15-8a80-7f2bf2192e0f") }, $db: "db" } planSummary: COLLSCAN keysExamined:0 docsExamined:6 cursorExhausted:1 numYields:0 nreturned:6 reslen:421 locks:{ ReplicationStateTransition: { acquireCount: { w: 1 } }, Global: { acquireCount: { r: 1 } }, Database: { acquireCount: { r: 1 } }, Collection: { acquireCount: { r: 1 } }, Mutex: { acquireCount: { r: 1 } } } storage:{ data: { bytesRead: 416 } } protocol:op_msg 3ms
2025-04-24T21:24:09.975+0800 D1 NETWORK [conn1] conca _processMessage _sep->handleRequest dbresponse
2025-04-24T21:24:09.978+0800 D1 NETWORK [conn1] conca _processMessage !toSink.empty()
2025-04-24T21:24:09.978+0800 D1 NETWORK [conn1] conca _processMessage makeExhaustMessage
2025-04-24T21:24:09.979+0800 D1 NETWORK [conn1] conca _processMessage TrafficRecorder::get(_serviceContext) .observe
2025-04-24T21:24:09.980+0800 D1 NETWORK [conn1] conca _processMessage _sinkMessage
2025-04-24T21:24:09.981+0800 D1 NETWORK [conn1] conca _sinkMessage State::Source
2025-04-24T21:24:09.986+0800 D1 NETWORK [conn1] conca _sinkMessage store State::SinkWait
2025-04-24T21:24:09.986+0800 D1 NETWORK [conn1] conca _sinkCallback State::SinkWait
2025-04-24T21:24:09.987+0800 D1 NETWORK [conn1] conca _sinkCallback store State::Source
2025-04-24T21:24:09.988+0800 D1 NETWORK [conn1] conca func end
2025-04-24T21:24:09.989+0800 D1 NETWORK [conn1] conca guard.release() end
2025-04-24T21:24:09.990+0800 D1 NETWORK [conn1] conca_serviceExecutor->schedule(std::move(func), flags, taskName)
2025-04-24T21:24:09.991+0800 D1 NETWORK [conn1] conca_serviceExecutor->schedule(std::move(func), flags, taskName)
2025-04-24T21:24:09.992+0800 D1 NETWORK [conn1] conca _runNextInGuard State::Source
2025-04-24T21:24:09.993+0800 D1 NETWORK [conn1] conca _sourceMessage State::Source
2025-04-24T21:24:09.994+0800 D1 NETWORK [conn1] conca _sourceMessage store State::SourceWait
总结:
mongo第一条命令流程是:State::Created 》 State::Source 》State::SourceWait 》 State::Process 》 State::SinkWait 》 State::Source 》State::SourceWait
mongo第二条命令流程是: State::Process 》 State::SinkWait 》 State::Source 》State::SourceWait
mongo第三条命令流程是: State::Process 》 State::SinkWait 》 State::Source 》State::SourceWait
相关文章:

mongodb源码分析session接受客户端find命令过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制。 现在继续研究ASIOSession和connection是怎么接受客户端命令的? mongo/transport/service_state_machine.cpp核心方法有…...
Netty 实战篇:为自研 RPC 框架加入异步调用与 Future 支持
我们在上篇实现了一个轻量级 RPC 框架,现在要进一步优化 —— 加入异步响应支持,让 RPC 通信变得真正高效、非阻塞、支持并发。 一、为什么需要异步调用? 上篇的 RPC 框架是“同步阻塞”的: 每次发送请求后,必须等待服…...
python37天打卡
知识点回顾: 过拟合的判断:测试集和训练集同步打印指标 模型的保存和加载 仅保存权重 保存权重和模型 保存全部信息checkpoint,还包含训练状态 早停策略 作业:对信贷数据集训练后保存权重,加载权重后继续训练50轮&am…...

变焦位移计:机器视觉如何克服人工疲劳与主观影响?精准对结构安全实时监测
变焦视觉位移监测与人工监测的对比 人工监测是依靠目测检查或借助于全站仪,水准仪,RTK等便携式仪器测量得到的信息,但是随着整个行业的发展,传统的人工监测方法已经不能满足监测需求,从人工监测到自动化监测已是必然趋…...
嵌入式硬件篇---Ne555定时器
文章目录 前言1. 基本概述类型功能封装形式2. 引脚功能(DIP-8 封装)内部结构阈值电压两种工作模式4. 主要特性优点:缺点:5. 典型应用场景定时控制脉冲生成检测与触发信号处理6. 关键参数速查表前言 本文简单介绍了Ne555定时器(多谐振荡器/定时器)。DIP与SOP封装。 1. 基…...

【Axure结合Echarts绘制图表】
1.绘制一个矩形,用于之后存放图表,将其命名为test: 2.新建交互 -> 载入时 -> 打开链接: 3.链接到URL或文件路径: 4.点击fx: 5.输入: javascript: var script document.createEleme…...

使用web3工具结合fiscobcos网络部署调用智能合约
借助 web3 工具,在 FISCO BCOS 网络上高效部署与调用智能合约,解锁区块链开发新体验。 搭建的区块链网络需要是最新的fiscobcos3.0,最新的才支持web3调用 现在分享踩坑经验,希望大家点赞 目录 1.搭建fiscobcos节点(3.…...

Oracle/openGauss中,DATE/TIMESTAMP与数字日期/字符日期比较
ORACLE 运行环境 openGauss 运行环境 0、前置知识 ORACLE:DUMP()函数用于返回指定表达式的数据类型、字节长度及内部存储表示的详细信息 SELECT DUMP(123) FROM DUAL; -- Typ2 Len3: 194,2,24 SELECT DUMP(123) FROM DUAL;-- Typ96 Len3: 49,50,51 -- ASCII值&am…...
Datatable和实体集合互转
1.使用已废弃的 JavaScriptSerializer,且反序列化为弱类型 ArrayList。可用但不推荐。 using System; using System.Collections; using System.Collections.Generic; using System.Data; using System.Linq; using System.Reflection; using System.Web; using Sy…...
Win11切换JDK版本批处理脚本
维护的老项目jdk1.8,新项目开发采用jdk21,所以寻找类似nvm的软件,都不太满意,最后还是决定采用写一个脚本算了,先不折腾了。 1、创建switch_jdk.bat文件 2、把如下内容复制进行 echo off chcp 65001 >nul setloc…...

爬虫学习-Scrape Center spa6 超简单 JS 逆向
关卡 spa6 电影数据网站,无反爬,数据通过 Ajax 加载,数据接口参数加密且有时间限制,适合动态页面渲染爬取或 JavaScript 逆向分析。 首先抓包发现get请求的参数token有加密。 offset表示翻页,limit表示每一页有多少…...
对数的运算困惑
难点总结 学生在对数运算中的难点分析: 一、不理解对数,不会用对数公式或错用对数公式 ①对数 l o g 2 3 log_23 log23和指数幂 2 3 2^3 23一样,也就是个实数而已,所以其也会有加减乘除乘方开方等运算; 比如 2 2 + l o g 2 3 = 2 2 ⋅ 2 l o g 2 3 = 4 ⋅ 3 = 12 2^{2…...
C++ 图像处理库 CxImage 简介 (迁移至OpenCV)
文章目录 核心功能特点局限性与替代方案常用方法构造函数从数组创建图像访问属性访问像素点Windows平台支持 常用方法迁移至OpenCV CxImage 是一款功能强大的图像处理类库,主要用于 Windows 平台的图像处理任务。它支持多种图像格式的加载、保存、编辑及特效处理&am…...
linux系统与shell 笔记
Linux 系统 Linux 是一种开源的操作系统内核,基于 Unix 设计,具有多用户、多任务、高稳定性和安全性的特点。它广泛应用于服务器、嵌入式设备和个人计算机领域。Linux 系统的核心组件包括内核、系统库、工具链和用户界面(如命令行或图形界面…...

尚硅谷redis7 86 redis集群分片之3主3从集群搭建
86 redis集群分片之3主集群搭建 3主3从redis集群配置 找3台真实虚拟机,各自新建 mķdir -p /myredis/cluster 新建6个独立的redis实例服务 IP:192.168.111.175端口6381/端口6382 vim /myredis/cluster/redisCluster6381.conf bind 0.0.0.0 daemonize yes protected-mode no …...
Kaggle-Predict Calorie Expenditure-(回归+xgb+cat+lgb+模型融合+预测结果)
Predict Calorie Expenditure 题意: 给出每个人的基本信息,预测运动后的卡路里消耗值。 数据处理: 1.构造出人体机能、运动相关的特征值。 2.所有特征值进行从新组合,注意唯独爆炸 3.对连续信息分箱变成离散 建立模型&#x…...

【解决办法】Git报错error: src refspec main does not match any.
在命令行中使用 Git 进行 git push -u origin main 操作时遇到报错error: src refspec main does not match any。另一个错误信息是:error: failed to push some refs to https://github.com/xxx/xxx.git.这是在一个新设备操作时遇到的问题,之前没有注意…...
React与Vue的内置指令对比
React 与 Vue 在内置指令的设计理念和实现方式上有显著差异。Vue 提供了一套丰富的模板指令系统,而 React 更倾向于通过原生 JavaScript 语法和 JSX 实现类似功能。以下是两者的核心对比: 一、条件渲染 Vue 使用 “v-if”/ “v-else” 指令,…...

2025年5月24号高项综合知识真题以及答案解析(第1批次)
2025年5月24号高项综合知识真题以及答案解析...

【NATURE氮化镓】GaN超晶格多沟道场效应晶体管的“闩锁效应”
2025年X月X日,布里斯托大学的Akhil S. Kumar等人在《Nature Electronics》期刊发表了题为《Gallium nitride multichannel devices with latch-induced sub-60-mV-per-decade subthreshold slopes for radiofrequency applications》的文章,基于AlGaN/GaN超晶格多通道场效应晶…...

Ubuntu24.04换源方法(新版源更换方式,包含Arm64)
一、源文件位置 Ubuntu24.04的源地址配置文件发生改变,不再使用以前的sources.list文件,升级24.04之后,而是使用如下文件 /etc/apt/sources.list.d/ubuntu.sources二、开始换源 1. 备份源配置文件 sudo cp /etc/apt/sources.list.d/ubunt…...

26 C 语言函数深度解析:定义与调用、返回值要点、参数机制(值传递)、原型声明、文档注释
1 函数基础概念 1.1 引入函数的必要性 在《街霸》这类游戏中,实现出拳、出脚、跳跃等动作,每项通常需编写 50 - 80 行代码。若每次调用都重复编写这些代码,程序会变得臃肿不堪,代码可读性与维护性也会大打折扣。 为解决这一问题&…...

彻底理解一个知识点的具体步骤
文章目录 前言一、了解概念(是什么)二、理解原理(为什么)三、掌握方法(怎么用) 四、动手实践(会用)五、类比拓展(迁移能力)六、总结归纳(融会贯通…...
FFmpeg 时间戳回绕处理:保障流媒体时间连续性的核心机制
FFmpeg 时间戳回绕处理:保障流媒体时间连续性的核心机制 一、回绕处理函数 /** * Wrap a given time stamp, if there is an indication for an overflow * * param st stream // 传入一个指向AVStream结构体的指针,代表流信息 * pa…...

yolov8改进模型
YOLOv8 作为当前 YOLO 系列的最新版本,已经具备出色的性能。若要进一步改进,可以从网络架构优化、训练策略增强、多任务扩展和部署效率提升四个方向入手。以下是具体改进思路和实现示例: 1. 网络架构优化 (1) 骨干网络增强 引入 Transform…...
PostgreSQL日常运维
目录 一、PostgreSQL基础操作 1.1 登录数据库 1.2 数据库管理 1.3 数据表操作 二、数据备份与恢复 2.1 备份工具pg_dump 2.2 恢复工具pg_restore与psql 2.3 备份策略建议 三、模式(Schema) 3.1 模式的核心作用 3.2 模式操作全流程 四、远程连…...

<< C程序设计语言第2版 >> 练习 1-23 删除C语言程序中所有的注释语句
1. 前言 本篇文章介绍的是实现删除C语言源文件中所有注释的功能.希望可以给C语言初学者一点参考.代码测试并不充分, 所以肯定还有bug, 有兴趣的同学可以改进. 原题目是: 练习1-23 编写一个删除C语言程序中所有的注释语句. 要正确处理带引号的字符串与字符常量. 在C语言中, 注释…...

Fluence (FLT) 2026愿景:RWA代币化加速布局AI算力市场
2025年5月29日,苏黎世 - Fluence,企业级去中心化计算平台,荣幸地揭开其2026愿景的面纱,并宣布将于6月1日起启动四大新举措。 Fluence 成功建立、推出并商业化了其去中心化物理基础设施计算网络(DePIN)&…...

如何撰写一篇优质 Python 相关的技术文档 进阶指南
💝💝💝在 Python 项目开发与协作过程中,技术文档如同与团队沟通的桥梁,能极大提高工作效率。但想要打造一份真正实用且高质量的 Python 技术文档类教程,并非易事,需要在各个环节深入思考与精心打…...
选择if day5
5.scanf(“空白符”) 空白符作用表示匹配任意多个空白符 进入了内存缓冲区(本质就是一块内存空间) 6.scanf读取问题: a.遇到非法字符读取结束 2. %*d * 可以跳过一个字符 eg:%d%*d%d 读取第一和第三个字符…...