C++高性能服务器框架muduo,与配套书籍《Linux多线程服务端编程》解读

本章解读C++开源项目 muduo 代码,与配套书籍《Linux多线程服务端编程》,均来自作者陈硕,是业内比较有名的大神。

目录

    • muduo 源码解读
    • 《Linux多线程服务端编程》笔记
      • 第1章 线程安全的对象生命周期管理
      • 第2章 线程同步精要
      • 第3章 多线程服务器的适用场合与常用编程模型
      • 第4章 C++多线程系统编程精要
      • 第5章 高效的多线程日志
      • 第6章 muduo 网络库简介
      • 第7章 muduo 编程示例
      • 第8章 muduo 网络库设计与实现
      • 第9章 分布式系统工程实践
      • 第10章 C++编译链接模型精要
      • 第11章 反思C++面向对象与虚函数
      • 第12章 C++经验谈

muduo 源码解读

1、开源地址:https://github.com/chenshuo/muduo
2、muduo 是C++多线程服务器框架,支持高并发的高性能服务器。
3、muduo 基础源码与2010年左右完成,最近几年已无重大更新,上一次提交是2022年。

muduo 源码框架
├── base
│ ├── AsyncLogging.h //异步日志。
│ ├── Atomic.h //对原子整型数据的封装(C++11标准已经加入原子数据)。
│ ├── BlockingQueue.h //线程安全的阻塞任务队列,成员:queue_,mutex_,接口:put、take,当队列为空时,条件变量wait等待。
│ ├── BoundedBlockingQueue.h //成员:circular_buffer queue_,与 BlockingQueue 类似,是大小有限制的环形队列。
│ ├── Condition.h //成员:pthread_cond_t pcond_、MutexLock,对条件变量的封装。
│ ├── copyable.h //可复制的基类。
│ ├── CountDownLatch.h //条件变量实现的计数器,计数变为0时唤醒wait。
│ ├── CurrentThread.h //当前线程的一些信息,线程名、线程号、线程调用栈(backtrace)等。
│ ├── Date.h //对日期的封装(年/月/日)。
│ ├── Exception.h //对异常 std::exception 的封装,特点是可以打印调用栈(CurrentThread::stackTrace)。
│ ├── FileUtil.h //文件读写,class ReadSmallFile,class AppendFile 。
│ ├── GzipFile.h //zlib压缩解压缩的封装。
│ ├── LogFile.h //写日志的日志文件。
│ ├── Logging.h //提供 LOG_TRACE、LOG_INFO等记录日志接口。
│ ├── LogStream.h //日志格式化,重载operator<<等。
│ ├── Mutex.h //封装了 class MutexLock,class MutexLockGuard 类,框架内基本都是使用这两个互斥锁类。
│ ├── noncopyable.h //不允许拷贝基类,delete 了复制构造和拷贝构造。
│ ├── ProcessInfo.h //进程信息查询接口,进程号,用户名,主机IP,线程数量,线程状态,可执行文件路径等。
│ ├── Singleton.h //pthread_once 实现的线程安全的单例类。
│ ├── StringPiece.h //字符串操作封装。
│ ├── Thread.h //线程封装,pthread_create 创建线程,bind 指定线程处理函数。
│ ├── ThreadLocal.h //pthread_key_t 线程变量封装,线程私有的全局变量。
│ ├── ThreadLocalSingleton.h //成员:static thread T* t_value,线程单例模式,每个线程都有一个该单例的实例。
│ ├── ThreadPool.h //线程池,创建指定数量的线程,从一个任务队列阻塞地取任务执行,线程安全。
│ ├── Timestamp.h //时间戳,没有使用线程做时间同步,每次调用 gettimeofday 获取当前时间。
│ ├── TimeZone.h //时区的封装。
│ ├── Types.h //包含了几个常用头文件。
│ └── WeakCallback.h //弱回调,传递智能指针弱引用 std::weak_ptr ,回调时如果 lock() 强引用成功则执行回调,否则不执行回调。
└── net
├── Acceptor.h //成员:Socket 、acceptChannel
、NewConnectionCallback,接口:listen、handleRead,接受接入的TCP连接。
├── boilerplate.h
├── Buffer.h //成员:std::vector buffer_、readerIndex_、writerIndex_,对读写内存的封装。
├── Callbacks.h //各种回调函数 std::function 模板定义。
├── Channel.h //成员:readCallback_、writeCallback_、closeCallback_、errorCallback_,fd的事件和回调管理。
├── Connector.h //成员:serverAddr_,channel_,NewConnectionCallback,States,tcp连接器,tcp非阻塞connect操作。
├── Endian.h //字节序转换接口,例:hostToNetwork32。
├── EventLoop.h //成员:poller_,timerQueue_,std::vector pendingFunctors_,接口:线程主循环 loop(处理监听的网络fd、定时器fd,runInLoop 加入的异步任务队列),为外部调用提供接口。
├── EventLoopThread.h //成员:EventLoop* loop_,Thread thread_,创建线程,并把 EventLoop 主循环放在线程里执行。
├── EventLoopThreadPool.h //成员:threads_,vector<EventLoop*> loops_,创建并管理 EventLoopThread 线程池,提供接口返回 EventLoop。
├── http
│ ├── HttpContext.h //成员:HttpRequestParseState state_、HttpRequest request_,接口:parseRequest,解析http请求。
│ ├── HttpRequest.h //成员:Method、Version、path_、headers_,保存http请求的状态信息,提供设置和查询接口。
│ ├── HttpResponse.h //成员:std::map<string, string> headers_、HttpStatusCode statusCode_、string body_,保存http响应状态信息,包括头部、消息体、状态等。
│ └── HttpServer.h //成员:TcpServer server_,httpCallback_,接口:onConnection、onMessage、onRequest,一个提供简单功能的http服务端。
├── InetAddress.h //成员:struct sockaddr_in addr_,struct sockaddr_in6 addr6_,对网络地址的封装,支持ipv4和ipv6。
├── inspect
│ ├── Inspector.h //成员:HttpServer server_,ProcessInspector,PerformanceInspector,SystemInspector,
std::map<string, CommandList> modules_,std::map<string, HelpList> helps_,提供http接口查询系统监测数据。
│ ├── PerformanceInspector.h //基于gperftools的性能监测接口。
│ ├── ProcessInspector.h //进程数据监测接口,进程号,线程,打开的文件描述符等。
│ └── SystemInspector.h //操作系统信息监测接口,系统版本,cpu信息,cpu状态等。
├── poller
│ ├── EPollPoller.h //epoll_wait 监听事件,把 Channel 传入epoll参数,由上层 EventLoop 处理 Channel 的事件。
│ └── PollPoller.h //与 EPollPoller 类似,使用的是 ::poll。
├── Poller.h //抽象类,接口:poll,updateChannel,成员:ChannelMap channels_,EventLoop* ownerLoop_。
├── Socket.h //成员:const int sockfd_,接口:listen、bindAddress、accept,和一些参数设置,是对 socket fd 常用接口的封装。
├── SocketsOps.h//socket 系统调用接口的封装,全局接口。
├── TcpClient.h //成员:ConnectorPtr connector_,TcpConnectionPtr connection_,tcp客户端的封装,接口:connect,disconnect。
├── TcpConnection.h//成员:socket_、channel_、InetAddress localAddr_、peerAddr_、Buffer inputBuffer_、outputBuffer_,TCP连接操作,包括数据的发送和接收。
├── TcpServer.h //成员:std::unique_ptr acceptor_,std::shared_ptr threadPool_,ConnectionMap connections_,tcp服务端,创建 EventLoopThreadPool 线程池,启动监听,把接入的tcp客户端平均分配到线程池。
├── Timer.h //成员:TimerCallback callback_、Timestamp expiration_、double interval_,定义定时器,管理回调、定时触发等。
├── TimerId.h //成员:Timer* timer_、int64_t sequence_,对 Timer 的封装。
├── TimerQueue.h//成员:EventLoop* loop_、const int timerfd_、TimerList timers_、Channel timerfdChannel_,接口:addTimer、cancel,在 EventLoop 线程管理定时器。
└── ZlibStream.h//ZlibInputStream,ZlibOutputStream,输入为zlib压缩数据,输出未压缩数据。

