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

muduo源码阅读:linux timefd定时器

⭐timerfd

timerfd 是Linux一个定时器接口,它基于文件描述符工作,并通过该文件描述符的可读事件进行超时通知。可以方便地与select、poll和epoll等I/O多路复用机制集成,从而在没有处理事件时阻塞程序执行,实现高效的零轮询编程模型。

🟠timerfd_create

创建一个新的定时器对象,并返回一个与其关联的文件描述符。

#include <sys/timerfd.h>
int timerfd_create(int clockid,int flags);

clockid:定时器所依据的时间基准。

CLOCK_REALTIME/CLOCK_MONOTONIC(含义见下文)。

flags:控制定时器文件描述符的行为,可以是0或多个以下标志通过位或(|)组合而成:

TFD_NONBLOCK: 设置为非阻塞模式,使得读取操作立即返回而不是等待直到有数据可读。

TFD_CLOEXEC: 设置执行新程序时自动关闭文件描述符的标志,这可以防止子进程中继承不必要的文件描述符(子进程不继承父进程的定时器文件描述符)。

系统实时时间 (CLOCK_REALTIME)

系统实时时间指的是从一个固定的时间点(通常是1970年1月1日UTC,也称为Unix纪元)到现在的总时间。这个时间是可以通过系统设置或网络时间协议(NTP)进行调整。

使用 CLOCK_REALTIME 获取的时间可以被操作系统或其他软件手动更改,例如当系统管理员手动调整系统时钟或自动同步时间时。如果应用程序依赖于 CLOCK_REALTIME 来计算事件之间的时间差,那么这些计算可能会因为系统时间的突然跳跃变得不准确。

单调递增的时间 (CLOCK_MONOTONIC)

单调递增的时间通常是从系统启动时开始计数,并且会持续增加直到系统关闭。与CLOCK_REALTIME 不同的是,CLOCK_MONOTONIC 不受系统时间的手动调整或自动同步的影响。

使用 CLOCK_MONOTONIC 可以确保获得的时间值总是向前移动,不会出现向后跳跃的情况。因此,它非常适合用来测量时间段。

🟠timerfd_settime

启动或停止由timerfd_create创建的定时器,并可以设置其初始时间和间隔时间。

#include <sys/timerfd.h>
int timerfd_settime(int ufd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);

ufd: 由timerfd_create返回的文件描述符。

flags: 设置为0表示相对定时器,即从当前时间开始计时;设置为TFD_TIMER_ABSTIME则表示绝对定时器,即按照指定的时间点来触发。

new_value:指向包含初始到期时间和后续间隔时间的结构体指针。

old_value: 如果不为NULL,则指向一个用于接收旧的定时器值的结构体。

返回值:成功时返回0;失败时返回-1并设置相应的错误号。

struct timespec{time_t tv_sec;                /* Seconds */long   tv_nsec;               /* Nanoseconds */
};
struct itimerspec {struct timespec it_interval;  /* Interval for periodic timer */struct timespec it_value;     /* Initial expiration */
};

it_value是首次超时时间,需要填写从clock_gettime获取的时间,并加上要超时的时间。 it_interval是后续周期性超时时间,是多少时间就填写多少。注意一个容易犯错的地方:tv_nsec加上去后一定要判断是否超出1000000000(如果超过要秒加一),否则会设置失败。

🟠clock_gettime
#include <time.h>
int clock_gettime(clockid_t clk_id, struct timespec *tp);

clockid_t clk_id 是时钟 ID,常用的选项包括 CLOCK_REALTIME 和 CLOCK_MONOTONIC。

CLOCK_REALTIME 提供的是系统实时时间,可能会因为系统时间调整而发生跳跃。
CLOCK_MONOTONIC 提供单调递增的时间,适合用于测量时间间隔。
struct timespec *tp 是一个指向 timespec 结构体的指针,用于存储获取到的时间信息。

第三个参数设置超时时间,如果为0则表示停止定时器。定时器设置超时方法:

设置超时时间是需要调用clock_gettime获取当前时间,如果是绝对定时器,那么需要获取CLOCK_REALTIME,在加上要超时的时间。如果是相对定时器,要获取CLOCK_MONOTONIC时间。

定时器代码实例:

#define _GNU_SOURCE
#include<sys/timerfd.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<time.h>
void print_itimerspec(struct itimerspec *new_value) {printf("Initial expiration: sec: %ld nsec: %ld\n", new_value->it_value.tv_sec, new_value->it_value.tv_nsec);printf("Interval: sec: %ld nsec: %ld\n", new_value->it_value.it_interval.tv_sec, new_value->it_value.it_interval.tv_nsec);
}
int main() {struct itimerspec new_value;int tfd;//创建一个新的定时器对象tfd = timerfd_create(CLOCK_MONOTONIC, 0);if (tfd == -1) {perror("timerfd_create");exit(EXIT_FAILURE);}//设置定时器参数//首次超时时间为3秒后new_value.it_value.tv_sec = 3;new_value.it_value.tv_nsec = 0;// 后续每隔2秒触发一次new_value.it_interval.tv_sec = 2;new_value.it_interval.tv_nsec = 0;print_itimerspec(&new_value);// 启动定时器if (timerfd_settime(tfd, 0, &new_value, NULL) == -1) {perror("timerfd_settime");close(tfd);exit(EXIT_FAILURE);}// 循环读取定时器事件uint64_t exp;ssize_t s;while((s = read(tfd, &exp, sizeof(uint64_t))) != sizeof(uint64_t)) {if (s != -1) {fprintf(stderr, "Error reading timerfd\n");break;}if (errno == EINTR)continue;perror("read");break;}printf("Timer expired %llu times\n", exp);close(tfd);return 0;
}

read函数可以读timerfd,读的内容为uint_64,表示超时次数。

❓补充:什么是零轮询编程模型?

零轮询编程模型是一种高效处理I/O操作的方法,旨在避免传统轮询(polling)带来的CPU资源浪费。

传统的轮询会周期性地检查I/O设备是否准备好进行数据传输,可能导致大量的CPU时间被消耗在无意义的检查上。

相比之下,零轮询编程模型利用了操作系统提供的机制(select/poll/epoll等),允许程序在等待I/O事件时进入阻塞状态,即不占用CPU资源,直到有实际的I/O事件发生才会唤醒程序进行处理。这种模型通过减少或消除不必要的检查循环。

❓补充:timerfd、eventfd、signalfd分别有什么用?

timerfd、eventfd、signalfd配合epoll使用的场景,共同工作以实现一个不需要主动轮询的环境。

timerfd 提供了一个基于文件描述符的定时器接口,可以通过文件描述符的可读事件来通知超时。

eventfd 是一种用于进程间或线程间事件通知的机制,它提供了一个文件描述符,可以用来执行简单的事件计数。

signalfd 允许信号的接收通过文件描述符进行,这样就可以将信号处理集成到文件描述符的多路复用中。

epoll 则是一个I/O多路复用的接口,能够监控大量文件描述符的集合,当某个文件描述符准备好进行I/O操作时,就返回通知给应用程序。

补充:把定时器文件描述符设置为非阻塞模式和阻塞模式有什么区别,举例说明?和select/poll/epoll集成时,应该设置为阻塞还是非阻塞?为什么?

(1)非阻塞模式与阻塞模式的区别

非阻塞模式(通过设置 TFD_NONBLOCK 标志):当尝试从一个非阻塞的定时器文件描述符读取数据时,如果当前没有定时器到期事件可供读取,read 调用会立即返回。程序可以在不等待I/O操作完成的情况下继续执行其他任务。

阻塞模式:在默认情况下(即未设置 TFD_NONBLOCK),对定时器文件描述符进行读操作时,如果当前没有定时器到期事件可供读取,调用线程会被挂起,直到有数据可读为止。这允许程序在等待I/O操作完成期间节省CPU资源,但同时也会导致线程暂时不可用于处理其他任务。

和 select/poll/epoll 集成时的选择

在使用 select、poll 或 epoll 等机制管理多个文件描述符时,推荐将定时器文件描述符设置为 非阻塞模式。

因为这些机制本身已经提供了等待I/O就绪的功能。当将文件描述符设置为非阻塞模式时,可以避免在轮询中出现不必要的阻塞。例如使用 epoll 监控定时器文件描述符,当定时器到期时,epoll_wait 返回,由于定时器文件描述符处于非阻塞模式,可以立即尝试读取而不担心阻塞问题,然后根据需要执行相应的处理逻辑。这样确保应用能够高效地响应各种I/O事件,不会因为某个特定的操作被阻塞而导致整体性能下降(具体解释看补充问题)

❓补充:如果定时器文件描述符设置为阻塞模式会发生什么情况?