《Linux多线程服务端编程》笔记

前言
本书写于2012年,2013年出版,主要讲述采用C++在x86-64linux上编写多线程TCP网络服务程序的主流技术,也是对作者多年生成环境开发的经验总结。
本书源码参见开源项目 muduo,其是一个基于非阻塞IO和事件驱动的C++网络库,并未涉及UDP和文件存储,和书是同一个作者。
本书所使用的C++版本是2005年之后的C++语言和库,并非C++11及以后的C++版本,所使用的 shared_ptr/bind 等来自 boost 库。
muduo 也多年未做重大更新,是一个老且成熟的库,对于喜欢新技术的朋友来说不太友好。
阅读过程中,不感兴趣的部分(对开发帮助不大)只是大致浏览一遍,没有做笔记。

第1部分 C++多线程系统编程

第1章 线程安全的对象生命周期管理

C++要求程序员自己管理对象的生命周期,在多线程环境下较为困难。
1、对象构造的线程安全:不要在构造函数中注册任何回调;不要在构造函数中泄露this指针。
2、对象析构的线程安全:
(a)线程锁mutex不能在析构时保证多线程安全,锁本身也会在析构时被销毁,在析构中加锁本身就是错误的。
(b)如果同时读写一个class的两个对象,存在死锁可能,比如swap(a,b)和swap(b,a)。
©如果要锁住相同类的多个对象,可以比较mutex对象地址,始终先加锁地址较小的mutex。

3、万能的指针线程安全解决方案:智能指针 shared_ptr。
4、shared_ptr 的线程安全
(a)shared_ptr 是一个类模板,用来管理对象的生命周期。
(b)智能指针的引用计数是线程安全的,但 shared_ptr 对象的多线程读写不安全,需要加锁。
©弱回调:如果对象还存在,则执行它的接口,否则忽略,weak_ptr 弱引用可以实现。

智能指针的知识和应用详见:。

第2章 线程同步精要

一、线程同步原则(作者的建议):
1、尽量避免共享对象,优先共享不可改变对象,共享可修改对象时,必须用同步措施保护对象。
2、使用高级的并发编程构建:TaskQueue、Producer-Consumer Queue、CountDownLatch 等。
3、必须用线程锁时,只用非递归的互斥锁和条件变量,慎用读写锁,不要用信号量。
4、除了使用 atomic 整数外,不自己编写 lock-free 代码。

二、互斥锁(mutex)使用原则
1、用RAII理念封装 mutex 的创建、销毁、加锁、解锁。
2、只用非递归的 metux。
3、不手动调用 lock 和 unlock,交给 Guard 对象管理。
4、每次构造 Guard 对象时,思考已经持有的锁,防止因加锁顺序不同导致的死锁。
5、不使用跨进程的 mutex,加锁和解锁在同一个线程。

三、不使用递归锁
递归锁不容易死锁,方便使用,但出现bug很难排查,比如两个接口A和B都获取锁,A和B会修改同一个容器V,如果A在遍历V时调用了B,B修改了V,则会出现意想不到的问题。
使用非递归锁,出现问题会今早暴露,且方便排查。
(不是不能使用递归锁,只要逻辑够严谨即可,在 ZLToolKit 中几乎都是使用递归锁 std::recursive_mutex)

四、死锁
1、在获取一个锁后没有解锁,又去获取它则会造成死锁,RAII模式下还未离开 Guard 作用域又调用了其他接口获取锁。
2、在两个不同的线程分别连续获取两把锁,如果获取两个锁的顺序不同,则可能会造成死锁。