当定时器文件描述符使用阻塞模式,并使用epoll监听时,可能会导致应用程序在处理定时器事件时被阻塞,进而影响整体性能,使其他I/O事件无法及时得到处理。

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/epoll.h> 
#include <time.h> 
#include <unistd.h> 
#include <fcntl.h> 
#define MAX_EVENTS 10 
int main() { int epoll_fd = epoll_create1(0); if (epoll_fd == -1) { perror("epoll_create1"); return 1; } // 创建定时器文件描述符 int timer_fd = timerfd_create(CLOCK_MONOTONIC, 0); if (timer_fd == -1) { perror("timerfd_create"); return 1; } // 设置定时器 struct itimerspec new_value; new_value.it_interval.tv_sec  = 5; new_value.it_interval.tv_nsec  = 0; new_value.it_value.tv_sec  = 5; new_value.it_value.tv_nsec  = 0; if (timerfd_settime(timer_fd, 0, &new_value, NULL) == -1) { perror("timerfd_settime"); return 1; } // 将定时器文件描述符添加到epoll实例中 struct epoll_event ev, events[MAX_EVENTS]; ev.events  = EPOLLIN; ev.data.fd  = timer_fd; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, timer_fd, &ev) == -1) { perror("epoll_ctl: timer_fd"); return 1; } while (1) { int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); if (nfds == -1) { perror("epoll_wait"); return 1; } for (int i = 0; i < nfds; i++) { if (events[i].data.fd  == timer_fd) { // 由于定时器文件描述符是阻塞模式,这里可能会阻塞 uint64_t expirations; ssize_t s = read(timer_fd, &expirations, sizeof(uint64_t)); if (s!= sizeof(uint64_t)) { perror("read"); return 1; } printf("Timer expired %lu times\n", expirations); } } } close(timer_fd); close(epoll_fd); return 0; 
} 

阻塞模式下,当对定时器文件描述符执行readwrite等操作时,如果操作不能立即完成,进程会进入睡眠状态,等待操作条件满足。这就导致应用程序在这个操作上被阻塞,无法继续执行后续代码,包括处理其他I/O事件。

在Linux内核中,每个文件描述符都有一个对应的文件对象,文件对象中包含了与该文件描述符相关的操作函数集合。对于定时器文件描述符,当执行read操作时,内核会检查定时器的状态和相关的缓冲区。如果缓冲区没有数据,内核会将当前进程加入到等待队列中,并将进程状态设置为睡眠状态,直到定时器到期并产生数据,或者发生其他可以满足read操作的条件。这种机制是为了确保read操作能够正确完成,但在多I/O事件处理的场景下,会导致其他 I/O 事件延迟处理:主线程或事件循环被挂起,网络套接字、文件操作等事件无法及时响应

❓上一个问题的补充:为什么要使用read读取定时器的内核缓冲区?为什么数据会存在定时器的内核缓冲区?

定时器文件描述符为何需要 read 操作?

内核缓冲区的数据来源

定时器文件描述符(如 Linux 的 timerfd)通过 timerfd_create 创建时,内核会为其维护一个计数器缓冲区。当定时器到期时,内核会向该缓冲区写入一个 8 字节的无符号整数,表示自上次读取后定时器触发的次数。(这就是定时器可读事件的本质)。

uint64_t expirations;
read(timer_fd, &expirations, sizeof(expirations));

若不读取,缓冲区会持续累积到期次数,导致后续 epoll_wait误判为"持续就绪"。

为什么检测到定时器文件描述符就绪时,需要通过read来读取定时器文件描述符?

  • 清除就绪状态:读取后重置内核缓冲区,避免 epoll_wait 重复触发。
  • 获取触发次数:通过读取的整数值,可统计定时器到期次数 (适用于周期性定时器)。
  • 避免数据堆积:长期不读取可能导致缓冲区溢出或逻辑错误。
上一个问题的补充:什么时候read定时器文件描述符会阻塞?

定时器文件描述符的缓冲区设计为“有数据时触发读就绪”,因此在正常逻辑中,epoll_wait 返回定时器就绪时,缓冲区应已有数据,此时 read 操作应立刻成功。但以下情况可能导致阻塞:

假设定时器到期时,内核触发超时事件并准备向文件描述符的缓冲区写入超时次数(uint64_t 类型数据).

内核检测到定时器到期,将事件标记为就绪并唤醒 epoll_wait
在写入缓冲区的过程中(如正在更新计数器),发生线程/进程上下文切换。
用户线程从 epoll_wait 返回后,立即调用 read,但此时内核尚未完成缓冲区数据的写入。

read 操作因缓冲区无数据而阻塞(若文件描述符未设置为非阻塞模式),或返回EAGAIN(非阻塞模式)。

类比: 多线程环境下“先通知后执行”的竞态,例如生产者-消费者模型中,消费者收到通知但数据尚未生产完毕。

解决方案:设置为非阻塞模式,通过fcntl(fd, F_SETFL, O_NONBLOCK) 避免 read 阻塞。

最佳实践

  • 非阻塞读取:所有通过 epoll 监听的文件描述符均设置为非阻塞模式。
  • 事件处理原子化在单次 epoll_wait 返回后,批量处理所有就绪事件,避免穿插阻塞调用。

定时器文件描述符的阻塞模式会破坏事件驱动架构的异步性,内核缓冲区的数据读取机制是定时触发的核心逻辑。通过非阻塞模式 + 严格的数据读取,可确保系统的高效性和可靠性。理解这一机制对设计高并发服务(如 Web 服务器、实时交易系统)至关重要。

❓上一个问题的补充:如果不使用timerfd实现定时器,应该怎么实现定时器?

定时器的替代方案

若需避免 read 操作,可结合信号(如 SIGEV_THREAD用户态定时器队列(如 libevent 的定时器堆),但需权衡精度和性能。

相关文章:

muduo源码阅读:linux timefd定时器

⭐timerfd timerfd 是Linux一个定时器接口&#xff0c;它基于文件描述符工作&#xff0c;并通过该文件描述符的可读事件进行超时通知。可以方便地与select、poll和epoll等I/O多路复用机制集成&#xff0c;从而在没有处理事件时阻塞程序执行&#xff0c;实现高效的零轮询编程模…...

nvm 1.2.2 报错解决方法 The system cannot find the file specified.

安装了最新版本nvn1.2.2后&#xff0c;安装老版本node时报错&#xff08;安装新版本没问题&#xff09;&#xff0c;报错内容如下&#xff1a; error installing 14.16.0: open C:\Users\admin\AppData\Local\Temp\nvm-npm-430098699\npm-v6.14.11.zip: The system cannot fin…...

学习Python的基本入门知识

学习Python的入门知识可以从以下几个方面开始&#xff1a; ### 1. **Python简介** - **Python** 是一种高级、解释型、通用的编程语言&#xff0c;由Guido van Rossum于1989年发布。 - 它以简洁、易读的语法著称&#xff0c;适合初学者。 - 广泛应用于Web开发、数据分…...

web安全——分析应用程序

文章目录 一、确定用户输入入口点二、确定服务端技术三、解析受攻击面 一、确定用户输入入口点 在检查枚举应用程序功能时生成的HTTP请求的过程中&#xff0c;用户输入入口点包括&#xff1a; URL文件路径 通常&#xff0c;在查询字符?之前的URL部分并不视为用户输入入口&am…...

OpenEuler学习笔记(三十五):搭建代码托管服务器

以下是主流的代码托管软件分类及推荐&#xff0c;涵盖自托管和云端方案&#xff0c;您可根据团队规模、功能需求及资源情况选择&#xff1a; 一、自托管代码托管平台&#xff08;可私有部署&#xff09; 1. GitLab 简介: 功能全面的 DevOps 平台&#xff0c;支持代码托管、C…...

平台设备驱动之gpio子系统(写驱动实现在/sys/...目录下用echo命令点灯)

1、 关键函数&#xff08;include/linux 及 driver目录下&#xff09; ​ module_platform_driver(leds_drv); //平台设备驱动入口//获取匹配成功后设备树节点中的 property ​ of_get_named_gpio_flags(node, "led_gpio", 0, &flags); //在/sys/目录下创建文…...

PS渐变工具

渐变工具&#xff1a;&#xff08;颜色条 起点到终点 为 前景色到背景色&#xff09; 渐变shift&#xff1a;垂直、水平、45度 渐变工具–》仿色&#xff1a;让渐变变得细腻。仿色值高&#xff0c;过渡柔和&#xff0c;仿色值低&#xff0c;过渡粗糙 渐变工具–》渐变编辑器&am…...

JavaScript将:;隔开的字符串转换为json格式。使用正则表达式匹配键值对,并构建对象。多用于解析cssText为style Object对象

// 使用正则表达式匹配键值对&#xff0c;并构建对象 let string2Json(s)>{const r {};s.replace(/&#xff1b;/g, ;).replace(/\;/g, \n).replace(/&#xff1a;/g, :).replace(/\n/g, \n)//合并多个换行符.split(\n).forEach(item > {const [k, v] item.split(:);(k…...

2025年追觅科技社招校招入职测评北森题库商业推理测试内容与技巧

在追觅科技的招聘流程中&#xff0c;无论是校园招聘还是社会招聘&#xff0c;应聘者都需要通过北森测评题库的商业推理部分。这部分的测评旨在评估应聘者的商业推理能力&#xff0c;是评估考生综合能力的重要工具。考试时间为40分钟&#xff0c;需要完成28题&#xff0c;题型以…...

AI大模型-提示工程学习笔记19-自我反思

目录 1. 自我反思的核心思想 (1) LLM 的局限性 (2) Reflexion 的解决方案 2. Reflexion 的工作流程 (1) 任务输入 (2) 初始生成 (3) 反思 (Reflection) (4) 调整与改进 (5) 迭代 (6) 结果输出 3. Reflexion 的关键组件 (1) 大语言模型 (LLM) (2) 反思者 (Reflector…...

【工具变量】公司企业数字领导力(2004-2023年)

数据简介&#xff1a;企业数字化领导力是指在数字经济时代&#xff0c;领导者通过战略性地使用数字资产、引领组织变革&#xff0c;使企业在数字化环境中获得持续成功的能力。对于上市公司而言&#xff0c;这种领导力尤为重要&#xff0c;因为它直接关系到企业的战略方向、市场…...

Android 10.0 Settings中系统菜单去掉备份二级菜单

1.前言 在10.0的系统rom定制化开发中,在系统Settings开发过程中,会发现在settings中的系统菜单中需要去掉 备份这个菜单,接下来就需要分析下系统菜单中的备份菜单的相关功能,然后实现去掉备份菜单的功能 2.Settings中系统菜单去掉备份二级菜单的核心类 packages/apps/Se…...

C++ | 设计模式 | 代理模式

代理模式&#xff08;Proxy Pattern&#xff09; 代理模式是一种结构型设计模式 &#xff0c;它为某个对象提供一个代理&#xff0c;以控制对该对象的访问。代理模式可以在不改变原始对象的情况下&#xff0c;通过引入代理对象来扩展功能或控制对原始对象的访问。 核心思想 代…...

【Vue工作原理】VueCli4 模板文件template不存在会生产一个默认文件原理

Vue CLI 4 的 HtmlWebpackPlugin 在模板文件缺失时&#xff0c;‌不会中断构建流程‌&#xff0c;而是自动生成一个‌极简 HTML 结构‌&#xff08;DeepSeek回答&#xff09; ‌&#x1f4d8; 官方资料来源解析&#xff08;基于 2025 年存档数据&#xff09;‌ 当前时间&#…...

数据开发的简历及面试

简历 个人信息: 邮箱别写QQ邮箱, 写126邮箱/189邮箱等 学历>>本科及以上写,大专及以下不写 专业>>非计算机专业不写 政治面貌>>党员写, 群众不用写 掌握的技能: 精通 > 熟悉 > 了解 专业工具: 大数据相关的 公司: 如果没有可以写的>>金融服…...

如何在docker上部署前端nginx服务(VUE)

目录结构 clean.sh docker stop rszWeb; docker rm rszWeb; start.sh docker run -d \ --name rszWeb \ -p 7084:80 \ -m 500m \ --privileged=true \ --restart=always \ -v /home/rsz/ui/conf/nginx.conf:/etc/nginx/nginx.conf \ -v /home/rsz/ui/logs:/meta/logs \ -v /…...

模型和数据集的平台之在Hugging Face上进行模型下载、上传以及创建专属Space

模型下载 步骤&#xff1a; 注册Hugging Face平台 https://huggingface.co/ 新建一个hf_download_josn.py 文件 touch hf_download_josn.py 编写hf_download_josn.py文件 import os from huggingface_hub import hf_hub_download# 指定模型标识符 repo_id "inter…...

[Web 信息收集] Web 信息收集 — 手动收集 IP 信息

关注这个专栏的其他相关笔记&#xff1a;[Web 安全] Web 安全攻防 - 学习手册-CSDN博客 0x01&#xff1a;通过 DNS 服务获取域名对应 IP DNS 即域名系统&#xff0c;用于将域名与 IP 地址相互映射&#xff0c;方便用户访问互联网。对于域名到 IP 的转换过程则可以参考下面这篇…...

排序算法(3):

这是我们的最后一篇排序算法了&#xff0c;也是我们的初阶数据结构的最后一篇了。 我们来看&#xff0c;我们之前已经讲完了插入排序&#xff0c;选择排序&#xff0c;交换排序&#xff0c;我们还剩下最后一个归并排序&#xff0c;我们今天就讲解归并排序&#xff0c;另外我们还…...

TypeScript - 泛型

泛型允许在定义函数、类或接口时&#xff0c;使用类型参数来表示未指定的类型&#xff0c;这些参数在具体使用时&#xff0c;才被指定具体的类型&#xff0c;泛型能让同一段代码适用于多种类型&#xff0c;同时仍然保持类型的安全性。 举例&#xff1a;如下代码中 <T> 就…...

Python基于Django和Vue的校园互助平台(附源码、文档说明)

博主介绍&#xff1a;✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3…...

打包rocketmq-dashboard报错问题记录

在进入到\rocketmq-dashboard-2.0.0\rocketmq-dashboard-rocketmq-dashboard-2.0.0目录下&#xff0c;执行mvn clean package -Dmaven.test.skiptrue打包&#xff0c;报错&#xff1a; java: java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImport does…...

Redis 高可用性:如何让你的缓存一直在线,稳定运行?

&#x1f3af; 引言&#xff1a;Redis的高可用性为啥这么重要&#xff1f; 在现代高可用系统中&#xff0c;Redis 是一款不可或缺的分布式缓存与数据库系统。无论是提升访问速度&#xff0c;还是实现数据的高效持久化&#xff0c;Redis 都能轻松搞定。可是&#xff0c;当你把 …...

Windows和AD域提权枚举脚本及工具介绍

在 Windows 和 Active Directory (AD) 域环境中&#xff0c;权限提升和枚举是渗透测试的关键部分。为了有效进行这些操作&#xff0c;使用特定的工具和脚本可以帮助你识别和利用系统中的漏洞。以下是一些常用于 Windows 和 AD 域环境中的权限提升和枚举脚本工具&#xff1a; W…...

【Linux】调试工具GDB的使用及案例讲解

Linux系列 文章目录 Linux系列前言一、gdb的使用背景二、gdb的使用总结 本篇主要针对小白讲解&#xff0c;可以很多地方比较咯嗦 前言 GDB是Linux下一款强大的调试工具。GDB可以调试C、C、Java等语言&#xff0c;对于在Linux下工作的程序员来说&#xff0c;GDB是必不可少的调试…...

DeepSeek回答:AI时代Go语言学习路线

最近有小伙伴经常会问&#xff1a;**该如何学习入门Go语言&#xff1f;怎样提升Go语言Coding水平&#xff1f;**这篇文章我们就使用DeepSeek来梳理下Go语言在AI时代的学习路线。 向DeepSeek提问的问题原文&#xff1a; 你现在是一名资深的Go语言工程师&#xff0c;精通Go语言并…...

1分钟用DeepSeek编写一个PDF转Word软件

一、引言 如今&#xff0c;在线工具的普及让PDF转Word成为了一个常见需求&#xff0c;常见的pdf转word工具有收费的wps&#xff0c;免费的有pdfgear&#xff0c;见下文&#xff1a; PDFgear:一款免费的PDF编辑、格式转化软件-CSDN博客 还有网上在线的免费pdf转word工具smallp…...

【Linux】初探信号的奥秘

目录 一、引入信号&#xff1a; 1、什么是信号&#xff1a; 二、前后台进程&#xff1a; 三、信号的处理方式&#xff1a; 四、键盘数据与信号&#xff1a; 前言&#xff1a; 在Linux系统编程中&#xff0c;信号&#xff08;Signal&#xff09;是一种至关重要的进程间通信…...

Fetch MCP 安装笔记-roo cline实操版本

Fetch MCP 安装笔记-roo cline实操版本 cursor中将MCP加入知识库&#xff1a;Doc->add new doc https://modelcontextprotocol.io/introduction MCP 参考https://github.com/langgptai/LangGPT 帮我生成一个langgpt的提示词&#xff0c;写到langgpt提示词.md MCP 学习这个文…...

C#初级教程(6)——函数:从基础到实践

一、函数的核心价值&#xff1a;简化与复用代码 以游戏开发项目为例&#xff0c;在游戏中&#xff0c;角色的移动、攻击等行为并非只在单一场景中出现。设想一下&#xff0c;若每次需要角色执行这些行为时&#xff0c;都要重新编写对应的代码&#xff0c;那将是何等繁琐且易错的…...