五、不使用读写锁和信号量
1、读写锁:对性能提升有限;容易在读锁中修改了数据;写锁会造成读锁死锁;使用复杂容易出错;开销比普通锁大。
2、信号量:大意是,条件变量配合互斥锁完全可以替换信号量,且不易用错。

六、封装 MutexLock、MutexLockGuard、Condition
详见 muduo/base

七、sleep 不是同步原语
1、手动 sleep 只能出现在测试程序,不应该出现在生产环境,如果依赖 sleep 做轮询是低效的,业余的做法。
2、正确的线程等待:select/poll/epoll_wait 上等待;条件变量上等待;互斥锁上等待等。

八、普通 mutex 配合 shared_ptr 替换读写锁
1、巧妙的设计,容器定义为智能指针 pmap ,假如是单线程写、多线程读模型。
2、读时获取锁并返回 pmap,引用计数加1,在遍历 pmap 时无锁,读完返回后减1。
3、写时全程持有锁,如果发现 pmap 的引用计数大于1,说明有线程在读,此时不直接改 pmap ,而是拷贝一个新的 pmap 并交换新旧 pmap。
4、此时成员变量持有的是新拷贝的 pmap,修改新的 pmap。读线程在读的一直是那个旧的 pmap。
5、旧的 pmap 怎么释放? 读写执行完后离开作用域,相应的智能指针引用计数都会减1,自然就释放了。
思路:使用 shared_ptr 管理容器,读时use_count加1,写时如果use_count大于1说明有线程在读,复制一份容器,在新复制的上面修改,交换新旧容器。

第3章 多线程服务器的适用场合与常用编程模型

一、多线程服务器模型
1、多线程的价值是为了更好地发挥多核处理器的效能。
2、event loop 线程:reactor模型,基于epoll,网络IO、定时器、异步执行等。
3、线程池:对于没有IO而只有计算任务的线程,使用 event loop 有点浪费,补充方案就是共享任务队列的多个线程组成的线程池(muduo/base/ThreadPool.h)。
4、多线程服务器推荐模型:非阻塞IO + 每个线程一个事件循环+线程池。

二、进程间通信只用TCP
作者罗列了一堆使用TCP的好处,比如可以跨机器使用,可以使用tcpdump抓包分析,可以查看tcp端口占用,可以使用长连接,双向通信等等。

三、单线程服务器
1、程序可能会 fork 子进程,多线程也可以fork,但会让程序变得异常复杂(fork子进程只有一个线程,其他线程都消失了)。
2、限制程序的CPU占用率,其只能占满一个core,不影响其他core给别的进程使用。
3、可以让单个core的性能达到最佳。

四、多线程服务器
1、IO线程:event loop 线程,基于epoll_wait系统调用,也可以处理定时器和异步操作。
2、计算线程:主循环是 blocking queue,阻塞在条件变量上,等待处理任务队列。
3、其他线程:比如日志,时钟等。

第4章 C++多线程系统编程精要

一、多线程编程特点
1、不能依赖任何一个线程的执行速度或通过sleep的方式控制线程执行的先后顺序,跨线程必须有同步措施。
2、muduo 对多线程的封装:muduo::Thread(线程的创建和等待结束),muduo::MutexLock(mutex的创建、销毁、加锁、解锁),muduo::Condtion(条件变量的创建、销毁、等待、通知、广播)。
3、C++标准库大多数泛型算法是线程安全的,因为这些都是无状态纯函数。
4、C++的iostream不是线程安全的,因为可以拆分为多个<<语句;printf是线程安全的,但这等于是调用了全局锁,任何时刻只能一个线程调用printf。
5、一个程序创建线程的数量应小于等于 core 数量,不应该创建过多的线程,否则会增加内核调度的负担,降低整体性能。

二、线程的创建和销毁的守则
1、程序库不应该在未告知的情况下创建自己的线程,程序的每个线程必须是程序员知道并掌控的。
2、用相同的方式创建线程,比如 muduo::Thread,方便管理。
3、main()函数之前不应该启动线程,C++会在main之前完成全局对象的构造,无需考虑并发和线程安全,在此过程中启动线程是不安全的。
4、程序在初始化阶段就创建全部工作线程,在程序运行期间不再创建或销毁线程,因为线程销毁通常是不安全的。
5、线程正常退出只有一种方式:从线程主函数返回,其他pthead_exit、pthread_cancel、抛出异常等都是错误的退出方式。

三、多线程IO
1、socket是双向IO,读和写可以分到两个线程进行,但不能多个线程进行读或写。
2、磁盘IO:每个磁盘配一个线程,因为每个磁盘都有一个操作队列;或者简单点:一个文件只有一个进程的一个线程来读写。

四、多线程中的 fork 和 signal
1、fork 一般不能在多线程调用,因为linux的fork只克隆当前线程,不克隆其他线程。
2、多线程时代 signal 语义复杂,不建议使用。

第5章 高效的多线程日志

文章介绍了日志的功能需求和性能需求,以及muduo日志的实现思路(这里不应花费过多时间,每个框架都有自己的日志封装,功能大同小异)。

第6章 muduo 网络库简介

第7章 muduo 编程示例

第8章 muduo 网络库设计与实现

这三章内容是在介绍 muduo 开源库,我的建议是直接看 muduo 源码,有什么疑问再来看书。

第9章 分布式系统工程实践

一、分布式系统面临的问题
1、 RPC(Remote Procedure Call Protocol)调度超时,无法区分是网络故障,还是对方机器崩溃,或者是上行问题还是下行问题,硬件故障还是软件故障等。
2、分布式系统的负荷均衡问题。
3、分布式系统软件的可靠性,作者罗列了大量测试数据和计算公式,可以做材料素材。
4、软件遇到异常错误重启是在所难免的,服务端程序要保证可以随时重启,操作系统能回收资源,不要使用操作系统都无法回收的资源:如共享内存,全局锁和全局信号量,父子进程共享fd通信等。
5、优雅地重启:停止服务进程心跳,对于短链接,关闭监听端口,不会有新请求到达,对于长连接,客户端主动failover到备用地址或其他服务器。
6、还有一种迁移:先启动新版本的服务进程,然后让旧版本服务进程停止接受新请求,所有新请求导向新进程,一段时间后旧版本进程没有活动请求后,直接kill。

二、分布式系统心跳设计
1、TCP连接心跳的必要性:操作系统崩溃,不会发出FIN;硬件故障导致机器重启,也不会发出FIN;FIN可能丢包。
2、TCP keepalive 不能替代心跳:keepalive 由操作系统负责探测,即便进程死锁或阻塞,操作系统也会正常收发 TCP keepalive。
3、应该使用工作线程收发心跳,防止工作线程死锁或阻塞还在继续收发心跳。
4、与业务消息用同一个连接收发心跳,不要使用单独的心跳连接,避免TCP做业务连接,UDP做心跳连接()。

三、分布式系统部署、监控、进程管理的几重境界
程序员一般不关系这些,感兴趣的可以看看。
1、全手工操作
2、使用零散的自动化脚本和第三方组件
3、自制机群管理系统,集中化配置
4、机群管理与 naming service 结合

第10章 C++编译链接模型精要

C++编译知识,编译优化,想深入了解编译的可以看看。

第11章 反思C++面向对象与虚函数

从muduo源码可以看出,作者不喜欢使用继承与派生、虚函数与多态,原因:
1、引入基类和派生类,带来了灵活性,但代码易读性变差,在几十个类的继承体内绕来绕去确实很费脑筋。
2、继承与派生增加了类之间的耦合性,涉及改动时牵一发动全身(一个事物归于哪个类有时是模糊的,随着需求的改变可能会归于不同的类)。
3、虚函数会破坏二进制文件的兼容性(动态库开发的思路,可以看看作者的观点)。
4、使用 std::function 和 std::bind 替代虚函数回调。

第12章 C++经验谈

作者总结了几个C++开发的知识点,我没仔细看,工作中遇到了可以看看。
1、用异或来交换变量是错误的
2、不要重载全局operator new()
3、带符号整数的除法与余数
4、在单元测试中mock系统调用
5、慎用著名namespace
6、采用有利于版本管理的代码格式
7、再探std:string
8、用sTL algorithm轻松解决几道算法面试题

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

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

相关文章

【【HDMI 方块移动实验 】】

HDMI 方块移动实验 dvi_transmitter_top.v module dvi_transmitter_top(input pclk ,input sys_rst_n ,input pclk_x5 ,input video_hsync ,input video_vsync ,input …

MySQL数据库的基础概念

目录 顾名思义&#xff0c;数据库是用于存储数据的&#xff0c;那这些数据被存储在哪呢&#xff1f; 文件也能存储数据&#xff0c;那在这个基础上&#xff0c;为什么还要搞出一个数据库来存储数据呢&#xff1f; MySQL的客户端登录/退出指令、服务端的启动/关闭指令 数据…

如何查看PHP信息

创建一个 PHP 文件&#xff0c;比如 info.php&#xff0c;在其中添加以下代码&#xff1a; <?php phpinfo(); ?>访问这个文件&#xff08;例如&#xff0c;在浏览器中输入 http://localhost/info.php&#xff09;&#xff0c;它会显示 PHP 的所有配置信息。在这个页面…

【设计模式】之工厂模式

工厂模式 1.介绍 工厂模式&#xff08;创建型模式&#xff09;&#xff0c;是我们最常用的实例化对象模式&#xff0c;是用工厂方法代替new操作的一种模式&#xff1b;在工厂模式中&#xff0c;我们在创建对象时不会对客户端暴露创建逻辑&#xff0c;并且是通过使用一个共同的…

服务器挖矿木马识别与清理

一、什么是挖矿木马 挖矿木马会占用CPU进行超频运算,从而占用主机大量的CPU资源,严重影响服务器上的其他应用的正常运行。黑客为了得到更多的算力资源,一般都会对全网进行无差别扫描,同时利用SSH爆破和漏洞利用等手段攻击主机。部分挖矿木马还具备蠕虫化的特点,在主机被成…

Threejs利用着色器编写动态飞线特效

一、导语 动态飞线特效是可视化数据地图中常见的需求之一&#xff0c;鼠标点击的区块作为终点&#xff0c;从其他区块飞线至点击区块&#xff0c;附带颜色变换或者结合粒子动画 二、分析 利用创建3点来构成贝塞尔曲线&#xff0c;形成线段利用着色器材质来按照线段以及时间…

Go语言学习:第1天

一、为什么开始学go语言 我自己是做测试的&#xff0c;所测试项目使用的是go语言。开始学习go语言的原因有两个&#xff1a;一方面&#xff0c;为了更好的做好工作&#xff1b; 另一方面&#xff0c;为了提高自己的核心竞争力。 二、第1天学习到的内容 2.1 Go是怎么解决包依…

JavaScript——严格检查模式

‘use strict’ &#xff1a; 严格检查模式&#xff0c;预防JavaScript的随意性导致产生的一些问题&#xff08;必须写在JavaScript的第一行&#xff09; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title…

buildadmin:表格中实现详情按钮

其一&#xff1a;创建组件并在当前控制器中引入组件 <!-- 示例核心代码(1/3) --> <!-- 详情组件在此处使用&#xff0c;但显示与否的判断是写在组件内的 --> <Detail /><!-- 引入组件 --> import Detail from ./detail.vue其二&#xff1a;注册按钮 …

ospf 知识总结

ospf 知识总结 一、ospf的概念 - 开放式最短路径优先协议&#xff0c;是广泛使用的一种动态路由协议&#xff0c;它属于链路状态路由协议&#xff0c;是一个内部网关协议&#xff08;IGP&#xff09;&#xff0c;用于在单一自治系统&#xff08;AS&#xff09;内决策路由。 - …

DeepMind大型语言AI模型FunSearch在数学科学中取得新发现

大型语言模型 (LLM) 是有用的助手 – 它们擅长组合概念&#xff0c;并且可以阅读、编写和编码来帮助人们解决问题。但他们能发现全新的知识吗&#xff1f;由于法学硕士已被证明会“幻觉”事实上不正确的信息&#xff0c;因此利用它们来做出可验证的正确发现是一个挑战。 FunSea…

HarmonyOS:使用MindSpore Lite引擎进行模型推理

场景介绍 MindSpore Lite 是一款 AI 引擎&#xff0c;它提供了面向不同硬件设备 AI 模型推理的功能&#xff0c;目前已经在图像分类、目标识别、人脸识别、文字识别等应用中广泛使用。 本文介绍使用 MindSpore Lite 推理引擎进行模型推理的通用开发流程。 基本概念 在进行开…

各技术栈需要掌握的知识

一、前端工程师需要掌握的知识 前端工程师需要掌握的知识主要包括以下几个方面: HTML、CSS和JavaScript:这是前端工程师的基础知识,需要熟练掌握。HTML是网页的骨架,CSS是网页的外观和样式,JavaScript则是实现网页交互效果的关键。响应式设计:随着移动设备的普及,响应…

测试用例设计方法之判定表详解!!

理论部分 判定表是分析和表达多种输入条件下系统执行不同动作的工具&#xff0c;它可以把复杂的逻辑关系和多种 条件组合的情况表达得既具体又明确。 条件桩(Condition Stub)动作桩(Action Stub&#xff09;条件项(Condition Entry&#xff09;动作项(Action Entry&#xff0…

Linux(4)-LAMP

L-LinuxA-apache/nginxM-mysqlp-php 搭建LAMP以及使用discuz搭建论坛网站 安装apache yum install httpd -y // 安装service httpd start // 启动Apache通过netstat -tunlp查看apache运行的端口&#xff0c;然后打开虚拟机ip 80端口能看到以下页面 或者 安装Mysql centOS6…

ECAT【对象字典】

1000h&#xff1a; - wAxisStructID WORD 16#FE12 3S用于检查结构类型的内部变量。 1000 n轴状态 SMC_AXIS_STATE (INT) standstill 轴PLCopen状态机: 0: power_off1: errorstop 2: stopping 3: standstill 4: discrete_motion5: continuous_motion 6:synchronized_mo…

自然数分解 C语言xdoj64

输入说明 一个正整数 n&#xff0c;0<n<30 输出说明 输出n个连续奇数&#xff0c;数据之间用空格隔开&#xff0c;并换行 输入样例 4 输出样例 13 15 17 19 int main() {int n;scanf("%d",&n);if(n % 2 0){//n为偶数int in;//打印数字个数&#xff0c;做循…

【WINCC制作水管水流动画】

&#xff37;&#xff29;&#xff2e;&#xff23;&#xff23;简单制作水管水流动画 详情如下图所示&#xff1a; 1.首先用布化好管道&#xff0c;同时在管道内部画好折线图用以表示水流路径 2.选中折线图调整全局颜色方案 3.选择线条颜色 4.调整线条的线宽和线型 5.效果…

设计模式——组合模式(结构型)

引言 组合模式是一种结构型设计模式&#xff0c; 你可以使用它将对象组合成树状结构&#xff0c; 并且能像使用独立对象一样使用它们。 问题 如果应用的核心模型能用树状结构表示&#xff0c; 在应用中使用组合模式才有价值。 例如&#xff0c; 你有两类对象&#xff1a; ​…

关于Linux你必须知道的五件事

Linux是一种开源操作系统 (OS)。操作系统是直接管理系统硬件和资源&#xff08;如 CPU、内存和存储&#xff09;的软件。操作系统位于应用程序和硬件之间&#xff0c;并在所有软件和执行工作的物理资源之间建立连接。 俄罗斯军方计划用 Astra Linux 取代 Windows&#xff01;为…