一文打尽pthread库

一文打尽pthread库

pthread(POSIX Threads)是遵循 POSIX 标准的线程库,广泛用于 Unix/Linux 系统中实现多线程编程。它提供了一组 C 语言 API,用于创建、管理、同步线程。以下是对 pthread 库及相关函数的系统性总结:

一、基本概念

  • 线程(Thread):轻量级进程,共享进程地址空间,独立执行流。

  • 主线程:程序启动时默认创建的线程。

  • 并发 vs 并行:并发是逻辑上同时执行,并行是物理上同时执行(多核)。

  • 线程安全:函数/数据结构在多线程环境下能正确工作。

二、核心数据类型

1
2
3
4
5
6
7
#include <pthread.h>

pthread_t // 线程标识符(类似进程的 pid)
pthread_attr_t // 线程属性结构体
pthread_mutex_t // 互斥锁
pthread_cond_t // 条件变量
pthread_rwlock_t // 读写锁

三、线程管理函数

1. 创建线程

1
2
3
4
int pthread_create(pthread_t *thread, 
const pthread_attr_t *attr,
void *(*start_routine)(void*),
void *arg);
  • thread:输出参数,新线程 ID。

  • attr:线程属性,NULL 表示默认。

  • start_routine:线程入口函数。

  • arg:传递给入口函数的参数。

  • 返回值:0 成功,非 0 错误码(非 errno)。

⚠️ 注意:必须链接 -lpthread(或 -pthread)

2. 等待线程结束(阻塞)

1
int pthread_join(pthread_t thread, void **retval);
  • 阻塞调用线程,直到目标线程结束。

  • 可获取线程返回值(通过 retval)。

  • 适用于需要同步或回收资源的场景。

3. 分离线程(非阻塞回收)

1
int pthread_detach(pthread_t thread);
  • 线程结束后自动释放资源,无需 pthread_join。

  • 不能对已分离线程调用 pthread_join。

4. 获取当前线程 ID

1
pthread_t pthread_self(void);

5. 比较线程 ID

1
int pthread_equal(pthread_t t1, pthread_t t2);
  • 返回非 0 表示相等。

6. 退出线程

1
void pthread_exit(void *retval);
  • 主动终止当前线程,可传递返回值。

  • 主线程调用 pthread_exit 不会终止整个进程(除非是最后一个线程)。

四、线程属性设置(可选)

1
2
3
4
5
6
7
8
9
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);

// 设置分离状态
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
// detachstate: PTHREAD_CREATE_JOINABLE / PTHREAD_CREATE_DETACHED

// 设置栈大小
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);

五、线程同步机制

1. 互斥锁(Mutex)

用于保护临界区,防止多个线程同时访问共享资源。

1
2
3
4
5
6
7
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 静态初始化

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex); // 非阻塞
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);

🔒 使用模式:

1
2
3
pthread_mutex_lock(&mutex);
// 临界区代码
pthread_mutex_unlock(&mutex);

2. 条件变量(Condition Variable)

用于线程间通信,常与互斥锁配合使用,实现“等待某个条件成立”。

1
2
3
4
5
6
7
8
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); // 阻塞等待
int pthread_cond_timedwait(...); // 带超时
int pthread_cond_signal(pthread_cond_t *cond); // 唤醒一个等待线程
int pthread_cond_broadcast(pthread_cond_t *cond); // 唤醒所有等待线程
int pthread_cond_destroy(pthread_cond_t *cond);

🔄 典型使用模式:

1
2
3
4
5
6
pthread_mutex_lock(&mutex);
while (condition_is_false) {
pthread_cond_wait(&cond, &mutex); // 自动释放锁,被唤醒后重新加锁
}
// 执行操作
pthread_mutex_unlock(&mutex);

3. 读写锁(Read-Write Lock)

允许多个读者同时访问,写者独占访问。

1
2
3
4
5
6
7
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

int pthread_rwlock_init(...);
int pthread_rwlock_rdlock(...); // 读锁
int pthread_rwlock_wrlock(...); // 写锁
int pthread_rwlock_unlock(...);
int pthread_rwlock_destroy(...);

适合“读多写少”场景。

4. 自旋锁(Spinlock)【可选】

忙等待锁,适用于锁持有时间极短的场景。

1
2
3
4
5
6
pthread_spinlock_t spinlock;

int pthread_spin_init(...);
int pthread_spin_lock(...);
int pthread_spin_unlock(...);
int pthread_spin_destroy(...);

六、线程局部存储(TLS)

每个线程拥有独立的变量副本。

1
2
3
4
5
6
pthread_key_t key;

int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
int pthread_setspecific(pthread_key_t key, const void *value);
void *pthread_getspecific(pthread_key_t key);
int pthread_key_delete(pthread_key_t key);

类似于 C11 的 _Thread_local 或 GCC 的 __thread。

七、取消机制(Cancellation)

允许一个线程请求终止另一个线程。

1
2
3
4
5
6
7
8
9
10
int pthread_cancel(pthread_t thread); // 发送取消请求

// 设置取消状态和类型
int pthread_setcancelstate(int state, int *oldstate);
// state: PTHREAD_CANCEL_ENABLE / PTHREAD_CANCEL_DISABLE

int pthread_setcanceltype(int type, int *oldtype);
// type: PTHREAD_CANCEL_DEFERRED(默认,到取消点) / PTHREAD_CANCEL_ASYNCHRONOUS

void pthread_testcancel(void); // 显式设置取消点

⚠️ 取消点(Cancellation Points):如 sleep, read, write, pthread_cond_wait 等阻塞函数。

八、一次初始化(One-time Initialization)

确保某段初始化代码只执行一次。

1
2
3
pthread_once_t once_control = PTHREAD_ONCE_INIT;

int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));

常用于初始化全局资源(如互斥锁、TLS key)。

九、常见错误处理

pthread 函数不设置 errno,而是直接返回错误码:

1
2
3
4
int ret = pthread_create(...);
if (ret != 0) {
fprintf(stderr, "Error: %s\n", strerror(ret));
}

或使用 perror 需要先设置 errno:

1
2
errno = ret;
perror("pthread_create");

十、编译与链接

1
2
3
gcc -o program program.c -lpthread
# 或推荐使用:
gcc -o program program.c -pthread # 自动定义宏、链接库

十一、最佳实践与注意事项

避免死锁:加锁顺序一致,避免嵌套锁。

资源回收:join 或 detach 所有线程,避免资源泄漏。

线程安全函数:避免使用非线程安全函数(如 strtok, rand → 改用 strtok_r, rand_r)。

信号处理:线程中慎用信号,推荐使用 sigwait 或指定信号处理线程。

性能考虑:锁粒度不宜过大,避免频繁加锁。

调试工具:使用 valgrind –tool=helgrind 或 ThreadSanitizer 检测竞争条件。

十二、简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

void* thread_func(void* arg) {
int id = *(int*)arg;
printf("Thread %d running\n", id);
sleep(1);
pthread_exit((void*)(long)id);
}

int main() {
pthread_t t1, t2;
int id1 = 1, id2 = 2;

pthread_create(&t1, NULL, thread_func, &id1);
pthread_create(&t2, NULL, thread_func, &id2);

void* retval;
pthread_join(t1, &retval);
printf("Thread 1 returned: %ld\n", (long)retval);

pthread_join(t2, &retval);
printf("Thread 2 returned: %ld\n", (long)retval);

return 0;
}

总结

功能主要函数创建线程pthread_create等待线程pthread_join分离线程pthread_detach互斥锁pthread_mutex_*条件变量pthread_cond_*读写锁pthread_rwlock_*线程局部存储pthread_key_*一次初始化pthread_once线程取消pthread_cancel, pthread_testcancel

掌握 pthread 库是进行 Linux/Unix 系统级并发编程的基础。合理使用同步机制、避免竞态条件和死锁,是编写健壮多线程程序的关键。

✅ 推荐学习顺序:线程创建 → 互斥锁 → 条件变量 → 读写锁 → 高级特性(TLS、取消、一次初始化)📚 参考资料:《UNIX环境高级编程》、《POSIX标准文档》、Linux man pages (man pthread_create)

以下是《UNIX环境高级编程》和《POSIX标准文档》的官方或权威获取链接:

📘 1.《UNIX环境高级编程》(Advanced Programming in the UNIX Environment, 简称 APUE)

  • 作者:W. Richard Stevens & Stephen A. Rago

  • 当前最新版:第3版(2013),涵盖 POSIX.1-2008 和 UNIX System V / BSD 扩展

  • 官方出版商页面:👉 https://www.apuebook.com/

该网站由作者 Stephen Rago 维护,提供:

源代码下载(所有示例代码)

勘误表(errata)

各章摘要和更新说明

非常适合配合学习使用

⚠️ 本书无“开源免费电子版”,请支持正版。

📜 2.《POSIX 标准文档》(IEEE Std 1003.1)

POSIX 是由 IEEE 和 The Open Group 共同维护的标准,官方文档需通过其网站获取。

✅ 官方免费在线查阅版(HTML):

👉 The Open Group Base Specifications, Issue 7 (2018) — POSIX.1-2017🔗 https://pubs.opengroup.org/onlinepubs/9699919799/

这是当前最权威、最新且免费公开访问的 POSIX 标准文档(含 Shell、Utilities、System Interfaces、Headers 等)。

  • 包含所有 pthread 函数规范(如 pthread_create, pthread_mutex_lock 等)

  • 可直接搜索函数名,查看标准定义、参数、返回值、错误码、可移植性说明

  • 支持书签、交叉引用,适合开发者查阅

📄 PDF 下载版(需注册,部分免费):

👉 https://publications.opengroup.org/standards/unix

  • 注册后可免费下载 PDF(部分文档需付费)

  • 搜索 “IEEE Std 1003.1™-2017” 或 “The Open Group Base Specifications Issue 7”

🆚 IEEE 官方标准购买页面(付费):

👉 https://standards.ieee.org/standard/1003_1-2017.html

  • IEEE 出版的正式标准文档(PDF)

  • 价格较高(约 $200+),适合企业或标准研究者

  • 内容与 The Open Group 版本基本一致

✅ 对于绝大多数开发者,The Open Group 免费在线版已完全足够。

📌 补充:Linux man pages(在线)

虽然不是“标准文档”,但 Linux man pages 是最实用的函数参考:

🔗 https://man7.org/linux/man-pages/

✅ 总结推荐

资源名称类型推荐链接备注《APUE》官网图书配套https://www.apuebook.com/源码+勘误,必备POSIX 标准在线文档官方标准https://pubs.opengroup.org/onlinepubs/9699919799/免费、权威、最新Linux man-pages函数手册https://man7.org/linux/man-pages/实用开发参考IEEE POSIX 标准购买付费标准https://standards.ieee.org/standard/1003_1-2017.html企业/研究用

📘 建议学习路径:

学 pthread → 查 man7.org 快速上手

深入理解标准行为 → 查 Open Group POSIX 文档

系统学习 UNIX 编程 → 读 APUE 第3版

全球知名在线电子图书馆

全球知名在线电子图书馆 (英文版)

探索全球知名在线电子图书馆,获取海量电子书与学术资源,随时随地畅享阅读乐趣。以下是根据您提供的原始列表,对全球知名在线电子图书馆进行扩充(新增6个具有地区或专业影响力的平台),并对全部12个电子图书馆按照统一模板进行详细信息整理的完整内容。

相关文章:LinuxKernel全球下载站点与镜像站点统计

Z-Library 详细介绍

  1. 基本信息
  1. 资源内容与规模
  • 总资源量:超过1.3亿本书籍、8400万篇学术文章

  • 资源类型:电子书、教材、小说、学术论文、期刊文章、杂志、有声书等

  • 语言与学科覆盖:支持100+种语言,涵盖文史哲、理工医、法律、艺术等几乎所有学科领域

  1. 资源来源与版权状况
  • 来源渠道:用户上传、网络爬取、影印扫描、第三方数据库整合

  • 版权说明:大量资源未获正式授权,属于“影子图书馆”(Shadow Library),在多国被认定为侵犯版权,存在法律争议

  1. 访问方式与使用权限
  • 是否免费:是(但需通过镜像站点访问)

  • 是否需注册:部分功能需注册账号,每日可免费下载有限数量资源

  • 下载与借阅规则:支持PDF、EPUB、MOBI等多种格式下载;高级会员可提升下载额度

  1. 技术功能与用户体验
  • 搜索功能:支持书名、作者、ISBN、ISSN、DOI、关键词等多种检索方式,具备模糊匹配和推荐系统

  • 界面与设备支持:响应式网页设计,适配手机与桌面端;提供简洁清晰的界面

  • 阅读工具:支持在线预览,但无内置阅读器标注功能

  1. 目标用户
  • 主要服务对象:自学者、发展中国家学生、科研人员、无法负担高价学术资源的人群
  1. 合作网络与影响力
  • 合作机构:无官方合作机构,依赖分布式镜像和志愿者维护

  • 学术地位:虽非正规学术平台,但在全球范围内广泛使用,尤其在低收入国家影响巨大

  1. 特色与价值
  • 独特优势:资源极其丰富,更新速度快,检索高效,几乎“无所不包”

  • 社会影响:推动知识平权,但也引发关于版权保护与开放获取边界的广泛讨论

World Library 详细介绍

  1. 基本信息
  1. 资源内容与规模
  • 总资源量:超过349万册电子图书

  • 资源类型:经典文学、历史文献、宗教典籍、教育教材、儿童读物、科学普及书籍等

  • 语言与学科覆盖:支持100余种语言,最早资源可追溯至11世纪,涵盖人文、历史、宗教、教育等领域

  1. 资源来源与版权状况
  • 来源渠道:公共领域文献数字化、合作图书馆捐赠、政府档案开放

  • 版权说明:主要收录公共领域(Public Domain)作品,合法合规,无版权争议

  1. 访问方式与使用权限
  • 是否免费:完全免费

  • 是否需注册:无需注册即可浏览和下载

  • 下载与借阅规则:所有书籍均可直接下载PDF或EPUB格式,无DRM限制

  1. 技术功能与用户体验
  • 搜索功能:基础检索+高级检索(按语言、年代、主题);2025年起引入AI智能检索、AI内容摘要生成

  • 界面与设备支持:网页界面友好,支持移动端访问

  • 阅读工具:提供在线阅读器,支持文本高亮与字体调整

  1. 目标用户
  • 主要服务对象:学生、教师、终身学习者、多语言读者、文化遗产研究者
  1. 合作网络与影响力
  • 合作机构:联合国教科文组织合作伙伴,与全球多个公共图书馆和教育机构合作

  • 学术地位:作为公益性数字图书馆,在发展中国家教育推广中具有重要价值

  1. 特色与价值
  • 独特优势:强调跨文化保存与AI赋能的知识发现,致力于“让每本书都能被找到”

  • 社会影响:促进全球知识共享,尤其服务于语言少数群体和偏远地区学习者

Google Books 详细介绍

  1. 基本信息
  1. 资源内容与规模
  • 总资源量:可搜索数千万本书籍,其中数百万本提供预览或全文

  • 资源类型:图书、期刊、手册、技术报告、古籍等

  • 语言与学科覆盖:多语言(以英语为主),覆盖所有主流学科

  1. 资源来源与版权状况
  • 来源渠道:与全球图书馆(如哈佛、牛津、纽约公共图书馆)及出版商合作扫描

  • 版权说明:分为三类——公共领域(全文可读)、受限预览(部分内容)、仅元数据(不可读)。部分扫描曾引发版权诉讼(如Authors Guild诉Google案),但法院最终裁定其为“合理使用”

  1. 访问方式与使用权限
  • 是否免费:部分内容免费,完整阅读需购买或通过图书馆访问

  • 是否需注册:无需注册即可搜索和预览

  • 下载与借阅规则:仅公共领域书籍可下载PDF;其余仅支持在线预览

  1. 技术功能与用户体验
  • 搜索功能:强大的全文检索,支持OCR识别文本搜索,可定位到具体段落

  • 界面与设备支持:高度优化的搜索引擎界面,适配所有设备

  • 阅读工具:内置翻页式阅读器,支持复制文本、翻译句子、查看相似书籍

  1. 目标用户
  • 主要服务对象:研究人员、普通读者、图书发现者、引用查找者
  1. 合作网络与影响力
  • 合作机构:哈佛大学、斯坦福大学、牛津大学、纽约公共图书馆、各大出版社

  • 学术地位:全球最大图书搜索引擎,是学术研究中常用的文献发现工具

  1. 特色与价值
  • 独特优势:强大的搜索引擎集成,能将图书内容纳入Google整体知识图谱

  • 社会影响:极大提升了图书的可见性与可发现性,推动了“数字人文”发展

Project Gutenberg 详细介绍

  1. 基本信息
  • 成立时间:1971年

  • 运营机构:Project Gutenberg Association(志愿者组织)

  • 官网地址:https://www.gutenberg.org

  1. 资源内容与规模
  • 总资源量:超过7万本免费电子书(截至2025年)

  • 资源类型:经典文学、诗歌、戏剧、历史著作、哲学文本等

  • 语言与学科覆盖:以英语为主,含法语、德语、西班牙语等数十种语言;聚焦人文经典

  1. 资源来源与版权状况
  • 来源渠道:志愿者从纸质书手工录入或OCR处理,经校对后发布

  • 版权说明:所有书籍均为公共领域作品(通常出版超过95年),完全合法免费

  1. 访问方式与使用权限
  • 是否免费:完全免费

  • 是否需注册:无需注册

  • 下载与借阅规则:支持EPUB、Kindle、HTML、TXT等格式自由下载,无任何限制

  1. 技术功能与用户体验
  • 搜索功能:支持书名、作者、主题、语言检索,可按热门度排序

  • 界面与设备支持:简洁网页设计,兼容移动端

  • 阅读工具:支持在线阅读,部分书籍提供语音朗读版本

  1. 目标用户
  • 主要服务对象:文学爱好者、学生、英语学习者、古典文化研究者
  1. 合作网络与影响力
  • 合作机构:与Distributed Proofreaders等志愿者项目合作,全球数千名志愿者参与

  • 学术地位:最早启动的数字图书馆之一,被誉为“数字图书馆运动的起点”

  1. 特色与价值
  • 独特优势:纯公益、无广告、完全开放,强调志愿者精神与文化传承

  • 社会影响:为全球读者提供无障碍的经典阅读体验,是开放获取理念的典范

JSTOR 详细介绍

  1. 基本信息
  1. 资源内容与规模
  • 总资源量:收录近2000种学术期刊、10万+本学术书籍、数百万篇论文

  • 资源类型:学术期刊过刊、学术专著、原始资料(如档案、照片)

  • 语言与学科覆盖:以英语为主,涵盖人文社科(历史、文学、政治、经济学等)为主,近年扩展至自然科学

  1. 资源来源与版权状况
  • 来源渠道:与学术出版社和大学合作授权收录

  • 版权说明:合法授权,受版权保护;部分早期内容进入公共领域后免费开放

  1. 访问方式与使用权限
  • 是否免费:部分免费(Old Archive计划),大部分需机构订阅或个人付费

  • 是否需注册:个人可注册免费账户(查看摘要),全文需通过机构登录

  • 下载与借阅规则:订阅用户可下载PDF,通常每月有下载限额

  1. 技术功能与用户体验
  • 搜索功能:强大的全文检索,支持高级筛选(学科、出版年、期刊名等)

  • 界面与设备支持:现代网页设计,支持移动访问

  • 阅读工具:内置阅读器支持笔记、引用导出(EndNote、Zotero)、高亮标记

  1. 目标用户
  • 主要服务对象:高校师生、研究人员、图书馆员
  1. 合作网络与影响力
  • 合作机构:全球超过8000家教育与研究机构,包括哈佛、MIT、剑桥等顶尖学府

  • 学术地位:人文学科最重要的文献数据库之一,被广泛用于学术写作与研究

  1. 特色与价值
  • 独特优势:高质量、同行评审、长期保存,强调学术严谨性

  • 社会影响:推动学术资源的系统化归档与可持续访问,是“数字典藏”的标杆

世界数字图书馆(The World Digital Library) 详细介绍

  1. 基本信息
  • 成立时间:2009年

  • 运营机构:美国国会图书馆主导,联合国教科文组织联合发起

  • 官网地址:https://www.wdl.org

  1. 资源内容与规模
  • 总资源量:超过20,000件数字化文物

  • 资源类型:古籍、手稿、地图、照片、影片、乐谱、稀有图书等

  • 语言与学科覆盖:涵盖全球193个国家的文化遗产,支持7种语言(阿拉伯语、中文、英语、法语、西班牙语、俄语、葡萄牙语)

  1. 资源来源与版权状况
  • 来源渠道:来自全球图书馆、博物馆、档案馆的捐赠与合作数字化

  • 版权说明:所有资源均为公共领域或已获授权,合法开放

  1. 访问方式与使用权限
  • 是否免费:完全免费

  • 是否需注册:无需注册

  • 下载与借阅规则:所有资源可自由查看、下载高清图像或PDF

  1. 技术功能与用户体验
  • 搜索功能:支持按国家、时期、主题、语言、类型检索;提供时间轴和地图浏览模式

  • 界面与设备支持:多语言界面,响应式设计,适合教学展示

  • 阅读工具:高清图像缩放、元数据详细说明、多语言描述

  1. 目标用户
  • 主要服务对象:教师、学生、文化研究者、跨文化交流者
  1. 合作网络与影响力
  • 合作机构:UNESCO、美国国会图书馆、大英图书馆、中国国家图书馆等32个国际机构

  • 学术地位:全球最重要的跨文化数字遗产平台之一

  1. 特色与价值
  • 独特优势:强调文化多样性与全球共享,呈现非西方视角的历史文献

  • 社会影响:促进文明互鉴,成为国际教育与博物馆数字化的典范

✅ 新增平台(按地区/专业补充)

中国国家数字图书馆 详细介绍

  1. 基本信息
  1. 资源内容与规模
  • 总资源量:超1000万册电子资源

  • 资源类型:电子书、古籍、学位论文、地方志、音视频讲座、民国文献

  • 语言与学科覆盖:以中文为主,涵盖文史哲、法律、经济、艺术等

  1. 资源来源与版权状况
  • 来源渠道:馆藏数字化、出版社合作、政府项目支持

  • 版权说明:合法授权或公共领域,部分资源需认证访问

  1. 访问方式与使用权限
  • 是否免费:部分免费,注册用户可远程访问更多资源

  • 是否需注册:需实名注册(可凭身份证办理)

  • 下载与借阅规则:部分图书支持在线阅读,部分可下载PDF

  1. 技术功能与用户体验
  • 搜索功能:支持全文检索、古籍繁体字检索、手写体识别试点

  • 界面与设备支持:PC与移动端兼容,支持微信小程序访问

  • 阅读工具:古籍高清浏览、语音朗读、放大镜功能

  1. 目标用户
  • 主要服务对象:国内高校师生、研究人员、公众读者
  1. 合作网络与影响力
  • 合作机构:全国各级公共图书馆、高校图书馆联盟

  • 学术地位:中国最大公益性数字图书馆,国家级文化基础设施

  1. 特色与价值
  • 独特优势:中华古籍数字化领先,如《永乐大典》《四库全书》高清呈现

  • 社会影响:推动中华优秀传统文化传承与全民阅读

Internet Archive 详细介绍

  1. 基本信息
  • 成立时间:1996年

  • 运营机构:Internet Archive(非营利组织)

  • 官网地址:https://archive.org

  1. 资源内容与规模
  • 总资源量:超3000万本电子书(含Open Library)、数百亿网页快照、百万音视频

  • 资源类型:电子书、网页存档、老软件、电影、音乐、广播

  • 语言与学科覆盖:多语言,综合类

  1. 耄源来源与版权状况
  • 来源渠道:捐赠、扫描、网络爬取、用户上传

  • 版权说明:混合模式——公共领域+合理使用+部分受控借阅(受DRM限制)

  1. 访问方式与使用权限
  • 是否免费:完全免费

  • 是否需注册:借阅电子书需注册

  • 下载与借阅规则:多数可下载;受控借阅书籍限借14天

  1. 技术功能与用户体验
  • 搜索功能:强大元数据检索,支持关键词、年份、类型筛选

  • 界面与设备支持:功能丰富但略显复杂,支持全平台

  • 阅读工具:内置通用阅读器,支持翻页、缩放、文本复制

  1. 目标用户
  • 主要服务对象:研究者、怀旧爱好者、数字保存关注者
  1. 合作网络与影响力
  • 合作机构:图书馆、大学、档案馆、开源社区

  • 学术地位:数字保存领域的全球领导者

  1. 特色与价值
  • 独特优势:“保存人类知识”,包括已消失的网站与老系统软件

  • 社会影响:被誉为“数字时代的亚历山大图书馆”

HathiTrust Digital Library 详细介绍

  1. 基本信息
  • 成立时间:2008年

  • 运营机构:HathiTrust联合体(由美国多所研究型大学组成)

  • 官网地址:https://www.hathitrust.org

  1. 资源内容与规模
  • 总资源量:超1700万卷图书

  • 资源类型:学术图书、期刊、政府出版物、专利

  • 语言与学科覆盖:以英语为主,涵盖各学科

  1. 资源来源与版权状况
  • 来源渠道:Google Books、成员图书馆扫描

  • 版权说明:约40%为公共领域可全文访问,其余受版权保护仅限成员机构检索

  1. 访问方式与使用权限
  • 是否免费:公共领域内容免费,其余需成员机构登录

  • 是否需注册:公众无需注册即可搜索,访问受限

  • 下载与借阅规则:公共领域书籍可整本下载PDF

  1. 技术功能与用户体验
  • 搜索功能:支持全文检索、批量元数据导出

  • 界面与设备支持:简洁专业,适合研究用途

  • 阅读工具:标准PDF阅读,支持文本复制

  1. 目标用户
  • 主要服务对象:北美研究型大学师生
  1. 合作网络与影响力
  • 合作机构:密歇根大学、哈佛、斯坦福、加州大学系统等170+高校

  • 学术地位:北美最重要的学术数字图书馆联盟

  1. 特色与价值
  • 独特优势:大规模合作典藏,确保学术资源长期可访问

  • 社会影响:为“数字学术”提供基础设施支持

Europeana 详细介绍

  1. 基本信息
  1. 资源内容与规模
  • 总资源量:超5000万条文化记录

  • 资源类型:书籍、手稿、艺术品、照片、音乐、电影

  • 语言与学科覆盖:29种欧洲语言,涵盖艺术、历史、文学等

  1. 资源来源与版权状况
  • 来源渠道:欧盟各国博物馆、图书馆、档案馆元数据聚合

  • 版权说明:遵循“开放元数据”原则,内容链接至原始机构,多数为合法开放资源

  1. 访问方式与使用权限
  • 是否免费:完全免费

  • 是否需注册:无需注册

  • 下载与借阅规则:可查看和使用元数据,原始文件依来源机构政策

  1. 技术功能与用户体验
  • 搜索功能:支持语义搜索、时间轴浏览、地图可视化

  • 界面与设备支持:现代化设计,支持多语言切换

  • 阅读工具:高清图像查看、收藏夹功能

  1. 目标用户
  • 主要服务对象:文化研究者、教师、艺术家、公众
  1. 合作网络与影响力
  • 合作机构:来自欧洲40+国家的上千家文化机构

  • 学术地位:欧洲最大文化遗产数字门户

  1. 特色与价值
  • 独特优势:跨机构、跨国家整合,展现欧洲多元文化

  • 社会影响:促进文化遗产数字化与公众参与

NDLI(印度国家数字图书馆) 详细介绍

  1. 基本信息
  • 成立时间:2014年

  • 运营机构:印度理工学院克勒格布尔分校(IIT Kharagpur)

  • 官网地址:https://ndl.iitkgp.ac.in

  1. 资源内容与规模
  • 总资源量:超1亿条教育资源

  • 资源类型:教科书、讲义、视频课程、试题、论文

  • 语言与学科覆盖:支持印地语、泰米尔语等22种印度语言,覆盖K12至研究生教育

  1. 资源来源与版权状况
  • 来源渠道:政府项目、高校合作、开放教育资源(OER)

  • 版权说明:多数为开放许可或公共领域

  1. 访问方式与使用权限
  • 是否免费:完全免费

  • 是否需注册:建议注册以使用个性化功能

  • 下载与借阅规则:支持自由下载PDF、视频等

  1. 技术功能与用户体验
  • 搜索功能:支持多语言检索、课程体系导航

  • 界面与设备支持:响应式设计,支持离线APP

  • 阅读工具:集成视频播放、笔记功能

  1. 目标用户
  • 主要服务对象:印度学生、教师、农村地区学习者
  1. 合作网络与影响力
  • 合作机构:UGC、AICTE、CBSE、各邦教育部门

  • 学术地位:南亚最大教育数字平台

  1. 特色与价值
  • 独特优势:服务教育公平,支持多语言与低带宽环境

  • 社会影响:助力印度“数字印度”与“全民教育”战略

PubMed Central (PMC) 详细介绍

  1. 基本信息
  1. 资源内容与规模
  • 总资源量:超700万篇全文论文

  • 资源类型:生物医学、生命科学领域的同行评审论文

  • 语言与学科覆盖:以英语为主,涵盖医学、遗传学、公共卫生等

  1. 资源来源与版权状况
  • 来源渠道:期刊出版社提交(符合NIH公共访问政策)

  • 版权说明:均为开放获取(OA),遵循CC许可或出版商政策

  1. 访问方式与使用权限
  • 是否免费:完全免费

  • 是否需注册:无需注册

  • 下载与借阅规则:支持PDF、XML格式下载,可用于研究与教学

  1. 技术功能与用户体验
  • 搜索功能:与PubMed集成,支持高级筛选、引文追踪

  • 界面与设备支持:专业简洁,支持API调用

  • 阅读工具:标准PDF阅读,支持参考文献链接

  1. 目标用户
  • 主要服务对象:医学研究人员、临床医生、学生
  1. 合作网络与影响力
  • 合作机构:全球数千家医学期刊、大学、研究机构

  • 学术地位:生物医学领域最权威的开放获取数据库

  1. 特色与价值
  • 独特优势:强制开放政策推动科研透明化

  • 社会影响:加速医学知识传播,尤其在疫情等公共卫生事件中发挥关键作用

✅ 总结:本整理共涵盖 12个全球知名电子图书馆,包括:

  • 全球综合性平台(Z-Library、Internet Archive)

  • 学术研究型(JSTOR、HathiTrust、PMC)

  • 国家级文化项目(中国、印度、美国、欧盟)

  • 开放获取先驱(Project Gutenberg、World Library)

  • 跨文化交流平台(WDL、Europeana)

全球知名在线电子图书馆, 在线电子图书馆推荐, 全球著名电子图书馆平台, 免费在线电子图书馆资源, 国际知名数字图书馆网站, 专业电子图书馆平台排名, 全球主流电子书阅读平台, 权威在线图书资源网站, 知名电子图书馆有哪些, 数字图书馆推荐与对比

How to Build an AI Agent with Dify

Here’s the English version of the beginner-friendly, highly practical guide to building an Agent using Dify — designed for non-technical users, with a clear, visual, and step-by-step approach.

🤖 How to Build an AI Agent with Dify (For Absolute Beginners)

A visual, no-code guide to creating smart agents that think, decide, and act — even if you’re not a developer.

🎯 What Is an AI Agent?

An AI Agent is more than a chatbot. It can:

  • Understand your goal

  • Break it into steps

  • Use tools (like search, APIs)

  • Make decisions

  • Take action

  • Return a complete result

Example: You say, “Will it rain in Shanghai tomorrow? Remind me to bring an umbrella if so.”The agent figures out what to do, checks the weather, and gives you a smart reply.

✅ Why Use Dify?

Dify is one of the best platforms for beginners to build AI agents because:

BenefitWhy It Helps BeginnersVisual Workflow BuilderDrag-and-drop nodes — no coding neededBuilt-in LLM SupportUse GPT, Qwen, etc. out of the boxCustom ToolsConnect to APIs, databases, web servicesFull in Chinese & EnglishEasy for global usersOpen-source & Self-hostableFlexible and secure

✅ Dify turns complex agent logic into simple visual blocks.

🚀 Step-by-Step: Build a “Weather Reminder Agent”

We’ll create an agent that:

Understands if you want weather info

Checks the weather

Decides whether to remind you

Replies naturally

No code. Just drag, click, and test.

🧱 Step 1: Create a Workflow App

Go to Dify.ai → Log in

Click “Create Application”

Choose “Workflow” mode

🔧 This is where you build your agent’s “brain”.

🧩 Step 2: Design the Workflow (5 Simple Nodes)

Here’s the flow:

1
2
3
4
5
6
7
8
9
10
11
&#91;User Input]

🟢 Node 1: Intent Detection (LLM) — What does the user want?

🟡 Node 2: Condition — Should we check weather?
↓ Yes ↓ No
🔵 Node 3: Tool Call 🔵 Node 4: Simple Reply

🟢 Node 5: Final Response (with reminder logic)

&#91;Output to User]

Let’s configure each node.

🔧 Step 3: Configure Each Node

🟢 Node 1: Intent Detection (LLM Node)

Purpose: Extract whether the user wants weather info and which city.

Settings:

  • Type: LLM

  • Model: GPT-3.5 / Qwen / etc.

  • Prompt (copy-paste this):

1
2
3
4
5
6
7
8
9
You are a task analyzer. Analyze the user input and decide if weather check is needed.

User input: {{input}}

Return JSON format:
{
"need_check": true or false,
"city": "city name, e.g. Beijing"
}

✅ Enable Structured Output → Format: JSON📌 Save output as variable: intent

🟡 Node 2: Condition Branch

Purpose: Decide which path to take.

Rule:

1
intent.need_check == true
  • If true → go to weather tool

  • If false → go to simple reply

🔵 Node 3: Tool Call — Get Weather

🛠️ First: Create a Custom Tool

Go to: Application Settings → Tools → Create Tool

FieldValueNameget_weatherDescriptionGet weather for a cityParametersUse this JSON Schema

1
2
3
4
5
6
7
8
9
10
{
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name, e.g. Shanghai"
}
},
"required": &#91;"city"]
}

📌 After saving, Dify gives you a Webhook URL — you’ll use this.

🌐 Build the Weather Backend (Beginner-Friendly)

You need a small service to return real weather data.

✅ Option 1: Use a Free Weather API

Example with OpenWeatherMap:

  • Sign up (free tier)

  • Build a simple FastAPI/Flask app that calls their API

✅ Option 2: Use a Ready-Made Template

We’ve prepared a simple FastAPI weather tool:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from fastapi import FastAPI
import requests

app = FastAPI()

@app.post("/weather")
def get_weather(data: dict):
city = data.get("city")
api_key = "YOUR_OPENWEATHER_KEY"
url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}"
response = requests.get(url).json()
return {
"temp": f"{response&#91;'main']&#91;'temp'] - 273.15:.1f}°C",
"condition": response&#91;'weather']&#91;0]&#91;'description']
}

Deploy it on:

  • Vercel / Render / Railway (free)

  • Or use Alibaba Cloud Function Compute

Then set the webhook URL in Dify.

🔧 Back in Dify: Call the Tool

  • Type: Tool

  • Tool: get_weather

  • Parameters: {“city”: ““}

  • Save result as: weather_info

🟢 Node 5: Generate Final Reply (LLM Node)

Prompt:

1
2
3
4
5
6
You are a helpful assistant. Based on the weather info, decide if a reminder is needed.

Weather info:
{{weather_info}}

Reply in natural language. If it's raining, remind the user to bring an umbrella.

This is your agent’s final answer.

🔵 Node 4: Simple Reply (for non-weather queries)

Prompt:

1
2
The user didn’t ask about weather. Just reply politely:
{{input}}

▶️ Step 4: Test It!

Input:

1
Will it rain in Hangzhou tomorrow? If yes, remind me.

Expected Output:

1
It will rain in Hangzhou tomorrow. Don’t forget your umbrella!

🎉 Success! Your first AI Agent is live.

📈 Level Up: Make Your Agent Smarter

FeatureHow to AddRemember past chatsEnable session context in DifyPlan a tripAdd a “task planner” LLM node to break goals into stepsBook hotelsAdd a booking API as a toolMulti-step loopsUse parallel or retry nodes (Pro feature)

🧰 Starter Kit for Beginners

🎁 1. Ready-to-Use Weather Webhook (Test Only)

We provide a demo endpoint (for testing):

1
2
3
POST https://demo-agent-tools.example.com/weather
Body: {"city": "Beijing"}
→ Returns: {"temp": "22°C", "condition": "Sunny"}

🔒 For real use, deploy your own for security.

🧩 2. Exportable Workflow Template (JSON)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
"nodes": &#91;
{
"id": "intent",
"type": "llm",
"config": {
"prompt": "You are a task analyzer...\nReturn JSON..."
},
"output_var": "intent"
},
{
"id": "condition",
"type": "condition",
"expression": "intent.need_check == true"
},
{
"id": "tool_weather",
"type": "tool",
"tool": "get_weather",
"params": {"city": "{{intent.city}}"},
"output_var": "weather_info"
},
{
"id": "final_reply",
"type": "llm",
"config": {
"prompt": "Based on {{weather_info}}, generate a reply..."
}
}
]
}

You can import this structure into Dify (if supported).

📘 3. Learning Resources

ResourceLinkDify Official Docshttps://docs.dify.aiYouTube: “Build AI Agents with Dify”Search on YouTubeDify Community (Discord/WeChat)Join for help and templates

🧭 Learning Path for Beginners

WeekGoalWeek 1Build a Q&A bot with DifyWeek 2Add one tool (e.g. weather, search)Week 3Create a decision-making agentWeek 4Build a real-world agent (e.g. travel planner, daily report generator)

🎉 Summary: How Beginners Can Succeed

TipExplanation🧱 Think in BlocksEach node is a step: Understand → Decide → Act → Reply🤖 LLM = BrainUse it for understanding and reasoning🔌 Tools = HandsThey do the real work (APIs, search, etc.)🖼️ Visual = CodeNo coding needed — just drag and connect🔄 Test Early, Iterate FastAdd one feature at a time

❓ FAQ

Q: I’m not a developer. Can I really do this?A: Yes! If you can use a mouse and understand logic, you can build agents.

Q: Do I need to code the tools?A: Not always. Use free APIs (like weather, translation). Only complex tools need coding.

Q: Can it remember past conversations?A: Yes! Enable session context in Dify settings.

Q: Can I connect to Slack, WeChat, or DingTalk?A: Yes! Dify supports API integration and webhooks.

📎 Next Steps

Want me to:

  • Generate a full exportable workflow file?

  • Provide a Docker-ready weather tool?

  • Help you build a custom agent (e.g. sales assistant, customer support)?

Just ask! I’ll guide you step by step. 🚀

🎯 Start now: Log in to Dify → Create a Workflow → Drag an LLM Node → Try it!Your first AI agent is just minutes away.

https://www.calcguide.tech/2025/08/28/how-to-build-an-ai-agent-with-dify/

如何基于 Dify 平台开发 Agent智能代理 - LinuxGuide 如何基于 Dify 平台开发 Agent智能代理 如何基于 Dify 平台开发 Agent智能代理,如何基于Dify平台,开发,如何基于 Dify 平台开发 Agent,Dify 平台 Agent 开发教程,Dify 如何创建智能代理,Agent 智能代理开发指南,Dify 平台低代码开发 Agent,小白也能学的 Agent 开发,Dify 平台智能代理教程,如何快速开发智能代理,Dify Agent 开发入门指南,智能代理开发步骤详解LinuxGuide

LinuxKernel全球下载站点与镜像站点统计

Linux Kernel全球下载站点与镜像站点统计

Linux Kernel全球下载与镜像站点统计,了解官方最新下载数据与全球分布情况。

关键词:Linux Kernel 下载站点统计, Linux Kernel 镜像站点分布, 全球 Linux Kernel 下载源, Kernel.org 官方下载地址, Linux 内核镜像服务器列表, Linux 内核全球下载统计, Linux Kernel 镜像站点排名, 官方 Linux 内核下载网站, Linux 内核下载源代码站点, Linux 内核全球镜像节点分析

https://www.calcguide.tech/2025/08/28/linuxkernel全球下载站点与镜像站点统计/

🌐 官方主站点

Kernel.org (官方主站)

  • 站点地址: https://www.kernel.org/

  • 适用对象: 全球所有用户,官方权威源

  • 特点:

  • 官方发布版本

  • 最新稳定版本

  • 完整的历史版本归档

  • 数字签名验证

  • 多种下载格式支持

官方FTP站点

  • FTP地址: https://ftp.kernel.org/

  • 适用对象: 需要FTP协议下载的用户

  • 特点: FTP协议访问,适合企业环境

🌍 全球镜像站点

北美地区镜像

1. 美国 - Kernel.org 主镜像

  • 站点地址: https://www.kernel.org/pub/

  • 适用对象: 北美地区用户,全球用户备选

  • 特点: 官方主镜像,速度稳定

2. 美国 - 加州大学洛杉矶分校 (UCLA)

3. 美国 - 俄勒冈州立大学 (OSU)

4. 美国 - 麻省理工学院 (MIT)

5. 加拿大 - 多伦多大学

欧洲地区镜像

6. 德国 - Kernel.org 欧洲镜像

7. 英国 - 萨里大学

8. 法国 - 法国国家数字科学与技术研究所

9. 荷兰 - NLUUG

10. 瑞典 - 斯德哥尔摩大学

11. 意大利 - 比萨大学

12. 俄罗斯 - Yandex

亚洲地区镜像

13. 中国 - 清华大学

14. 中国 - 中科大

15. 中国 - 上海交通大学

16. 中国 - 华为云

17. 中国 - 阿里云

18. 日本 - JAIST

19. 韩国 - KAIST

20. 新加坡 - 国立大学

21. 印度 - 印度理工学院

大洋洲地区镜像

22. 澳大利亚 - 澳大利亚国立大学

南美地区镜像

23. 巴西 - 圣保罗大学

📊 镜像站点性能对比

按地区推荐优先级

中国大陆用户

清华大学镜像 - 最推荐

中科大镜像 - 教育网用户首选

阿里云镜像 - 商业用户推荐

华为云镜像 - 企业用户备选

北美用户

Kernel.org主站 - 官方推荐

UCLA镜像 - 西海岸用户

MIT镜像 - 东海岸用户

欧洲用户

Kernel.org欧洲镜像 - 官方欧洲镜像

英国镜像 - 西欧用户

德国镜像 - 中欧用户

荷兰镜像 - 北欧用户

亚洲用户

日本镜像 - 东亚用户

韩国镜像 - 韩国用户

新加坡镜像 - 东南亚用户

🚀 下载方式与建议

直接下载

1
2
3
4
5
# 从官方站点下载
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.x.x.tar.xz

# 从镜像站点下载(以清华为例)
wget https://mirrors.tuna.tsinghua.edu.cn/kernel/v6.x/linux-6.x.x.tar.xz

使用Git获取

1
2
3
4
5
# 克隆官方Git仓库
git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

# 从镜像克隆(以清华为例)
git clone https://mirrors.tuna.tsinghua.edu.cn/git/linux.git

镜像同步测试

1
2
3
# 测试不同镜像的下载速度
time wget -O /dev/null https://mirrors.tuna.tsinghua.edu.cn/kernel/HEADER.html
time wget -O /dev/null https://mirrors.edge.kernel.org/pub/linux/kernel/HEADER.html

🔧 镜像站点维护信息

官方镜像列表

镜像站点要求

带宽要求: 至少100Mbps

存储要求: 至少5TB可用空间

同步频率: 每日同步

协议支持: HTTP/HTTPS/FTP/Rsync

⚠️ 安全验证

校验文件完整性

1
2
3
4
5
6
# 验证SHA256校验和
sha256sum linux-6.x.x.tar.xz

# 验证PGP签名
wget https://www.kernel.org/signature.asc
gpg --verify signature.asc linux-6.x.x.tar.sign

官方密钥

📋 镜像站点适用对象总结

镜像站点地理位置适用对象特点Kernel.org美国全球用户官方权威,最可靠清华大学中国中国大陆速度最快,教育网中科大中国中国教育网同步及时阿里云全球CDN中国企业商业稳定UCLA美国西部北美西海岸教育网高速MIT美国东部北美东海岸东海岸优化欧洲镜像欧洲欧洲用户官方欧洲镜像日本镜像日本东亚地区亚洲优化

🔄 镜像同步状态检查

实时状态监控

镜像选择建议

优先选择地理距离近的镜像

教育网用户优先选择教育网镜像

企业用户可选择商业CDN镜像

始终验证文件完整性和数字签名

这些镜像站点为全球Linux用户提供了快速、可靠的内核下载服务,用户可以根据地理位置和网络环境选择最适合的镜像站点。

LinuxKernel全球下载站点与镜像站点统计

Kali Linux Download 官方站点及镜像站点整理

https://www.calcguide.tech/2025/08/28/linuxkernel全球下载站点与镜像站点统计/

https://www.calcguide.tech/2025/08/26/linux开源软件路线图/

quotactl系统调用及示例

我们来学习一下 quotactl 这个系统调用。它主要用于管理 Linux 系统上的磁盘配额,帮助系统管理员控制用户或组可以使用的磁盘空间量 。

1. 函数介绍

quotactl 是一个 Linux 系统调用(System Call),它提供了一个底层接口来操作 Unix 和 Linux 操作系统中的磁盘配额 。简单来说,它允许你设置和查询特定用户(UID)或组(GID)在某个文件系统上可以使用的磁盘空间上限 。这对于防止某个用户或组占用过多磁盘资源,确保系统资源公平分配非常有用。

2. 函数原型

1
2
3
4
#include <sys/quota.h>

int quotactl(int cmd, const char *special, int id, caddr_t addr);

3. 功能

这个函数的主要功能是操作磁盘配额 。根据传入的 cmd 参数,它可以执行多种操作,比如设置配额限制、获取当前配额使用情况、开启或关闭文件系统的配额检查等 。

4. 参数

  • cmd: int 类型。这是一个命令参数,指定了要执行的具体操作以及配额的类型(用户配额或组配额) 。通常使用 QCMD(subcmd, type) 宏来构造这个命令。subcmd 是具体的操作(如 Q_QUOTAON, Q_QUOTAOFF, Q_GETQUOTA, Q_SETQUOTA 等),type 是配额类型(通常是 USRQUOTA 表示用户配额,GRPQUOTA 表示组配额) 。

  • special: const char * 类型。指向一个以 null 结尾的字符串,该字符串表示要操作的文件系统设备的路径名(例如 “/dev/sda1”)或挂载点(例如 “/home”) 。

  • id: int 类型。指定要操作的用户 ID (UID) 或组 ID (GID),具体取决于 cmd 中指定的类型 。

  • addr: caddr_t 类型(通常可以看作 void *)。一个地址指针,指向包含操作所需数据的缓冲区,或者用于存放函数返回的数据。具体指向的数据结构取决于 cmd 指定的操作 。例如,如果是获取配额信息 (Q_GETQUOTA),它指向一个 struct dqblk 结构体变量,函数会将结果填入其中;如果是设置配额信息 (Q_SETQUOTA),它指向一个包含新配额设置的 struct dqblk 结构体变量。

5. 返回值

  • 成功: 返回 0。

  • 失败: 返回 -1,并设置全局变量 errno 来指示具体的错误类型 。

6. 相似函数或关联函数

  • quota 命令: 这是一个用户空间的命令行工具,用于显示用户或组的磁盘配额信息。它内部可能会使用 quotactl 系统调用来获取信息。

  • quotacheck 命令: 用于扫描文件系统并创建或更新配额文件(如 aquota.user 和 aquota.group),这些文件存储了配额信息 。

  • edquota 命令: 用于编辑用户或组的磁盘配额限制,它也依赖于 quotactl 来应用更改。

  • QCMD 宏: 用于构造 quotactl 的 cmd 参数 。

  • struct dqblk: 这是与 quotactl 配合使用的一个重要结构体,用于存储或传递配额限制和使用情况的数据。

7. 示例代码

前提: 运行这些示例代码需要 root 权限,因为管理配额是系统管理员的任务。同时,目标文件系统需要支持并已启用配额功能。

示例 1: 获取用户磁盘配额信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <stdio.h>
#include <unistd.h>
#include <sys/quota.h>
#include <errno.h>
#include <string.h>

int main() {
// 假设我们要查询 /home 文件系统的用户配额
const char *mount_point = "/home";
// 假设我们要查询 UID 为 1001 的用户的配额
int user_id = 1001;
struct dqblk quota_info;
int result;

// 使用 QCMD 宏构造 cmd 参数:操作是 Q_GETQUOTA,类型是 USRQUOTA (用户配额)
result = quotactl(QCMD(Q_GETQUOTA, USRQUOTA), mount_point, user_id, (caddr_t)&quota_info);

if (result == -1) {
perror("quotactl"); // 打印错误信息
fprintf(stderr, "Failed to get quota info for user %d on %s\n", user_id, mount_point);
return 1;
}

// 打印获取到的配额信息
// 注意: 数值通常以 1KB 块为单位
printf("User ID: %d\n", user_id);
printf("Block Hard Limit: %llu KB\n", (unsigned long long)quota_info.dqb_bhardlimit);
printf("Block Soft Limit: %llu KB\n", (unsigned long long)quota_info.dqb_bsoftlimit);
printf("Current Blocks Used: %llu KB\n", (unsigned long long)quota_info.dqb_curblocks);
printf("Inode Hard Limit: %llu\n", (unsigned long long)quota_info.dqb_ihardlimit);
printf("Inode Soft Limit: %llu\n", (unsigned long long)quota_info.dqb_isoftlimit);
printf("Current Inodes Used: %llu\n", (unsigned long long)quota_info.dqb_curinodes);
// dqb_btime 和 dqb_itime 是宽限时间,当超过软限制时才相关
// printf("Block Grace Time Expiry: %lu\n", quota_info.dqb_btime);
// printf("Inode Grace Time Expiry: %lu\n", quota_info.dqb_itime);

return 0;
}

示例 2: 设置用户磁盘配额限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <stdio.h>
#include <unistd.h>
#include <sys/quota.h>
#include <errno.h>
#include <string.h>
#include <time.h> // 需要设置宽限时间

int main() {
// 假设我们要设置 /home 文件系统的用户配额
const char *mount_point = "/home";
// 假设我们要设置 UID 为 1002 的用户的配额
int user_id = 1002;
struct dqblk new_quota_limits;
int result;

// 初始化结构体
memset(&new_quota_limits, 0, sizeof(new_quota_limits));

// 设置块(磁盘空间)限制 (单位通常是 1KB 块)
new_quota_limits.dqb_bhardlimit = 500000; // 硬限制 500MB
new_quota_limits.dqb_bsoftlimit = 400000; // 软限制 400MB

// 设置 inode(文件数量)限制
new_quota_limits.dqb_ihardlimit = 5000; // 硬限制 5000 个文件
new_quota_limits.dqb_isoftlimit = 4000; // 软限制 4000 个文件

// 设置宽限时间 (秒) - 当超过软限制时,用户还有这段时间可以清理,之后硬限制生效
// 如果不设置或设置为0,可能会使用默认值或导致软限制行为异常
new_quota_limits.dqb_btime = time(NULL) + 7 * 24 * 60 * 60; // 7天后
new_quota_limits.dqb_itime = time(NULL) + 7 * 24 * 60 * 60; // 7天后
// 设置有效的字段标志,告诉内核我们要设置哪些字段
new_quota_limits.dqb_valid = QIF_LIMITS | QIF_TIMES; // 设置限制和时间

// 使用 QCMD 宏构造 cmd 参数:操作是 Q_SETQUOTA,类型是 USRQUOTA (用户配额)
result = quotactl(QCMD(Q_SETQUOTA, USRQUOTA), mount_point, user_id, (caddr_t)&new_quota_limits);

if (result == -1) {
perror("quotactl"); // 打印错误信息
fprintf(stderr, "Failed to set quota limits for user %d on %s\n", user_id, mount_point);
return 1;
}

printf("Successfully set quota limits for user %d on %s\n", user_id, mount_point);
printf("Block Hard Limit: %llu KB, Soft Limit: %llu KB\n",
(unsigned long long)new_quota_limits.dqb_bhardlimit,
(unsigned long long)new_quota_limits.dqb_bsoftlimit);
printf("Inode Hard Limit: %llu, Soft Limit: %llu\n",
(unsigned long long)new_quota_limits.dqb_ihardlimit,
(unsigned long long)new_quota_limits.dqb_isoftlimit);

return 0;
}

编译和运行:

1
2
3
4
5
6
7
8
9
10
# 假设代码保存在 get_quota.c 和 set_quota.c 中
# 需要 root 权限编译和运行
sudo gcc -o get_quota get_quota.c
sudo gcc -o set_quota set_quota.c

# 运行前确保 /home 文件系统启用了用户配额
# 运行示例 (需要 root 权限)
sudo ./get_quota
sudo ./set_quota

请注意,实际使用中需要确保文件系统已经正确配置并启用了配额功能,这通常涉及在 /etc/fstab 中添加 usrquota 或 grpquota 选项,并使用 quotacheck 和 quotaon 命令初始化和启用配额。

quotactl 系统调用, quotactl 命令详解, linux quotactl 用法, quotactl 示例代码, linux 系统调用 quotactl, quotactl 函数说明, linux 磁盘配额管理, 系统调用 quotactl 解析, quotactl 实战教程, linux quotactl 教程

相关文章:

quotactl系统调用及示例-CSDN博客

quotactl(2) - Linux manual page

https://www.calcguide.tech/2025/08/28/quotactl系统调用及示例/

query_module系统调用及示例

query_module 函数详解

  1. 函数介绍

query_module 是 Linux 系统中用于查询内核模块信息的系统调用。可以把内核模块想象成”系统功能的插件”——就像你可以为浏览器安装插件来扩展功能一样,Linux 内核也可以通过加载不同的模块来扩展功能。

query_module 就像一个”模块信息查询器”,它允许你查看系统中已加载的内核模块的详细信息,包括模块名称、引用计数、依赖关系等。

  1. 函数原型
1
2
3
4
5
#include <linux/module.h>

int query_module(const char *name, int which, void *buf,
size_t bufsize, size_t *ret);

  1. 功能

query_module 函数用于查询内核模块的各种信息。它可以获取模块列表、模块引用计数、模块符号信息、模块依赖关系等。

  1. 参数
  • name: 要查询的模块名称(NULL 表示查询所有模块)

  • which: 查询类型(指定要获取的信息类型)

  • buf: 指向缓冲区的指针,用于存储返回的信息

  • bufsize: 缓冲区大小

  • ret: 指向返回值的指针

  1. 查询类型(which 参数)

类型值说明QM_MODULES1获取模块名称列表QM_REFS2获取模块引用计数QM_SYMBOLS3获取模块符号信息QM_INFO4获取模块详细信息QM_MODINFO5获取模块信息(新版本)

  1. module_info 结构体
1
2
3
4
5
6
7
8
struct module_info {
unsigned long addr; /* 模块地址 */
unsigned long size; /* 模块大小 */
unsigned long flags; /* 模块标志 */
long refcnt; /* 引用计数 */
unsigned char usecount; /* 使用计数 */
};

  1. 返回值
  • 成功: 返回 0

  • 失败: 返回 -1,并设置相应的 errno 错误码

  1. 常见错误码
  • EPERM: 权限不足(通常需要 root 权限)

  • EINVAL: 参数无效

  • ENOENT: 指定的模块不存在

  • ENOMEM: 内存不足

  • EFAULT: 参数指针无效

  • ENOSYS: 系统不支持此调用

  1. 相似函数或关联函数
  • lsmod: 命令行工具列出模块

  • insmod: 加载内核模块

  • rmmod: 卸载内核模块

  • modprobe: 智能加载内核模块

  • /proc/modules: 模块信息文件

  • /sys/module/: 模块 sysfs 接口

  • kmod: 内核模块管理工具

  1. 示例代码

示例1:基础用法 - 查询模块列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/module.h>
#include <errno.h>
#include <string.h>

// 注意:query_module 在现代 Linux 系统中可能不可用
// 这里提供概念性示例和现代替代方案

int main() {
printf("=== query_module 基础示例 ===\n\n");

printf("注意: query_module 是已废弃的系统调用\n");
printf("在现代 Linux 系统中通常不可用\n\n");

printf("功能说明:\n");
printf("query_module 用于查询内核模块信息:\n");
printf("1. QM_MODULES: 获取模块名称列表\n");
printf("2. QM_REFS: 获取模块引用计数\n");
printf("3. QM_SYMBOLS: 获取模块符号信息\n");
printf("4. QM_INFO: 获取模块详细信息\n");
printf("5. QM_MODINFO: 获取模块信息(新版本)\n");

printf("\n=== 现代替代方案 ===\n");
printf("1. 使用 /proc/modules 文件\n");
printf("2. 使用 lsmod 命令\n");
printf("3. 使用 modinfo 命令\n");
printf("4. 使用 /sys/module/ 接口\n");

return 0;
}

示例2:现代替代方案 - 模块信息查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>

// 通过 /proc/modules 获取模块信息
int get_modules_via_proc() {
printf("=== 通过 /proc/modules 获取模块信息 ===\n");

FILE *fp = fopen("/proc/modules", "r");
if (!fp) {
perror("打开 /proc/modules 失败");
return -1;
}

printf("%-25s %-10s %-8s %-15s %s\n",
"模块名称", "大小", "使用数", "被谁使用", "状态");
printf("%-25s %-10s %-8s %-15s %s\n",
"--------", "----", "----", "------", "----");

char line&#91;512];
int count = 0;

while (fgets(line, sizeof(line), fp) && count < 20) {
char module_name&#91;64];
unsigned long size;
int use_count;
char used_by&#91;256];
char status&#91;32];
char address&#91;32];

// 解析 /proc/modules 格式
int parsed = sscanf(line, "%63s %lu %d %255s %31s %31s",
module_name, &size, &use_count, used_by, status, address);

if (parsed >= 4) {
printf("%-25s %-10lu %-8d %-15s %s\n",
module_name, size, use_count, used_by,
parsed >= 5 ? status : "-");
count++;
}
}

fclose(fp);

if (count == 0) {
printf("没有模块信息\n");
} else {
printf("\n显示了前 %d 个模块\n", count);
}

return 0;
}

// 通过 /sys/module/ 获取模块详细信息
int get_module_details_via_sysfs() {
printf("\n=== 通过 /sys/module/ 获取模块详细信息 ===\n");

// 检查 /sys/module/ 目录
if (access("/sys/module", F_OK) != 0) {
printf("系统不支持 /sys/module/ 接口\n");
return -1;
}

// 列出几个模块的详细信息
const char *modules&#91;] = {"usbcore", "tcp", "ext4", "loop", NULL};

for (int i = 0; modules&#91;i]; i++) {
char module_path&#91;256];
snprintf(module_path, sizeof(module_path), "/sys/module/%s", modules&#91;i]);

if (access(module_path, F_OK) == 0) {
printf("\n模块: %s\n", modules&#91;i]);

// 检查模块参数
char param_path&#91;256];
snprintf(param_path, sizeof(param_path), "%s/parameters", module_path);
if (access(param_path, F_OK) == 0) {
printf(" ✓ 支持参数配置\n");
}

// 检查模块引用
char refcnt_path&#91;256];
snprintf(refcnt_path, sizeof(refcnt_path), "%s/refcnt", module_path);
FILE *refcnt_fp = fopen(refcnt_path, "r");
if (refcnt_fp) {
char refcnt&#91;32];
if (fgets(refcnt, sizeof(refcnt), refcnt_fp)) {
printf(" 引用计数: %s", refcnt);
}
fclose(refcnt_fp);
}

// 检查模块状态
char initstate_path&#91;256];
snprintf(initstate_path, sizeof(initstate_path), "%s/initstate", module_path);
FILE *initstate_fp = fopen(initstate_path, "r");
if (initstate_fp) {
char initstate&#91;32];
if (fgets(initstate, sizeof(initstate), initstate_fp)) {
printf(" 初始化状态: %s", initstate);
}
fclose(initstate_fp);
}
}
}

return 0;
}

// 模拟 query_module 功能
void simulate_query_module() {
printf("\n=== 模拟 query_module 功能 ===\n");

printf("QM_MODULES (获取模块列表):\n");
printf(" 模块1: usbcore\n");
printf(" 模块2: tcp\n");
printf(" 模块3: ext4\n");
printf(" 模块4: loop\n");
printf(" 模块5: sd_mod\n");

printf("\nQM_INFO (获取模块详细信息):\n");
printf(" 模块名称: usbcore\n");
printf(" 地址: 0xffffffffc0000000\n");
printf(" 大小: 123456 字节\n");
printf(" 引用计数: 5\n");
printf(" 状态: Live\n");

printf("\nQM_REFS (获取引用计数):\n");
printf(" usbcore: 5\n");
printf(" tcp: 3\n");
printf(" ext4: 2\n");
printf(" loop: 1\n");

printf("\nQM_SYMBOLS (获取符号信息):\n");
printf(" usb_register: 0xffffffffc0001000\n");
printf(" usb_deregister: 0xffffffffc0002000\n");
printf(" usb_submit_urb: 0xffffffffc0003000\n");
}

int main() {
printf("=== 内核模块查询系统 ===\n\n");

// 显示系统信息
printf("系统信息:\n");
printf(" 用户 ID: %d\n", getuid());
printf(" 进程 ID: %d\n", getpid());

// 检查权限
if (getuid() != 0) {
printf(" 注意: 某些模块信息需要 root 权限\n");
}
printf("\n");

// 通过 /proc/modules 获取信息
get_modules_via_proc();

// 通过 /sys/module/ 获取详细信息
get_module_details_via_sysfs();

// 模拟 query_module 功能
simulate_query_module();

printf("\n=== 现代模块管理工具 ===\n");
printf("常用的模块管理命令:\n");
printf("1. lsmod # 列出已加载模块\n");
printf("2. modinfo module # 显示模块信息\n");
printf("3. insmod module.ko # 加载模块\n");
printf("4. rmmod module # 卸载模块\n");
printf("5. modprobe module # 智能加载模块\n");
printf("6. depmod # 生成模块依赖\n");
printf("\n");

printf("模块信息文件:\n");
printf("1. /proc/modules # 已加载模块列表\n");
printf("2. /proc/devices # 设备号信息\n");
printf("3. /sys/module/ # 模块 sysfs 接口\n");
printf("4. /lib/modules/ # 模块文件目录\n");

return 0;
}

示例3:完整的模块管理工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>

// 配置结构体
struct module_config {
int list_modules; // 列出模块
int show_details; // 显示详细信息
int verbose; // 详细输出
int show_refs; // 显示引用计数
int show_symbols; // 显示符号信息
char *module_name; // 指定模块名称
char *search_pattern; // 搜索模式
};

// 模块信息结构体
struct module_info {
char name&#91;256];
unsigned long size;
int ref_count;
char used_by&#91;512];
char status&#91;32];
unsigned long address;
};

// 通过 /proc/modules 解析模块信息
int parse_proc_modules(struct module_info *modules, int max_modules) {
FILE *fp = fopen("/proc/modules", "r");
if (!fp) {
return -1;
}

int count = 0;
char line&#91;1024];

while (fgets(line, sizeof(line), fp) && count < max_modules) {
struct module_info *mod = &modules&#91;count];
char extra&#91;512];

// 解析 /proc/modules 格式
int parsed = sscanf(line, "%255s %lu %d %511s %31s %31s",
mod->name, &mod->size, &mod->ref_count,
mod->used_by, mod->status, extra);

if (parsed >= 4) {
count++;
}
}

fclose(fp);
return count;
}

// 显示模块列表
void show_module_list(struct module_info *modules, int count,
const struct module_config *config) {
printf("=== 内核模块列表 ===\n");
printf("%-25s %-12s %-8s %-20s %s\n",
"模块名称", "大小", "引用数", "被谁使用", "状态");
printf("%-25s %-12s %-8s %-20s %s\n",
"--------", "----", "----", "------", "----");

int displayed = 0;
for (int i = 0; i < count && displayed < 50; i++) {
struct module_info *mod = &modules&#91;i];

// 如果指定了搜索模式,进行过滤
if (config->search_pattern) {
if (strstr(mod->name, config->search_pattern) == NULL) {
continue;
}
}

// 如果指定了模块名称,精确匹配
if (config->module_name) {
if (strcmp(mod->name, config->module_name) != 0) {
continue;
}
}

printf("%-25s %-12lu %-8d %-20s %s\n",
mod->name, mod->size, mod->ref_count,
mod->used_by, mod->status);
displayed++;
}

if (displayed == 0) {
printf("没有找到匹配的模块\n");
} else if (displayed < count) {
printf("\n显示了 %d 个匹配模块 (总共 %d 个)\n", displayed, count);
}
}

// 显示模块详细信息
void show_module_details(struct module_info *modules, int count,
const struct module_config *config) {
printf("\n=== 模块详细信息 ===\n");

int found = 0;
for (int i = 0; i < count; i++) {
struct module_info *mod = &modules&#91;i];

// 检查是否匹配
if (config->module_name) {
if (strcmp(mod->name, config->module_name) != 0) {
continue;
}
} else if (config->search_pattern) {
if (strstr(mod->name, config->search_pattern) == NULL) {
continue;
}
}

printf("\n模块: %s\n", mod->name);
printf(" 大小: %lu 字节 (%.2f KB)\n", mod->size, (double)mod->size / 1024);
printf(" 引用计数: %d\n", mod->ref_count);
printf(" 被谁使用: %s\n", mod->used_by);
printf(" 状态: %s\n", mod->status);

// 显示 sysfs 信息
char sysfs_path&#91;256];
snprintf(sysfs_path, sizeof(sysfs_path), "/sys/module/%s", mod->name);

if (access(sysfs_path, F_OK) == 0) {
printf(" Sysfs 路径: %s\n", sysfs_path);

// 检查参数
char param_path&#91;256];
snprintf(param_path, sizeof(param_path), "%s/parameters", sysfs_path);
if (access(param_path, F_OK) == 0) {
printf(" ✓ 支持运行时参数配置\n");
}

// 检查引用计数
char refcnt_path&#91;256];
snprintf(refcnt_path, sizeof(refcnt_path), "%s/refcnt", sysfs_path);
FILE *refcnt_fp = fopen(refcnt_path, "r");
if (refcnt_fp) {
char refcnt&#91;32];
if (fgets(refcnt, sizeof(refcnt), refcnt_fp)) {
printf(" 当前引用计数: %s", refcnt);
}
fclose(refcnt_fp);
}
}

found++;
if (config->module_name) {
break; // 只显示指定模块
}

if (found >= 5) {
printf("\n只显示前 5 个匹配模块的详细信息\n");
break;
}
}

if (found == 0) {
if (config->module_name) {
printf("未找到模块: %s\n", config->module_name);
} else if (config->search_pattern) {
printf("未找到匹配模式 '%s' 的模块\n", config->search_pattern);
} else {
printf("没有模块信息\n");
}
}
}

// 显示系统模块统计
void show_module_statistics(struct module_info *modules, int count) {
printf("\n=== 模块统计信息 ===\n");
printf("总模块数: %d\n", count);

if (count > 0) {
// 统计引用计数
int total_refs = 0;
int max_refs = 0;
int zero_refs = 0;
unsigned long total_size = 0;

for (int i = 0; i < count; i++) {
total_refs += modules&#91;i].ref_count;
total_size += modules&#91;i].size;

if (modules&#91;i].ref_count > max_refs) {
max_refs = modules&#91;i].ref_count;
}

if (modules&#91;i].ref_count == 0) {
zero_refs++;
}
}

printf("总大小: %.2f MB\n", (double)total_size / (1024 * 1024));
printf("平均引用计数: %.2f\n", (double)total_refs / count);
printf("最大引用计数: %d\n", max_refs);
printf("零引用模块数: %d\n", zero_refs);

// 显示最常见的模块
printf("\n最常用的模块:\n");
int shown = 0;
for (int i = 0; i < count && shown < 5; i++) {
if (modules&#91;i].ref_count > 0) {
printf(" %s (%d 引用)\n", modules&#91;i].name, modules&#91;i].ref_count);
shown++;
}
}
}
}

// 显示帮助信息
void show_help(const char *program_name) {
printf("用法: %s &#91;选项]\n", program_name);
printf("\n选项:\n");
printf(" -l, --list 列出所有模块\n");
printf(" -d, --details 显示模块详细信息\n");
printf(" -r, --refs 显示引用计数\n");
printf(" -n, --name=MODULE 指定模块名称\n");
printf(" -s, --search=PATTERN 搜索模块名称\n");
printf(" -v, --verbose 详细输出\n");
printf(" -S, --statistics 显示统计信息\n");
printf(" -h, --help 显示此帮助信息\n");
printf("\n示例:\n");
printf(" %s -l # 列出所有模块\n", program_name);
printf(" %s -d -n usbcore # 显示 usbcore 模块详细信息\n", program_name);
printf(" %s -l -s usb # 列出包含 usb 的模块\n", program_name);
printf(" %s -S # 显示模块统计信息\n", program_name);
printf(" %s -d -s network # 显示网络相关模块详情\n", program_name);
}

int main(int argc, char *argv&#91;]) {
struct module_config config = {
.list_modules = 0,
.show_details = 0,
.verbose = 0,
.show_refs = 0,
.show_symbols = 0,
.module_name = NULL,
.search_pattern = NULL
};

printf("=== 内核模块查询工具 ===\n\n");

// 解析命令行参数
static struct option long_options&#91;] = {
{"list", no_argument, 0, 'l'},
{"details", no_argument, 0, 'd'},
{"refs", no_argument, 0, 'r'},
{"symbols", no_argument, 0, 'y'},
{"name", required_argument, 0, 'n'},
{"search", required_argument, 0, 's'},
{"verbose", no_argument, 0, 'v'},
{"statistics", no_argument, 0, 'S'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};

int opt;
while ((opt = getopt_long(argc, argv, "ldryn:s:vSh", long_options, NULL)) != -1) {
switch (opt) {
case 'l':
config.list_modules = 1;
break;
case 'd':
config.show_details = 1;
break;
case 'r':
config.show_refs = 1;
break;
case 'y':
config.show_symbols = 1;
break;
case 'n':
config.module_name = optarg;
break;
case 's':
config.search_pattern = optarg;
break;
case 'v':
config.verbose = 1;
break;
case 'S':
config.list_modules = 1;
break;
case 'h':
show_help(argv&#91;0]);
return 0;
default:
fprintf(stderr, "使用 '%s --help' 查看帮助信息\n", argv&#91;0]);
return 1;
}
}

// 如果没有指定操作,默认列出模块
if (!config.list_modules && !config.show_details &&
!config.show_refs && !config.show_symbols) {
config.list_modules = 1;
}

// 显示系统信息
if (config.verbose) {
printf("系统信息:\n");
printf(" 用户 ID: %d\n", getuid());
printf(" 进程 ID: %d\n", getpid());
printf(" 当前目录: %s\n", getcwd(NULL, 0));
printf("\n");
}

// 读取模块信息
struct module_info modules&#91;500];
int module_count = parse_proc_modules(modules, 500);

if (module_count == -1) {
printf("错误: 无法读取模块信息\n");
printf("可能的原因:\n");
printf("1. 系统不支持 /proc/modules\n");
printf("2. 权限不足\n");
printf("3. 内核不支持模块\n");
return 1;
}

if (config.verbose) {
printf("读取到 %d 个模块\n\n", module_count);
}

// 执行相应操作
if (config.list_modules) {
show_module_list(modules, module_count, &config);
}

if (config.show_details) {
show_module_details(modules, module_count, &config);
}

if (optind == 1) { // 如果没有指定参数,显示统计信息
show_module_statistics(modules, module_count);
}

printf("\n=== 模块管理最佳实践 ===\n");
printf("模块安全建议:\n");
printf("1. 只加载可信的模块\n");
printf("2. 定期更新模块\n");
printf("3. 监控模块加载活动\n");
printf("4. 限制模块加载权限\n");
printf("5. 使用签名验证模块\n");
printf("\n");

printf("模块性能优化:\n");
printf("1. 卸载不用的模块\n");
printf("2. 优化模块参数\n");
printf("3. 监控模块内存使用\n");
printf("4. 使用模块黑名单\n");
printf("5. 合理配置模块依赖\n");
printf("\n");

printf("现代替代方案:\n");
printf("1. /proc/modules: 模块列表信息\n");
printf("2. /sys/module/: 模块 sysfs 接口\n");
printf("3. modinfo: 模块详细信息\n");
printf("4. lsmod: 列出模块\n");
printf("5. modprobe: 智能模块加载\n");
printf("6. rmmod: 卸载模块\n");

return 0;
}

编译和运行说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 编译示例程序
gcc -o query_module_example1 example1.c
gcc -o query_module_example2 example2.c
gcc -o query_module_example3 example3.c

# 运行示例
./query_module_example1
./query_module_example2
./query_module_example3 --help
./query_module_example3 -l
./query_module_example3 -d -n usbcore
./query_module_example3 -l -s usb
./query_module_example3 -S

系统要求检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 检查模块支持
ls /proc/modules
ls /sys/module/

# 检查模块工具
which lsmod
which modinfo
which modprobe
which rmmod

# 查看内核模块目录
ls /lib/modules/$(uname -r)/kernel/

# 检查内核配置
grep -i module /boot/config-$(uname -r)

重要注意事项

已废弃: query_module 在现代 Linux 系统中已废弃

权限要求: 某些模块信息需要 root 权限

系统依赖: 需要内核支持模块和相应的文件系统接口

错误处理: 始终检查返回值和 errno

兼容性: 不同内核版本可能有差异

实际应用场景

系统监控: 监控内核模块加载和卸载

安全审计: 审计系统中加载的模块

性能分析: 分析模块对系统性能的影响

故障诊断: 诊断模块相关的问题

自动化管理: 自动化模块管理脚本

现代替代方案详解

使用 /proc/modules

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 读取模块列表
int read_module_list() {
FILE *fp = fopen("/proc/modules", "r");
if (!fp) return -1;

char line&#91;1024];
while (fgets(line, sizeof(line), fp)) {
char name&#91;256], used_by&#91;256], status&#91;32];
unsigned long size;
int ref_count;

if (sscanf(line, "%255s %lu %d %255s %31s",
name, &size, &ref_count, used_by, status) >= 4) {
printf("%-20s %8lu %3d %s\n", name, size, ref_count, used_by);
}
}

fclose(fp);
return 0;
}

使用 /sys/module/ 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 读取模块详细信息
int read_module_details(const char *module_name) {
char path&#91;256];

// 读取引用计数
snprintf(path, sizeof(path), "/sys/module/%s/refcnt", module_name);
FILE *fp = fopen(path, "r");
if (fp) {
char refcnt&#91;32];
if (fgets(refcnt, sizeof(refcnt), fp)) {
printf("引用计数: %s", refcnt);
}
fclose(fp);
}

return 0;
}

使用命令行工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 列出模块
lsmod

# 显示模块信息
modinfo module_name

# 加载模块
sudo modprobe module_name

# 卸载模块
sudo rmmod module_name

# 生成依赖关系
sudo depmod

最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 安全的模块信息查询函数
int safe_query_modules(struct module_info *modules, int max_count) {
// 验证参数
if (!modules || max_count <= 0) {
errno = EINVAL;
return -1;
}

// 检查文件存在性
if (access("/proc/modules", F_OK) != 0) {
errno = ENOENT;
return -1;
}

// 读取模块信息
return parse_proc_modules(modules, max_count);
}

// 模块安全检查
int validate_module_security(const char *module_name) {
// 检查模块名称合法性
if (!module_name || strlen(module_name) == 0) {
return -1;
}

// 检查是否在黑名单中
// 这里简化处理,实际应用中可以检查 /etc/modprobe.d/blacklist.conf
const char *blacklisted&#91;] = {"firewire-sbp2", "usbmouse", NULL};
for (int i = 0; blacklisted&#91;i]; i++) {
if (strcmp(module_name, blacklisted&#91;i]) == 0) {
printf("警告: 模块 '%s' 在黑名单中\n", module_name);
return -1;
}
}

return 0;
}

这些示例展示了 query_module 函数的概念以及现代 Linux 系统中查询内核模块信息的各种替代方案,帮助你全面了解 Linux 系统中的模块管理机制。

query_module系统调用及示例-CSDN博客

https://www.calcguide.tech/2025/08/28/query-module系统调用及示例/

readahead系统调用及示例

readahead 函数详解

  1. 函数介绍

readahead 是一个Linux系统调用,用于预读文件数据到内核页面缓存中。它允许应用程序提示内核提前读取指定文件区域的数据,从而提高后续读取操作的性能。这个函数特别适用于顺序访问大文件的场景,可以减少I/O等待时间。

  1. 函数原型
1
2
3
4
#define _GNU_SOURCE
#include <fcntl.h>
ssize_t readahead(int fd, off64_t offset, size_t count);

  1. 功能

readahead 向内核发出预读提示,建议内核提前将文件中从 offset 开始的 count 字节数据读入页面缓存。这是一个非阻塞操作,不会立即读取数据,而是让内核在适当的时候进行预读。

  1. 参数
  • int fd: 文件描述符,必须是已打开的文件(通常需要支持预读的文件系统)

  • off64_t offset: 文件中的偏移量,指定预读开始位置

  • size_t count: 预读的字节数,内核可能根据策略调整实际预读量

  1. 返回值
  • 成功: 返回0,表示预读请求已提交

  • 失败: 返回-1,并设置errno

  1. 相似函数,或关联函数
  • posix_fadvise: 文件访问建议接口,包含预读建议

  • mmap: 内存映射文件,可以配合MAP_POPULATE使用

  • read: 基本读取函数

  • lseek: 文件定位函数

  1. 示例代码

示例1:基础预读示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#define _GNU_SOURCE
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <time.h>

/**
* 创建大文件用于测试
*/
int create_test_file(const char *filename, size_t size) {
int fd;
char *buffer;
size_t chunk_size = 1024 * 1024; // 1MB chunks
size_t written = 0;

fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建测试文件失败");
return -1;
}

buffer = malloc(chunk_size);
if (!buffer) {
perror("分配缓冲区失败");
close(fd);
return -1;
}

// 填充测试数据
for (size_t i = 0; i < chunk_size; i++) {
buffer&#91;i] = 'A' + (i % 26);
}

printf("正在创建 %zu MB 的测试文件...\n", size / (1024 * 1024));

while (written < size) {
size_t to_write = (size - written < chunk_size) ? size - written : chunk_size;
ssize_t result = write(fd, buffer, to_write);
if (result == -1) {
perror("写入文件失败");
free(buffer);
close(fd);
return -1;
}
written += result;
}

free(buffer);
close(fd);
printf("测试文件创建完成\n");
return 0;
}

/**
* 测量读取时间
*/
double time_read_operation(int fd, void *buffer, size_t size) {
struct timespec start, end;
ssize_t total_read = 0;
off_t offset = 0;

clock_gettime(CLOCK_MONOTONIC, &start);

while (total_read < (ssize_t)size) {
ssize_t to_read = (size - total_read < 1024 * 1024) ? size - total_read : 1024 * 1024;
ssize_t result = pread(fd, (char*)buffer + total_read, to_read, offset);
if (result == -1) {
perror("读取文件失败");
return -1;
}
if (result == 0) break; // 文件结束

total_read += result;
offset += result;
}

clock_gettime(CLOCK_MONOTONIC, &end);

return (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
}

/**
* 演示readahead的基本使用
*/
int demo_readahead_basic() {
const char *filename = "test_readahead.dat";
const size_t file_size = 50 * 1024 * 1024; // 50MB
int fd;
char *buffer;
double time_without, time_with;

printf("=== readahead 基本使用示例 ===\n");

// 创建测试文件
if (create_test_file(filename, file_size) != 0) {
return -1;
}

// 分配读取缓冲区
buffer = malloc(file_size);
if (!buffer) {
perror("分配读取缓冲区失败");
unlink(filename);
return -1;
}

// 测试不使用readahead的读取性能
fd = open(filename, O_RDONLY);
if (fd == -1) {
perror("打开文件失败");
free(buffer);
unlink(filename);
return -1;
}

printf("第一次读取(无预读)...\n");
time_without = time_read_operation(fd, buffer, file_size);
if (time_without > 0) {
printf("无预读读取时间: %.3f 秒\n", time_without);
}

close(fd);

// 测试使用readahead的读取性能
fd = open(filename, O_RDONLY);
if (fd == -1) {
perror("打开文件失败");
free(buffer);
unlink(filename);
return -1;
}

// 使用readahead预读整个文件
printf("执行预读操作...\n");
if (readahead(fd, 0, file_size) == 0) {
printf("预读请求提交成功\n");
} else {
printf("预读请求失败: %s\n", strerror(errno));
}

// 等待一小段时间让预读完成
sleep(1);

printf("第二次读取(有预读)...\n");
time_with = time_read_operation(fd, buffer, file_size);
if (time_with > 0) {
printf("有预读读取时间: %.3f 秒\n", time_with);
if (time_without > 0) {
printf("性能提升: %.1f%%\n",
(time_without - time_with) / time_without * 100);
}
}

close(fd);
free(buffer);
unlink(filename);

return 0;
}

int main() {
return demo_readahead_basic();
}

示例2:分段预读示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#define _GNU_SOURCE
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <time.h>

/**
* 演示分段预读的使用
*/
int demo_readahead_segmented() {
const char *filename = "segmented_test.dat";
const size_t file_size = 100 * 1024 * 1024; // 100MB
const size_t segment_size = 10 * 1024 * 1024; // 10MB per segment
int fd;
char *buffer;
struct timespec start, end;
double total_time = 0;

printf("=== readahead 分段预读示例 ===\n");

// 创建测试文件
int test_fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (test_fd == -1) {
perror("创建测试文件失败");
return -1;
}

buffer = malloc(segment_size);
if (!buffer) {
perror("分配缓冲区失败");
close(test_fd);
unlink(filename);
return -1;
}

// 填充测试数据
for (size_t i = 0; i < segment_size; i++) {
buffer&#91;i] = 'A' + (i % 26);
}

// 写入文件数据
for (size_t offset = 0; offset < file_size; offset += segment_size) {
write(test_fd, buffer, segment_size);
}

close(test_fd);
printf("创建了 %zu MB 的测试文件\n", file_size / (1024 * 1024));

// 打开文件进行测试
fd = open(filename, O_RDONLY);
if (fd == -1) {
perror("打开文件失败");
free(buffer);
unlink(filename);
return -1;
}

printf("开始分段读取测试...\n");

// 分段预读和读取
for (size_t offset = 0; offset < file_size; offset += segment_size) {
printf("处理段 %zu/%zu MB\n",
(offset + segment_size) / (1024 * 1024),
file_size / (1024 * 1024));

// 预读当前段
clock_gettime(CLOCK_MONOTONIC, &start);
if (readahead(fd, offset, segment_size) == 0) {
// printf(" 预读段 %zu 完成\n", offset / segment_size);
} else {
printf(" 预读段 %zu 失败: %s\n", offset / segment_size, strerror(errno));
}

// 等待预读完成(实际应用中可能不需要)
usleep(100000); // 100ms

// 读取当前段
ssize_t bytes_read = pread(fd, buffer, segment_size, offset);
if (bytes_read == -1) {
perror("读取段失败");
break;
}

clock_gettime(CLOCK_MONOTONIC, &end);
double segment_time = (end.tv_sec - start.tv_sec) +
(end.tv_nsec - start.tv_nsec) / 1e9;
total_time += segment_time;

printf(" 段处理时间: %.3f 秒\n", segment_time);
}

printf("\n总处理时间: %.3f 秒\n", total_time);
printf("平均段处理时间: %.3f 秒\n", total_time / (file_size / segment_size));

close(fd);
free(buffer);
unlink(filename);

return 0;
}

int main() {
return demo_readahead_segmented();
}

示例3:与posix_fadvise对比示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#define _GNU_SOURCE
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <time.h>
#include <fcntl.h>

/**
* 创建测试文件
*/
int create_large_file(const char *filename, size_t size) {
int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建文件失败");
return -1;
}

char *buffer = malloc(1024 * 1024);
if (!buffer) {
perror("分配缓冲区失败");
close(fd);
return -1;
}

// 填充数据
for (int i = 0; i < 1024 * 1024; i++) {
buffer&#91;i] = 'A' + (i % 26);
}

size_t written = 0;
while (written < size) {
size_t to_write = (size - written < 1024 * 1024) ? size - written : 1024 * 1024;
ssize_t result = write(fd, buffer, to_write);
if (result == -1) {
perror("写入文件失败");
free(buffer);
close(fd);
return -1;
}
written += result;
}

free(buffer);
close(fd);
return 0;
}

/**
* 使用readahead进行预读
*/
int test_readahead_method(const char *filename) {
int fd = open(filename, O_RDONLY);
if (fd == -1) {
perror("打开文件失败");
return -1;
}

struct stat sb;
if (fstat(fd, &sb) == -1) {
perror("获取文件状态失败");
close(fd);
return -1;
}

// 使用readahead预读
if (readahead(fd, 0, sb.st_size) == 0) {
printf("使用readahead预读成功\n");
} else {
printf("使用readahead预读失败: %s\n", strerror(errno));
}

close(fd);
return 0;
}

/**
* 使用posix_fadvise进行预读
*/
int test_fadvise_method(const char *filename) {
int fd = open(filename, O_RDONLY);
if (fd == -1) {
perror("打开文件失败");
return -1;
}

struct stat sb;
if (fstat(fd, &sb) == -1) {
perror("获取文件状态失败");
close(fd);
return -1;
}

// 使用posix_fadvise预读
if (posix_fadvise(fd, 0, sb.st_size, POSIX_FADV_WILLNEED) == 0) {
printf("使用posix_fadvise预读成功\n");
} else {
printf("使用posix_fadvise预读失败: %s\n", strerror(errno));
}

close(fd);
return 0;
}

/**
* 演示readahead与posix_fadvise的对比
*/
int demo_readahead_vs_fadvise() {
const char *filename = "comparison_test.dat";
const size_t file_size = 50 * 1024 * 1024; // 50MB

printf("=== readahead vs posix_fadvise 对比示例 ===\n");

// 创建测试文件
if (create_large_file(filename, file_size) != 0) {
return -1;
}

printf("创建了 %zu MB 的测试文件\n", file_size / (1024 * 1024));

printf("\n1. 测试readahead方法:\n");
test_readahead_method(filename);

printf("\n2. 测试posix_fadvise方法:\n");
test_fadvise_method(filename);

printf("\n3. 功能对比:\n");
printf(" readahead:\n");
printf(" - 专门的预读系统调用\n");
printf(" - 直接控制预读字节数\n");
printf(" - 更精确的控制\n");
printf(" posix_fadvise:\n");
printf(" - 通用的文件访问建议接口\n");
printf(" - 支持多种访问模式\n");
printf(" - 更好的可移植性\n");

unlink(filename);
return 0;
}

int main() {
return demo_readahead_vs_fadvise();
}

示例4:实际应用场景示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#define _GNU_SOURCE
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <time.h>

/**
* 模拟视频播放器的预读策略
*/
typedef struct {
int fd;
off_t file_size;
off_t current_pos;
size_t buffer_size;
} video_player_t;

/**
* 初始化视频播放器
*/
int video_player_init(video_player_t *player, const char *filename) {
player->fd = open(filename, O_RDONLY);
if (player->fd == -1) {
perror("打开视频文件失败");
return -1;
}

struct stat sb;
if (fstat(player->fd, &sb) == -1) {
perror("获取文件状态失败");
close(player->fd);
return -1;
}

player->file_size = sb.st_size;
player->current_pos = 0;
player->buffer_size = 2 * 1024 * 1024; // 2MB缓冲区

printf("视频文件大小: %.2f MB\n", player->file_size / (1024.0 * 1024.0));

return 0;
}

/**
* 播放视频(模拟)
*/
int video_player_play(video_player_t *player, int use_readahead) {
char *buffer = malloc(player->buffer_size);
if (!buffer) {
perror("分配播放缓冲区失败");
return -1;
}

printf("开始播放视频%s预读...\n", use_readahead ? "(使用" : "(不使用");

struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);

while (player->current_pos < player->file_size) {
// 根据播放位置决定是否预读
if (use_readahead && player->current_pos + player->buffer_size < player->file_size) {
// 预读下一缓冲区的数据
off_t ahead_pos = player->current_pos + player->buffer_size;
size_t ahead_size = (player->file_size - ahead_pos > player->buffer_size) ?
player->buffer_size : player->file_size - ahead_pos;

if (readahead(player->fd, ahead_pos, ahead_size) == 0) {
// printf("预读位置 %ld, 大小 %zu\n", ahead_pos, ahead_size);
}
}

// 读取当前缓冲区数据
ssize_t bytes_read = pread(player->fd, buffer, player->buffer_size, player->current_pos);
if (bytes_read <= 0) {
if (bytes_read == -1) {
perror("读取视频数据失败");
}
break;
}

// 模拟解码和播放处理
usleep(50000); // 50ms处理时间

player->current_pos += bytes_read;

if (player->current_pos % (10 * 1024 * 1024) == 0) {
printf("已播放 %.2f MB\n", player->current_pos / (1024.0 * 1024.0));
}
}

clock_gettime(CLOCK_MONOTONIC, &end);
double play_time = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;

printf("播放完成,总时间: %.3f 秒\n", play_time);

free(buffer);
return 0;
}

/**
* 清理视频播放器
*/
void video_player_cleanup(video_player_t *player) {
if (player->fd != -1) {
close(player->fd);
player->fd = -1;
}
}

/**
* 演示视频播放场景中的预读应用
*/
int demo_video_player_scenario() {
const char *filename = "video_sample.dat";
const size_t file_size = 100 * 1024 * 1024; // 100MB
video_player_t player_without, player_with;

printf("=== 视频播放场景中的预读应用 ===\n");

// 创建测试视频文件
int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建视频文件失败");
return -1;
}

char *buffer = malloc(1024 * 1024);
if (!buffer) {
perror("分配缓冲区失败");
close(fd);
return -1;
}

// 填充随机视频数据
srand(time(NULL));
for (int i = 0; i < 1024 * 1024; i++) {
buffer&#91;i] = rand() % 256;
}

// 写入文件
size_t written = 0;
while (written < file_size) {
size_t to_write = (file_size - written < 1024 * 1024) ?
file_size - written : 1024 * 1024;
write(fd, buffer, to_write);
written += to_write;
}

free(buffer);
close(fd);
printf("创建了 %.2f MB 的视频测试文件\n", file_size / (1024.0 * 1024.0));

// 测试不使用预读的播放
printf("\n--- 不使用预读的播放测试 ---\n");
memset(&player_without, 0, sizeof(player_without));
player_without.fd = -1;

if (video_player_init(&player_without, filename) == 0) {
video_player_play(&player_without, 0);
video_player_cleanup(&player_without);
}

// 测试使用预读的播放
printf("\n--- 使用预读的播放测试 ---\n");
memset(&player_with, 0, sizeof(player_with));
player_with.fd = -1;

if (video_player_init(&player_with, filename) == 0) {
video_player_play(&player_with, 1);
video_player_cleanup(&player_with);
}

unlink(filename);
return 0;
}

int main() {
return demo_video_player_scenario();
}

示例5:预读策略优化示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
#define _GNU_SOURCE
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <time.h>

/**
* 智能预读器
*/
typedef struct {
int fd;
off_t file_size;
off_t last_read_pos;
size_t read_pattern&#91;10]; // 记录最近10次读取的大小
int pattern_index;
int pattern_count;
} smart_readaheater_t;

/**
* 初始化智能预读器
*/
int smart_readaheater_init(smart_readaheater_t *sr, const char *filename) {
sr->fd = open(filename, O_RDONLY);
if (sr->fd == -1) {
perror("打开文件失败");
return -1;
}

struct stat sb;
if (fstat(sr->fd, &sb) == -1) {
perror("获取文件状态失败");
close(sr->fd);
return -1;
}

sr->file_size = sb.st_size;
sr->last_read_pos = 0;
sr->pattern_index = 0;
sr->pattern_count = 0;

memset(sr->read_pattern, 0, sizeof(sr->read_pattern));

printf("智能预读器初始化完成\n");
printf("文件大小: %.2f MB\n", sr->file_size / (1024.0 * 1024.0));

return 0;
}

/**
* 分析读取模式
*/
size_t analyze_read_pattern(smart_readaheater_t *sr) {
if (sr->pattern_count < 3) {
return 1024 * 1024; // 默认1MB
}

// 计算平均读取大小
size_t total = 0;
int count = (sr->pattern_count < 10) ? sr->pattern_count : 10;

for (int i = 0; i < count; i++) {
total += sr->read_pattern&#91;i];
}

return total / count;
}

/**
* 智能预读
*/
int smart_readahead(smart_readaheater_t *sr, off_t pos, size_t size) {
// 记录本次读取模式
sr->read_pattern&#91;sr->pattern_index] = size;
sr->pattern_index = (sr->pattern_index + 1) % 10;
if (sr->pattern_count < 10) {
sr->pattern_count++;
}

// 分析读取模式
size_t predicted_size = analyze_read_pattern(sr);

// 预测下一个读取位置
off_t next_pos = pos + size;

// 如果下一个位置有效,则进行预读
if (next_pos < sr->file_size) {
size_t readahead_size = predicted_size * 2; // 预读两倍大小
if (next_pos + readahead_size > sr->file_size) {
readahead_size = sr->file_size - next_pos;
}

if (readahead(sr->fd, next_pos, readahead_size) == 0) {
printf("智能预读: 位置 %ld, 大小 %zu\n", next_pos, readahead_size);
return 0;
}
}

return -1;
}

/**
* 读取数据并触发智能预读
*/
ssize_t smart_read(smart_readaheater_t *sr, void *buf, size_t count, off_t offset) {
ssize_t bytes_read = pread(sr->fd, buf, count, offset);
if (bytes_read > 0) {
smart_readahead(sr, offset, bytes_read);
sr->last_read_pos = offset + bytes_read;
}
return bytes_read;
}

/**
* 演示智能预读策略
*/
int demo_smart_readahead() {
const char *filename = "smart_test.dat";
const size_t file_size = 50 * 1024 * 1024; // 50MB
smart_readaheater_t sr;

printf("=== 智能预读策略示例 ===\n");

// 创建测试文件
int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建测试文件失败");
return -1;
}

char *buffer = malloc(1024 * 1024);
if (!buffer) {
perror("分配缓冲区失败");
close(fd);
return -1;
}

// 填充测试数据
for (size_t i = 0; i < 1024 * 1024; i++) {
buffer&#91;i] = 'A' + (i % 26);
}

// 写入文件
size_t written = 0;
while (written < file_size) {
size_t to_write = (file_size - written < 1024 * 1024) ?
file_size - written : 1024 * 1024;
write(fd, buffer, to_write);
written += to_write;
}

free(buffer);
close(fd);
printf("创建了 %.2f MB 的测试文件\n", file_size / (1024.0 * 1024.0));

// 初始化智能预读器
if (smart_readaheater_init(&sr, filename) != 0) {
unlink(filename);
return -1;
}

// 模拟不同模式的读取
printf("\n开始智能预读测试:\n");

char *read_buffer = malloc(2 * 1024 * 1024); // 2MB缓冲区
if (!read_buffer) {
perror("分配读取缓冲区失败");
close(sr.fd);
unlink(filename);
return -1;
}

// 模拟顺序读取
printf("1. 顺序读取模式:\n");
for (off_t pos = 0; pos < 20 * 1024 * 1024; pos += 512 * 1024) {
ssize_t bytes_read = smart_read(&sr, read_buffer, 512 * 1024, pos);
if (bytes_read > 0) {
printf(" 读取位置 %ld, 大小 %zd\n", pos, bytes_read);
}
}

// 模拟随机读取
printf("\n2. 随机读取模式:\n");
srand(time(NULL));
for (int i = 0; i < 5; i++) {
off_t pos = (rand() % (int)(file_size - 1024 * 1024));
size_t size = 256 * 1024 + (rand() % (768 * 1024));
ssize_t bytes_read = smart_read(&sr, read_buffer, size, pos);
if (bytes_read > 0) {
printf(" 随机读取位置 %ld, 大小 %zd\n", pos, bytes_read);
}
}

free(read_buffer);
close(sr.fd);
unlink(filename);

printf("\n智能预读策略特点:\n");
printf(" - 学习读取模式\n");
printf(" - 动态调整预读大小\n");
printf(" - 适应不同的访问模式\n");

return 0;
}

int main() {
return demo_smart_readahead();
}

readahead 使用注意事项

适用场景:

大文件顺序访问: 读取大型文件时特别有效

可预测的访问模式: 顺序读取或规律性访问

I/O密集型应用: 数据库、媒体播放器、文件传输工具

不适用场景:

随机访问: 频繁随机访问的文件不适合预读

小文件: 文件很小时预读开销大于收益

内存紧张: 系统内存不足时预读可能降低性能

性能考虑:

预读大小: 需要根据具体场景调整预读大小

时机选择: 合适的预读时机很重要

系统负载: 高负载时谨慎使用预读

错误处理:

检查返回值: readahead失败时不会影响正常读取

权限检查: 确保有足够的权限访问文件

文件状态: 文件必须是打开状态且支持预读

总结

readahead 是一个强大的预读工具,能够显著提高顺序访问大文件的性能。通过合理的预读策略,可以减少I/O等待时间,提高应用程序的响应速度。在实际应用中,需要根据具体的访问模式和系统环境来设计合适的预读策略。

readahead系统调用及示例-CSDN博客

https://www.calcguide.tech/2025/08/28/readahead系统调用及示例/

reboot系统调用及示例

我们来学习一下 reboot 这个系统调用。它是一个底层的 Linux 系统调用,用于重启、关闭或挂起你的计算机 。

1. 函数介绍

reboot 系统调用(System Call)提供了直接与 Linux 内核交互以控制机器状态(如重启、关机)的接口 。顾名思义,它的主要作用是重新启动机器,但根据提供的参数,它也能执行关闭电源、挂起系统等操作 。这与用户通常在命令行使用的 reboot 命令不同,后者是一个更高级别的程序,内部可能会调用这个系统调用 。

reboot系统调用及示例-CSDN博客

https://www.calcguide.tech/2025/08/28/reboot系统调用及示例/

2. 函数原型

1
2
3
4
5
6
#include <unistd.h>
#include <linux/reboot.h> // 包含定义的常量

// 注意:实际的系统调用接口通常带有前缀下划线
int __reboot(int magic, int magic2, unsigned int cmd, void *arg);

注意:直接使用 __reboot 系统调用比较复杂且不推荐。通常,标准 C 库(glibc)会提供一个更方便的包装函数 reboot。

更常用的 glibc 包装函数原型:

1
2
3
4
#include <sys/reboot.h> // glibc 封装函数的头文件

int reboot(int cmd);

3. 功能

这个系统调用的核心功能是根据 cmd 参数的值来执行不同的系统级操作,主要包括重启系统、关闭系统电源、挂起系统或启用/禁用 Ctrl+Alt+Del 组合键的功能 。

4. 参数

对于 glibc 封装的 reboot(int cmd) 函数:

cmd: int 类型。这个参数指定了要执行的具体操作。常见的值定义在 sys/reboot.h 头文件中,例如:

  • LINUX_REBOOT_CMD_RESTART: 立即重启系统。

  • LINUX_REBOOT_CMD_HALT: 停止操作系统,但不关闭电源(挂起)。

  • LINUX_REBOOT_CMD_POWER_OFF: 停止操作系统并关闭电源(如果硬件支持)。

  • LINUX_REBOOT_CMD_RESTART2: 重启系统,并传递一个可选的命令行参数给内核(通过 arg 参数,但这在 glibc 包装函数中不直接暴露)。

  • LINUX_REBOOT_CMD_CAD_ON: 允许使用 Ctrl+Alt+Del 组合键重启系统。

  • LINUX_REBOOT_CMD_CAD_OFF: 禁止使用 Ctrl+Alt+Del 组合键。

对于底层的 __reboot(int magic, int magic2, unsigned int cmd, void *arg) 系统调用:

  • magic: int 类型。第一个“魔数”,必须是 LINUX_REBOOT_MAGIC1 。这是为了防止意外调用系统调用 。

  • magic2: int 类型。第二个“魔数”,必须是 LINUX_REBOOT_MAGIC2 或 LINUX_REBOOT_MAGIC2A/B/C 中的一个 。同样是为了防止误操作 。

  • cmd: unsigned int 类型。指定要执行的操作,与 glibc reboot 的 cmd 参数类似,但可能使用内核空间定义的常量(如 LINUX_REBOOT_CMD_*)。

  • arg: void * 类型。一个指向缓冲区的指针,用于传递附加参数,例如传递给内核的命令行字符串(当 cmd 为 LINUX_REBOOT_CMD_RESTART2 时)。

5. 返回值

  • 成功: 对于 reboot(int cmd),如果调用成功,通常不会返回,因为系统会立即执行指定的操作(如重启或关机)。

  • 失败: 返回 -1,并设置全局变量 errno 来指示错误原因,最常见的失败原因是权限不足(需要 root 权限)。

6. 相似函数或关联函数

  • reboot 命令: 用户空间的标准命令行工具,用于重启系统,内部可能调用 reboot() 系统调用 。

  • shutdown 命令: 更通用的关机/重启命令,允许指定时间、广播消息等,最终也可能调用 reboot() 系统调用。

  • poweroff 命令: 用于关闭系统电源,通常也是通过系统调用实现。

  • halt 命令: 停止系统,行为取决于实现和参数,有时与 poweroff 类似。

  • sync 系统调用: 在执行 reboot 之前,通常会先调用 sync() 将所有未写入磁盘的数据刷新到磁盘,以防数据丢失。

7. 示例代码

注意: 运行此代码需要 root 权限,因为它执行的是特权操作。系统会立即重启,所以请确保保存所有工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <stdio.h>
#include <unistd.h> // 包含 sync
#include <sys/reboot.h> // 包含 reboot 函数和命令常量
#include <errno.h>
#include <string.h>

int main() {
printf("准备重启系统...\n");

// 1. 强烈建议在重启前先同步文件系统
// 这会将所有缓存中的数据写入磁盘,防止数据丢失
printf("正在同步文件系统...\n");
sync(); // 执行同步操作
// sync() 通常没有返回值用于检查错误,它尽力而为

// 2. 调用 reboot 系统调用 (通过 glibc 包装)
// 使用 LINUX_REBOOT_CMD_RESTART 命令重启系统
printf("正在调用 reboot 系统调用...\n");
if (reboot(LINUX_REBOOT_CMD_RESTART) == -1) {
// 如果返回 -1,表示调用失败
perror("reboot"); // 打印具体的错误原因
fprintf(stderr, "错误: 无法重启系统。请确保以 root 权限运行此程序。\n");
return 1; // 返回错误码
}

// 如果 reboot 调用成功,下面的代码通常不会被执行,
// 因为系统已经开始重启过程。
printf("系统调用已执行,但程序仍在运行?这很奇怪。\n");

return 0; // 理论上不会到达这里
}

编译和运行:

1
2
3
4
5
6
7
# 假设代码保存在 reboot_example.c 中
# 编译
gcc -o reboot_example reboot_example.c

# 运行 (需要 root 权限)
sudo ./reboot_example

再次提醒: 执行此程序会使您的计算机立即重启,请谨慎操作!

remap_file_pages系统调用及示例

remap_file_pages 函数详解

  1. 函数介绍

remap_file_pages 是Linux系统调用,用于重新映射文件映射区域中的页面,创建非线性(non-linear)的内存映射。它允许将文件的不同部分映射到进程地址空间的不连续区域,或者将同一文件区域映射到多个不同的虚拟地址。这个功能对于实现复杂的内存布局和优化I/O操作非常有用。

  1. 函数原型
1
2
3
4
#define _GNU_SOURCE
#include <sys/mman.h>
int remap_file_pages(void *addr, size_t size, int prot, size_t pgoff, int flags);

  1. 功能

remap_file_pages 允许重新排列已经通过 mmap 映射的文件页面在虚拟地址空间中的布局。它可以创建循环缓冲区、跳过文件中的某些区域、或者重新排序文件内容的访问顺序,而无需实际移动物理内存页面。

  1. 参数
  • *void addr: 已映射内存区域的起始地址(必须是页面对齐的)

  • size_t size: 要重新映射的区域大小(必须是页面大小的倍数)

  • int prot: 保护标志(当前必须为0)

  • size_t pgoff: 文件中的页面偏移量(相对于映射区域的起始位置)

  • int flags: 标志位(当前必须为0)

  1. 返回值
  • 成功: 返回0

  • 失败: 返回-1,并设置errno

  1. 相似函数,或关联函数
  • mmap: 内存映射文件

  • mremap: 重新映射虚拟内存区域

  • munmap: 取消内存映射

  • madvise: 给内核提供内存访问建议

  1. 示例代码

示例1:基础remap_file_pages使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#define _GNU_SOURCE
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

/**
* 创建测试文件
*/
int create_test_file(const char *filename, size_t size) {
int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建测试文件失败");
return -1;
}

// 填充测试数据
char *buffer = malloc(4096);
if (!buffer) {
perror("分配缓冲区失败");
close(fd);
return -1;
}

for (int i = 0; i < 4096; i++) {
buffer&#91;i] = 'A' + (i % 26);
}

size_t written = 0;
while (written < size) {
size_t to_write = (size - written < 4096) ? size - written : 4096;
ssize_t result = write(fd, buffer, to_write);
if (result == -1) {
perror("写入文件失败");
free(buffer);
close(fd);
return -1;
}
written += result;
}

free(buffer);
close(fd);
return 0;
}

/**
* 演示remap_file_pages的基本使用方法
*/
int demo_remap_file_pages_basic() {
const char *filename = "test_remap.dat";
const size_t file_size = 16 * 4096; // 16个页面
int fd;
void *mapped_addr;
size_t page_size = getpagesize();

printf("=== remap_file_pages 基本使用示例 ===\n");
printf("页面大小: %zu 字节\n", page_size);
printf("文件大小: %zu 字节 (%zu 个页面)\n", file_size, file_size / page_size);

// 创建测试文件
if (create_test_file(filename, file_size) != 0) {
return -1;
}

// 打开文件并映射到内存
fd = open(filename, O_RDONLY);
if (fd == -1) {
perror("打开文件失败");
unlink(filename);
return -1;
}

mapped_addr = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (mapped_addr == MAP_FAILED) {
perror("内存映射失败");
close(fd);
unlink(filename);
return -1;
}

printf("文件映射到地址: %p\n", mapped_addr);

// 显示原始映射的内容
printf("\n原始映射内容 (前4个页面):\n");
for (int i = 0; i < 4; i++) {
char *page_start = (char*)mapped_addr + i * page_size;
printf("页面 %d (偏移 %zu): %.32s...\n", i, i * page_size, page_start);
}

// 使用remap_file_pages重新映射页面
// 将第3个页面映射到第1个页面的位置
printf("\n使用remap_file_pages重新映射...\n");

// 注意:remap_file_pages在现代Linux内核中可能不可用或被禁用
// 这里演示调用方式,但可能返回ENOSYS
int result = remap_file_pages((char*)mapped_addr + page_size, // 第2个页面位置
page_size, // 1个页面大小
0, // prot参数(必须为0)
2, // 映射第3个页面(索引2)
0); // flags参数(必须为0)

if (result == -1) {
if (errno == ENOSYS) {
printf("警告: remap_file_pages 系统调用不可用 (内核可能已禁用)\n");
printf("这是现代Linux内核的常见情况\n");
} else {
printf("remap_file_pages 失败: %s\n", strerror(errno));
}
} else {
printf("remap_file_pages 调用成功\n");

// 显示重新映射后的内容
printf("\n重新映射后的内容:\n");
for (int i = 0; i < 4; i++) {
char *page_start = (char*)mapped_addr + i * page_size;
printf("页面 %d: %.32s...\n", i, page_start);
}
}

// 清理资源
munmap(mapped_addr, file_size);
close(fd);
unlink(filename);

return 0;
}

int main() {
return demo_remap_file_pages_basic();
}

示例2:循环缓冲区实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#define _GNU_SOURCE
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

/**
* 演示使用remap_file_pages实现循环缓冲区
*/
int demo_circular_buffer() {
const char *filename = "circular_buffer.dat";
const size_t buffer_size = 8 * getpagesize(); // 8个页面的缓冲区
const size_t physical_size = 4 * getpagesize(); // 实际只有4个页面的数据
int fd;
void *mapped_addr;
size_t page_size = getpagesize();

printf("=== 循环缓冲区实现示例 ===\n");
printf("页面大小: %zu 字节\n", page_size);
printf("逻辑缓冲区大小: %zu 字节 (%zu 页面)\n", buffer_size, buffer_size / page_size);
printf("物理文件大小: %zu 字节 (%zu 页面)\n", physical_size, physical_size / page_size);

// 创建测试文件
fd = open(filename, O_CREAT | O_RDWR | O_TRUNC, 0644);
if (fd == -1) {
perror("创建测试文件失败");
return -1;
}

// 扩展文件大小
if (ftruncate(fd, physical_size) == -1) {
perror("扩展文件失败");
close(fd);
unlink(filename);
return -1;
}

// 填充测试数据
char *test_data = malloc(physical_size);
if (!test_data) {
perror("分配测试数据失败");
close(fd);
unlink(filename);
return -1;
}

// 创建循环模式的数据
for (size_t i = 0; i < physical_size; i++) {
test_data&#91;i] = '0' + (i % 10);
}

write(fd, test_data, physical_size);
free(test_data);

// 映射逻辑缓冲区大小(比物理文件大)
mapped_addr = mmap(NULL, buffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mapped_addr == MAP_FAILED) {
perror("内存映射失败");
close(fd);
unlink(filename);
return -1;
}

printf("映射地址: %p\n", mapped_addr);

// 显示原始映射内容
printf("\n原始映射内容:\n");
for (size_t i = 0; i < buffer_size; i += page_size) {
size_t page_index = i / page_size;
char *page_start = (char*)mapped_addr + i;
printf("逻辑页面 %zu: %.16s\n", page_index, page_start);
}

// 尝试使用remap_file_pages创建循环缓冲区
printf("\n尝试创建循环缓冲区...\n");

// 将前4个页面的内容循环映射到后4个页面位置
for (size_t i = 0; i < 4; i++) {
int result = remap_file_pages((char*)mapped_addr + (4 + i) * page_size, // 后4个页面位置
page_size, // 1个页面大小
0, // prot参数
i, // 映射前4个页面的内容
0); // flags参数

if (result == -1) {
if (errno == ENOSYS) {
printf("系统不支持remap_file_pages,无法创建循环缓冲区\n");
break;
} else {
printf("页面 %zu 重新映射失败: %s\n", i, strerror(errno));
}
} else {
printf("页面 %zu 重新映射成功\n", i);
}
}

// 显示重新映射后的内容(如果成功的话)
printf("\n重新映射后的内容:\n");
for (size_t i = 0; i < buffer_size; i += page_size) {
size_t page_index = i / page_size;
char *page_start = (char*)mapped_addr + i;
printf("逻辑页面 %zu: %.16s\n", page_index, page_start);
}

// 清理资源
munmap(mapped_addr, buffer_size);
close(fd);
unlink(filename);

return 0;
}

int main() {
return demo_circular_buffer();
}

示例3:跳过文件区域映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#define _GNU_SOURCE
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

/**
* 演示跳过文件中某些区域的映射
*/
int demo_skip_file_regions() {
const char *filename = "skip_regions.dat";
const size_t file_size = 12 * getpagesize(); // 12个页面
int fd;
void *mapped_addr;
size_t page_size = getpagesize();

printf("=== 跳过文件区域映射示例 ===\n");
printf("页面大小: %zu 字节\n", page_size);
printf("文件大小: %zu 字节 (%zu 页面)\n", file_size, file_size / page_size);

// 创建测试文件
fd = open(filename, O_CREAT | O_RDWR | O_TRUNC, 0644);
if (fd == -1) {
perror("创建测试文件失败");
return -1;
}

// 扩展文件大小
if (ftruncate(fd, file_size) == -1) {
perror("扩展文件失败");
close(fd);
unlink(filename);
return -1;
}

// 填充测试数据(每个页面不同内容)
for (int page = 0; page < 12; page++) {
char page_data&#91;4096];
for (int i = 0; i < 4096; i++) {
page_data&#91;i] = 'A' + page;
}
lseek(fd, page * 4096, SEEK_SET);
write(fd, page_data, 4096);
}

// 映射整个文件
mapped_addr = mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mapped_addr == MAP_FAILED) {
perror("内存映射失败");
close(fd);
unlink(filename);
return -1;
}

printf("文件映射到地址: %p\n", mapped_addr);

// 显示原始映射内容
printf("\n原始映射内容:\n");
for (int i = 0; i < 12; i++) {
char *page_start = (char*)mapped_addr + i * page_size;
printf("页面 %d: %c%c%c%c...\n", i, page_start&#91;0], page_start&#91;1],
page_start&#91;2], page_start&#91;3]);
}

// 假设我们要跳过第3、4、7、8页面,用其他页面的内容替换
printf("\n尝试跳过某些页面并重新映射...\n");

struct {
int virtual_page; // 虚拟页面索引
int source_page; // 源页面索引
} remap_rules&#91;] = {
{2, 0}, // 将页面0的内容映射到页面2的位置
{3, 1}, // 将页面1的内容映射到页面3的位置
{6, 5}, // 将页面5的内容映射到页面6的位置
{7, 9}, // 将页面9的内容映射到页面7的位置
{-1, -1} // 结束标记
};

// 执行重新映射
for (int i = 0; remap_rules&#91;i].virtual_page != -1; i++) {
int result = remap_file_pages((char*)mapped_addr + remap_rules&#91;i].virtual_page * page_size,
page_size,
0,
remap_rules&#91;i].source_page,
0);

if (result == -1) {
if (errno == ENOSYS) {
printf("系统不支持remap_file_pages\n");
break;
} else {
printf("页面 %d->%d 重新映射失败: %s\n",
remap_rules&#91;i].source_page, remap_rules&#91;i].virtual_page, strerror(errno));
}
} else {
printf("页面 %d->%d 重新映射成功\n",
remap_rules&#91;i].source_page, remap_rules&#91;i].virtual_page);
}
}

// 显示重新映射后的内容
printf("\n重新映射后的内容:\n");
for (int i = 0; i < 12; i++) {
char *page_start = (char*)mapped_addr + i * page_size;
printf("页面 %d: %c%c%c%c...\n", i, page_start&#91;0], page_start&#91;1],
page_start&#91;2], page_start&#91;3]);
}

// 清理资源
munmap(mapped_addr, file_size);
close(fd);
unlink(filename);

return 0;
}

int main() {
return demo_skip_file_regions();
}

示例4:内存访问模式优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#define _GNU_SOURCE
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>

/**
* 比较不同访问模式的性能
*/
int compare_access_patterns() {
const char *filename = "access_pattern_test.dat";
const size_t file_size = 16 * getpagesize();
int fd;
void *mapped_addr;
size_t page_size = getpagesize();
char *sequential_data, *random_data;

printf("=== 内存访问模式优化示例 ===\n");
printf("页面大小: %zu 字节\n", page_size);
printf("文件大小: %zu 字节 (%zu 页面)\n", file_size, file_size / page_size);

// 创建测试文件
fd = open(filename, O_CREAT | O_RDWR | O_TRUNC, 0644);
if (fd == -1) {
perror("创建测试文件失败");
return -1;
}

if (ftruncate(fd, file_size) == -1) {
perror("扩展文件失败");
close(fd);
unlink(filename);
return -1;
}

// 填充测试数据
char *test_data = malloc(file_size);
if (!test_data) {
perror("分配测试数据失败");
close(fd);
unlink(filename);
return -1;
}

// 创建有规律的数据模式
for (size_t i = 0; i < file_size; i++) {
test_data&#91;i] = (i / page_size) + 'A'; // 每个页面一个字母
}

write(fd, test_data, file_size);
free(test_data);

// 映射文件
mapped_addr = mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mapped_addr == MAP_FAILED) {
perror("内存映射失败");
close(fd);
unlink(filename);
return -1;
}

printf("文件映射到地址: %p\n", mapped_addr);

// 准备测试数据
sequential_data = malloc(file_size);
random_data = malloc(file_size);
if (!sequential_data || !random_data) {
perror("分配测试缓冲区失败");
free(sequential_data);
free(random_data);
munmap(mapped_addr, file_size);
close(fd);
unlink(filename);
return -1;
}

// 顺序访问测试
printf("\n1. 顺序访问测试:\n");
clock_t start = clock();

for (size_t i = 0; i < file_size; i++) {
sequential_data&#91;i] = ((char*)mapped_addr)&#91;i];
}

clock_t end = clock();
double sequential_time = ((double)(end - start)) / CLOCKS_PER_SEC;
printf("顺序访问耗时: %.6f 秒\n", sequential_time);

// 尝试重新映射以优化随机访问
printf("\n2. 尝试优化随机访问模式:\n");

// 创建一个访问模式:每隔一个页面访问
int result = remap_file_pages((char*)mapped_addr + 2 * page_size, // 第3个页面位置
page_size, // 1个页面大小
0, // prot参数
4, // 映射第5个页面
0); // flags参数

if (result == -1) {
if (errno == ENOSYS) {
printf("系统不支持remap_file_pages,跳过优化测试\n");
} else {
printf("重新映射失败: %s\n", strerror(errno));
}
} else {
printf("重新映射成功,优化了访问模式\n");
}

// 随机访问测试
printf("\n3. 随机访问测试:\n");
start = clock();

// 模拟随机访问模式
size_t access_pattern&#91;] = {0, 4096, 8192, 12288, 2048, 6144, 10240, 14336};
size_t pattern_size = sizeof(access_pattern) / sizeof(access_pattern&#91;0]);

for (size_t i = 0; i < 1000; i++) { // 重复1000次
for (size_t j = 0; j < pattern_size; j++) {
size_t offset = access_pattern&#91;j] + (i % 100);
if (offset < file_size) {
random_data&#91;i % file_size] = ((char*)mapped_addr)&#91;offset];
}
}
}

end = clock();
double random_time = ((double)(end - start)) / CLOCKS_PER_SEC;
printf("随机访问耗时: %.6f 秒\n", random_time);

// 显示访问模式优化的理论优势
printf("\n访问模式优化说明:\n");
printf(" remap_file_pages 可以:\n");
printf(" 1. 创建循环缓冲区,避免数据复制\n");
printf(" 2. 优化缓存局部性,提高访问效率\n");
printf(" 3. 实现非线性访问模式\n");
printf(" 4. 减少页面错误和TLB未命中\n");

// 清理资源
free(sequential_data);
free(random_data);
munmap(mapped_addr, file_size);
close(fd);
unlink(filename);

return 0;
}

int main() {
return compare_access_patterns();
}

示例5:实际应用场景演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#define _GNU_SOURCE
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

/**
* 模拟数据库页面缓存场景
*/
typedef struct {
void *mapped_addr;
size_t total_size;
size_t page_size;
int fd;
} db_cache_t;

/**
* 初始化数据库缓存
*/
int db_cache_init(db_cache_t *cache, const char *filename, size_t cache_size) {
size_t page_size = getpagesize();
size_t file_size = ((cache_size / page_size) + 16) * page_size; // 多分配一些空间

memset(cache, 0, sizeof(db_cache_t));
cache->page_size = page_size;
cache->total_size = file_size;

// 创建或打开数据库文件
cache->fd = open(filename, O_CREAT | O_RDWR | O_TRUNC, 0644);
if (cache->fd == -1) {
perror("打开数据库文件失败");
return -1;
}

// 设置文件大小
if (ftruncate(cache->fd, file_size) == -1) {
perror("设置文件大小失败");
close(cache->fd);
return -1;
}

// 初始化文件内容
char *init_data = malloc(page_size);
if (!init_data) {
perror("分配初始化数据失败");
close(cache->fd);
return -1;
}

for (size_t i = 0; i < file_size; i += page_size) {
for (size_t j = 0; j < page_size; j++) {
init_data&#91;j] = 'P' + ((i / page_size) % 26);
}
lseek(cache->fd, i, SEEK_SET);
write(cache->fd, init_data, page_size);
}

free(init_data);

// 映射文件
cache->mapped_addr = mmap(NULL, cache->total_size,
PROT_READ | PROT_WRITE, MAP_SHARED, cache->fd, 0);
if (cache->mapped_addr == MAP_FAILED) {
perror("内存映射失败");
close(cache->fd);
return -1;
}

printf("数据库缓存初始化完成\n");
printf(" 缓存地址: %p\n", cache->mapped_addr);
printf(" 缓存大小: %zu 字节\n", cache->total_size);
printf(" 页面大小: %zu 字节\n", cache->page_size);

return 0;
}

/**
* 演示数据库页面重排
*/
int db_cache_remap_pages(db_cache_t *cache) {
printf("\n=== 数据库页面重排演示 ===\n");

// 显示原始页面内容
printf("原始页面内容:\n");
for (int i = 0; i < 8; i++) {
char *page_start = (char*)cache->mapped_addr + i * cache->page_size;
printf(" 页面 %d: %c%c%c%c...\n", i, page_start&#91;0], page_start&#91;1],
page_start&#91;2], page_start&#91;3]);
}

// 尝试重新排列页面以优化访问模式
printf("\n尝试重新排列页面以优化热点数据访问...\n");

// 假设页面2和页面3是热点数据,将其映射到更易访问的位置
struct {
int virtual_page; // 虚拟页面位置
int source_page; // 源页面
} hot_pages&#91;] = {
{6, 2}, // 将热点页面2映射到位置6
{7, 3}, // 将热点页面3映射到位置7
{-1, -1}
};

int remap_success = 0;
for (int i = 0; hot_pages&#91;i].virtual_page != -1; i++) {
int result = remap_file_pages((char*)cache->mapped_addr +
hot_pages&#91;i].virtual_page * cache->page_size,
cache->page_size,
0,
hot_pages&#91;i].source_page,
0);

if (result == -1) {
if (errno != ENOSYS) {
printf("页面 %d->%d 重新映射失败: %s\n",
hot_pages&#91;i].source_page, hot_pages&#91;i].virtual_page, strerror(errno));
}
} else {
printf("热点页面 %d->%d 重新映射成功\n",
hot_pages&#91;i].source_page, hot_pages&#91;i].virtual_page);
remap_success = 1;
}
}

if (!remap_success && errno == ENOSYS) {
printf("系统不支持remap_file_pages,使用传统访问方式\n");
}

// 显示重新映射后的页面内容
printf("\n重新映射后的页面内容:\n");
for (int i = 0; i < 8; i++) {
char *page_start = (char*)cache->mapped_addr + i * cache->page_size;
printf(" 页面 %d: %c%c%c%c...\n", i, page_start&#91;0], page_start&#91;1],
page_start&#91;2], page_start&#91;3]);
}

return 0;
}

/**
* 清理数据库缓存
*/
void db_cache_cleanup(db_cache_t *cache) {
if (cache->mapped_addr && cache->mapped_addr != MAP_FAILED) {
munmap(cache->mapped_addr, cache->total_size);
}
if (cache->fd != -1) {
close(cache->fd);
}
printf("数据库缓存清理完成\n");
}

/**
* 演示实际应用场景
*/
int demo_real_world_application() {
db_cache_t cache;
const char *db_filename = "database_cache.dat";

printf("=== 实际应用场景演示 ===\n");
printf("场景: 数据库页面缓存优化\n");

// 初始化数据库缓存
if (db_cache_init(&cache, db_filename, 32 * 1024 * 1024) != 0) { // 32MB缓存
return -1;
}

// 演示页面重排
db_cache_remap_pages(&cache);

// 演示循环日志缓冲区
printf("\n=== 循环日志缓冲区演示 ===\n");
printf("remap_file_pages 可以用于实现:\n");
printf(" 1. 循环日志缓冲区(避免数据复制)\n");
printf(" 2. 数据库页面池管理\n");
printf(" 3. 文件系统元数据缓存优化\n");
printf(" 4. 多媒体数据流缓冲\n");

// 清理资源
db_cache_cleanup(&cache);
unlink(db_filename);

return 0;
}

int main() {
return demo_real_world_application();
}

remap_file_pages 限制和注意事项

系统支持:

内核版本: 需要Linux 2.6或更高版本

功能可用性: 现代内核可能默认禁用此功能

安全限制: 某些安全策略可能禁止使用

使用限制:

必须已映射: 目标区域必须已经通过mmap映射

页面对齐: 地址和大小必须是页面大小的倍数

参数限制: prot和flags参数当前必须为0

性能考虑:

TLB优化: 可以减少TLB未命中

缓存局部性: 优化内存访问模式

减少复制: 避免不必要的数据复制

错误处理:

ENOSYS: 系统调用不支持

EINVAL: 参数无效

ENOMEM: 内存不足

替代方案

由于 remap_file_pages 在现代系统中可能不可用,可以考虑以下替代方案:

1. 多次mmap:

1
2
3
4
// 为同一文件的不同部分创建多个映射
void *map1 = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, offset1);
void *map2 = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, offset2);

2. 使用madvise:

1
2
3
4
// 给内核提供访问建议
madvise(addr, size, MADV_WILLNEED); // 预读建议
madvise(addr, size, MADV_SEQUENTIAL); // 顺序访问建议

3. 用户空间缓冲区管理:

1
2
// 在用户空间实现复杂的缓冲区管理逻辑

总结

remap_file_pages 是一个强大的系统调用,用于实现非线性的内存映射布局。虽然在现代Linux内核中可能不可用,但它在以下场景中仍然有价值:

循环缓冲区实现: 避免数据复制,提高效率

数据库页面管理: 优化热点数据访问

多媒体流处理: 实现高效的缓冲区重用

文件系统优化: 优化元数据访问模式

正确使用这个函数需要深入理解虚拟内存管理和文件映射机制。在实际应用中,需要检查系统支持情况并提供适当的回退方案。

https://www.calcguide.tech/2025/08/28/remap-file-pages系统调用及示例/

remap_file_pages系统调用及示例

recvmsg系统调用及示例

recvmsg 函数详解

  1. 函数介绍

recvmsg 是Linux网络编程中功能最强大的接收数据函数之一。它是 recv 和 recvfrom 的增强版本,支持接收控制信息(如文件描述符、时间戳等)和分散缓冲区(scatter-gather I/O)。recvmsg 特别适用于需要接收复杂网络数据包的应用场景。

  1. 函数原型
1
2
3
#include <sys/socket.h>
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

  1. 功能

recvmsg 从套接字接收数据,并可以同时接收额外的控制信息。它支持分散缓冲区接收、接收发送者地址信息、接收辅助数据(如文件描述符传递)等功能。

  1. 参数
  • int sockfd: 套接字文件描述符

  • *struct msghdr msg: 消息头结构,描述接收缓冲区和控制信息

  • int flags: 接收标志,控制接收行为

  1. 返回值
  • 成功: 返回实际接收到的字节数

  • 连接关闭: 返回0

  • 失败: 返回-1,并设置errno

  1. 相似函数,或关联函数
  • recv: 基本接收函数

  • recvfrom: 带地址信息的接收函数

  • sendmsg: 对应的发送函数

  • read: 基本读取函数

  1. 示例代码

示例1:基础recvmsg使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

/**
* 演示recvmsg的基本使用方法
*/
int demo_recvmsg_basic() {
int server_fd, client_fd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_len = sizeof(client_addr);
struct msghdr msg;
struct iovec iov&#91;1];
char buffer&#91;1024];
char control_buffer&#91;1024];
ssize_t bytes_received;

printf("=== recvmsg 基本使用示例 ===\n");

// 创建TCP服务器套接字
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("创建服务器套接字失败");
return -1;
}

// 设置服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(8080);

// 绑定套接字
if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("绑定套接字失败");
close(server_fd);
return -1;
}

// 监听连接
if (listen(server_fd, 1) == -1) {
perror("监听失败");
close(server_fd);
return -1;
}

printf("服务器监听在端口 8080\n");

// 在后台启动客户端(简化演示)
if (fork() == 0) {
// 客户端代码
sleep(1); // 等待服务器启动
int client_sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serv_addr;

memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8080);
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

if (connect(client_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == 0) {
const char *message = "Hello from client!";
send(client_sock, message, strlen(message), 0);
printf("客户端发送消息: %s\n", message);
}

close(client_sock);
exit(0);
}

// 接受客户端连接
client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
if (client_fd == -1) {
perror("接受连接失败");
close(server_fd);
return -1;
}

printf("客户端连接来自: %s:%d\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

// 准备msghdr结构
memset(&msg, 0, sizeof(msg));

// 设置接收缓冲区
iov&#91;0].iov_base = buffer;
iov&#91;0].iov_len = sizeof(buffer) - 1;
msg.msg_iov = iov;
msg.msg_iovlen = 1;

// 设置控制缓冲区(用于接收辅助数据)
msg.msg_control = control_buffer;
msg.msg_controllen = sizeof(control_buffer);

// 设置地址信息缓冲区
msg.msg_name = &client_addr;
msg.msg_namelen = client_len;

// 接收消息
bytes_received = recvmsg(client_fd, &msg, 0);
if (bytes_received == -1) {
perror("recvmsg 失败");
close(client_fd);
close(server_fd);
return -1;
}

buffer&#91;bytes_received] = '\0';
printf("recvmsg 接收到 %zd 字节数据: %s\n", bytes_received, buffer);
printf("消息标志: %d\n", msg.msg_flags);

// 显示地址信息
if (msg.msg_namelen > 0) {
struct sockaddr_in *addr = (struct sockaddr_in*)msg.msg_name;
printf("发送者地址: %s:%d\n",
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
}

close(client_fd);
close(server_fd);

return 0;
}

int main() {
return demo_recvmsg_basic();
}

示例2:分散缓冲区接收

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

/**
* 演示recvmsg的分散缓冲区接收功能
*/
int demo_recvmsg_scatter_gather() {
int server_fd, client_fd;
struct sockaddr_in server_addr;
struct msghdr msg;
struct iovec iov&#91;3];
char buffer1&#91;100], buffer2&#91;100], buffer3&#91;100];
ssize_t bytes_received;

printf("=== recvmsg 分散缓冲区接收示例 ===\n");

// 创建UDP套接字
server_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (server_fd == -1) {
perror("创建UDP套接字失败");
return -1;
}

// 设置服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(8081);

// 绑定套接字
if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("绑定套接字失败");
close(server_fd);
return -1;
}

printf("UDP服务器监听在端口 8081\n");

// 启动UDP客户端
if (fork() == 0) {
// 客户端代码
sleep(1);
int client_sock = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in serv_addr;

memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8081);
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

// 发送长消息
const char *long_message = "This is a very long message that will be split across multiple buffers during scatter-gather reception.";
sendto(client_sock, long_message, strlen(long_message), 0,
(struct sockaddr*)&serv_addr, sizeof(serv_addr));
printf("客户端发送长消息 (%zu 字节)\n", strlen(long_message));

close(client_sock);
exit(0);
}

// 准备分散缓冲区
memset(&msg, 0, sizeof(msg));

// 设置三个分散的缓冲区
iov&#91;0].iov_base = buffer1;
iov&#91;0].iov_len = sizeof(buffer1) - 1;
iov&#91;1].iov_base = buffer2;
iov&#91;1].iov_len = sizeof(buffer2) - 1;
iov&#91;2].iov_base = buffer3;
iov&#91;2].iov_len = sizeof(buffer3) - 1;

msg.msg_iov = iov;
msg.msg_iovlen = 3;

// 接收消息
bytes_received = recvmsg(server_fd, &msg, 0);
if (bytes_received == -1) {
perror("recvmsg 失败");
close(server_fd);
return -1;
}

printf("recvmsg 接收到 %zd 字节数据\n", bytes_received);
printf("消息被分散到 %d 个缓冲区\n", (int)msg.msg_iovlen);
printf("实际使用的缓冲区数: %d\n", (int)msg.msg_iovlen);

// 添加字符串终止符
size_t total_len = 0;
for (int i = 0; i < 3 && total_len < (size_t)bytes_received; i++) {
size_t buf_len = iov&#91;i].iov_len;
if (total_len + buf_len > (size_t)bytes_received) {
buf_len = bytes_received - total_len;
}
((char*)iov&#91;i].iov_base)&#91;buf_len] = '\0';
total_len += buf_len;
}

printf("缓冲区1内容 (%zu 字节): %s\n", strlen(buffer1), buffer1);
printf("缓冲区2内容 (%zu 字节): %s\n", strlen(buffer2), buffer2);
printf("缓冲区3内容 (%zu 字节): %s\n", strlen(buffer3), buffer3);

// 合并显示完整消息
char full_message&#91;512];
snprintf(full_message, sizeof(full_message), "%s%s%s", buffer1, buffer2, buffer3);
printf("完整消息: %s\n", full_message);

close(server_fd);

return 0;
}

int main() {
return demo_recvmsg_scatter_gather();
}

示例3:接收控制信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>

/**
* 演示recvmsg接收控制信息(时间戳)
*/
int demo_recvmsg_control_data() {
int server_fd, client_fd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_len = sizeof(client_addr);
struct msghdr msg;
struct iovec iov&#91;1];
char buffer&#91;1024];
char control_buffer&#91;1024];
ssize_t bytes_received;

printf("=== recvmsg 控制信息接收示例 ===\n");

// 创建TCP服务器套接字
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("创建服务器套接字失败");
return -1;
}

// 启用时间戳选项
int timestamp_on = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_TIMESTAMP,
&timestamp_on, sizeof(timestamp_on)) == -1) {
printf("警告: 无法启用时间戳选项: %s\n", strerror(errno));
}

// 设置服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(8082);

// 绑定套接字
if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("绑定套接字失败");
close(server_fd);
return -1;
}

// 监听连接
if (listen(server_fd, 1) == -1) {
perror("监听失败");
close(server_fd);
return -1;
}

printf("带时间戳的服务器监听在端口 8082\n");

// 启动客户端
if (fork() == 0) {
// 客户端代码
sleep(1);
int client_sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serv_addr;

memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8082);
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

if (connect(client_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == 0) {
const char *message = "Message with timestamp";
send(client_sock, message, strlen(message), 0);
printf("客户端发送消息: %s\n", message);
}

close(client_sock);
exit(0);
}

// 接受客户端连接
client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
if (client_fd == -1) {
perror("接受连接失败");
close(server_fd);
return -1;
}

printf("客户端连接建立\n");

// 准备msghdr结构用于接收控制信息
memset(&msg, 0, sizeof(msg));

// 设置接收缓冲区
iov&#91;0].iov_base = buffer;
iov&#91;0].iov_len = sizeof(buffer) - 1;
msg.msg_iov = iov;
msg.msg_iovlen = 1;

// 设置控制缓冲区
msg.msg_control = control_buffer;
msg.msg_controllen = sizeof(control_buffer);

// 接收消息和控制信息
bytes_received = recvmsg(client_fd, &msg, 0);
if (bytes_received == -1) {
perror("recvmsg 失败");
close(client_fd);
close(server_fd);
return -1;
}

buffer&#91;bytes_received] = '\0';
printf("接收到消息: %s\n", buffer);
printf("接收时间戳信息:\n");

// 解析控制信息
struct cmsghdr *cmsg;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
struct timeval *tv = (struct timeval*)CMSG_DATA(cmsg);
printf(" 时间戳: %ld.%06ld\n", tv->tv_sec, tv->tv_usec);

// 转换为可读格式
char time_str&#91;64];
time_t sec = tv->tv_sec;
struct tm *tm_info = localtime(&sec);
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info);
printf(" 可读时间: %s.%06ld\n", time_str, tv->tv_usec);
} else {
printf(" 其他控制信息: level=%d, type=%d\n",
cmsg->cmsg_level, cmsg->cmsg_type);
}
}

if (msg.msg_controllen == 0) {
printf(" 没有接收到控制信息\n");
}

close(client_fd);
close(server_fd);

return 0;
}

int main() {
return demo_recvmsg_control_data();
}

示例4:文件描述符传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#define _GNU_SOURCE
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>

/**
* 演示通过recvmsg传递文件描述符
*/
int demo_recvmsg_fd_passing() {
int sv&#91;2]; // socket pair
struct msghdr msg;
struct iovec iov&#91;1];
char buffer&#91;256];
char control_buffer&#91;CMSG_SPACE(sizeof(int))];
ssize_t bytes_received;

printf("=== recvmsg 文件描述符传递示例 ===\n");

// 创建socket pair用于进程间通信
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
perror("创建socket pair失败");
return -1;
}

printf("创建了socket pair: %d, %d\n", sv&#91;0], sv&#91;1]);

if (fork() == 0) {
// 子进程:发送文件描述符
close(sv&#91;0]); // 关闭接收端

// 创建一个临时文件
const char *temp_filename = "/tmp/fd_pass_test.txt";
int temp_fd = open(temp_filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (temp_fd == -1) {
perror("创建临时文件失败");
close(sv&#91;1]);
exit(1);
}

// 写入一些数据
const char *test_data = "This data is in the passed file descriptor";
write(temp_fd, test_data, strlen(test_data));

printf("子进程创建了文件: %s\n", temp_filename);
printf("子进程准备传递文件描述符 %d\n", temp_fd);

// 准备发送消息和文件描述符
struct msghdr send_msg;
struct iovec send_iov&#91;1];
struct cmsghdr *cmsg;
char send_buffer&#91;] = "File descriptor passed";
char send_control&#91;CMSG_SPACE(sizeof(int))];

// 设置消息内容
send_iov&#91;0].iov_base = send_buffer;
send_iov&#91;0].iov_len = strlen(send_buffer);

memset(&send_msg, 0, sizeof(send_msg));
send_msg.msg_iov = send_iov;
send_msg.msg_iovlen = 1;
send_msg.msg_control = send_control;
send_msg.msg_controllen = sizeof(send_control);

// 设置控制信息(文件描述符)
cmsg = CMSG_FIRSTHDR(&send_msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(cmsg), &temp_fd, sizeof(int));

send_msg.msg_controllen = cmsg->cmsg_len;

// 发送消息和文件描述符
if (sendmsg(sv&#91;1], &send_msg, 0) == -1) {
perror("sendmsg 失败");
close(temp_fd);
close(sv&#91;1]);
exit(1);
}

printf("子进程发送了消息和文件描述符\n");

// 关闭原始文件描述符
close(temp_fd);
unlink(temp_filename);
close(sv&#91;1]);

exit(0);
} else {
// 父进程:接收文件描述符
close(sv&#91;1]); // 关闭发送端

// 准备接收消息和文件描述符
memset(&msg, 0, sizeof(msg));

iov&#91;0].iov_base = buffer;
iov&#91;0].iov_len = sizeof(buffer) - 1;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = control_buffer;
msg.msg_controllen = sizeof(control_buffer);

// 接收消息和文件描述符
bytes_received = recvmsg(sv&#91;0], &msg, 0);
if (bytes_received == -1) {
perror("recvmsg 失败");
close(sv&#91;0]);
return -1;
}

buffer&#91;bytes_received] = '\0';
printf("父进程接收到消息: %s\n", buffer);

// 解析接收到的文件描述符
int received_fd = -1;
struct cmsghdr *cmsg;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
memcpy(&received_fd, CMSG_DATA(cmsg), sizeof(int));
printf("父进程接收到文件描述符: %d\n", received_fd);
break;
}
}

if (received_fd != -1) {
// 使用接收到的文件描述符读取数据
char read_buffer&#91;256];
lseek(received_fd, 0, SEEK_SET); // 重置文件位置
ssize_t read_bytes = read(received_fd, read_buffer, sizeof(read_buffer) - 1);
if (read_bytes > 0) {
read_buffer&#91;read_bytes] = '\0';
printf("从传递的文件描述符读取数据: %s\n", read_buffer);
}

// 关闭接收到的文件描述符
close(received_fd);
} else {
printf("没有接收到文件描述符\n");
}

close(sv&#91;0]);

// 等待子进程结束
int status;
wait(&status);
}

return 0;
}

int main() {
return demo_recvmsg_fd_passing();
}

示例5:完整的网络服务器示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <poll.h>
#include <time.h>

/**
* 网络服务器结构
*/
typedef struct {
int server_fd;
int port;
struct pollfd *clients;
int max_clients;
int client_count;
} network_server_t;

/**
* 初始化网络服务器
*/
int server_init(network_server_t *server, int port, int max_clients) {
struct sockaddr_in server_addr;

memset(server, 0, sizeof(network_server_t));
server->port = port;
server->max_clients = max_clients;
server->client_count = 0;

// 分配客户端数组
server->clients = calloc(max_clients + 1, sizeof(struct pollfd));
if (!server->clients) {
perror("分配客户端数组失败");
return -1;
}

// 创建服务器套接字
server->server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server->server_fd == -1) {
perror("创建服务器套接字失败");
free(server->clients);
return -1;
}

// 设置套接字选项
int opt = 1;
if (setsockopt(server->server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
perror("设置套接字选项失败");
close(server->server_fd);
free(server->clients);
return -1;
}

// 设置服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(port);

// 绑定套接字
if (bind(server->server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("绑定套接字失败");
close(server->server_fd);
free(server->clients);
return -1;
}

// 监听连接
if (listen(server->server_fd, 10) == -1) {
perror("监听失败");
close(server->server_fd);
free(server->clients);
return -1;
}

// 设置服务器套接字为poll监听
server->clients&#91;0].fd = server->server_fd;
server->clients&#91;0].events = POLLIN;

printf("网络服务器初始化完成,监听端口 %d\n", port);
return 0;
}

/**
* 接受新客户端连接
*/
int server_accept_client(network_server_t *server) {
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_fd;

client_fd = accept(server->server_fd, (struct sockaddr*)&client_addr, &client_len);
if (client_fd == -1) {
perror("接受连接失败");
return -1;
}

if (server->client_count >= server->max_clients) {
printf("客户端数量已达上限,拒绝连接\n");
close(client_fd);
return -1;
}

// 添加到客户端数组
int index = server->client_count + 1;
server->clients&#91;index].fd = client_fd;
server->clients&#91;index].events = POLLIN;
server->client_count++;

printf("新客户端连接: %s:%d (fd=%d)\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), client_fd);

return 0;
}

/**
* 使用recvmsg处理客户端消息
*/
int server_handle_client_message(network_server_t *server, int client_index) {
int client_fd = server->clients&#91;client_index].fd;
struct msghdr msg;
struct iovec iov&#91;2];
char buffer1&#91;512], buffer2&#91;512];
char control_buffer&#91;1024];
ssize_t bytes_received;

// 准备msghdr结构
memset(&msg, 0, sizeof(msg));

// 设置分散缓冲区
iov&#91;0].iov_base = buffer1;
iov&#91;0].iov_len = sizeof(buffer1) - 1;
iov&#91;1].iov_base = buffer2;
iov&#91;1].iov_len = sizeof(buffer2) - 1;
msg.msg_iov = iov;
msg.msg_iovlen = 2;

// 设置控制缓冲区
msg.msg_control = control_buffer;
msg.msg_controllen = sizeof(control_buffer);

// 接收消息
bytes_received = recvmsg(client_fd, &msg, 0);
if (bytes_received == -1) {
if (errno == ECONNRESET) {
printf("客户端 %d 连接重置\n", client_fd);
} else {
perror("recvmsg 失败");
}
return -1;
}

if (bytes_received == 0) {
printf("客户端 %d 关闭连接\n", client_fd);
return -1;
}

printf("从客户端 %d 接收到 %zd 字节数据\n", client_fd, bytes_received);

// 处理接收到的数据
size_t total_copied = 0;
char full_message&#91;1024];
full_message&#91;0] = '\0';

for (int i = 0; i < 2 && total_copied < (size_t)bytes_received; i++) {
size_t to_copy = iov&#91;i].iov_len;
if (total_copied + to_copy > (size_t)bytes_received) {
to_copy = bytes_received - total_copied;
}

strncat(full_message, (char*)iov&#91;i].iov_base, to_copy);
total_copied += to_copy;
}

printf(" 消息内容: %s\n", full_message);
printf(" 使用缓冲区数: %d\n", (int)msg.msg_iovlen);
printf(" 消息标志: %d\n", msg.msg_flags);

// 回显消息
char response&#91;1024];
snprintf(response, sizeof(response), "Echo: %s", full_message);
send(client_fd, response, strlen(response), 0);

return 0;
}

/**
* 运行服务器主循环
*/
int server_run(network_server_t *server) {
printf("服务器开始运行,等待客户端连接...\n");

while (1) {
// 使用poll等待事件
int nfds = server->client_count + 1;
int activity = poll(server->clients, nfds, 1000); // 1秒超时

if (activity == -1) {
if (errno == EINTR) continue; // 被信号中断
perror("poll 失败");
break;
}

if (activity == 0) {
// 超时,继续循环
continue;
}

// 检查服务器套接字(新连接)
if (server->clients&#91;0].revents & POLLIN) {
server_accept_client(server);
activity--;
}

// 检查客户端套接字
for (int i = 1; i <= server->client_count && activity > 0; i++) {
if (server->clients&#91;i].revents & POLLIN) {
if (server_handle_client_message(server, i) == -1) {
// 客户端断开连接,移除客户端
close(server->clients&#91;i].fd);
// 将最后一个客户端移到当前位置
if (i < server->client_count) {
server->clients&#91;i] = server->clients&#91;server->client_count];
}
server->client_count--;
i--; // 重新检查当前位置
}
activity--;
}
}
}

return 0;
}

/**
* 清理服务器资源
*/
void server_cleanup(network_server_t *server) {
// 关闭所有客户端连接
for (int i = 1; i <= server->client_count; i++) {
close(server->clients&#91;i].fd);
}

// 关闭服务器套接字
if (server->server_fd != -1) {
close(server->server_fd);
}

// 释放内存
if (server->clients) {
free(server->clients);
}

printf("服务器资源清理完成\n");
}

/**
* 演示完整的网络服务器
*/
int demo_complete_network_server() {
network_server_t server;

printf("=== 完整网络服务器示例 ===\n");

// 初始化服务器
if (server_init(&server, 8083, 10) != 0) {
return -1;
}

// 启动测试客户端
if (fork() == 0) {
sleep(2); // 等待服务器启动

// 创建多个客户端进行测试
for (int i = 0; i < 3; i++) {
if (fork() == 0) {
int client_sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serv_addr;

memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8083);
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

if (connect(client_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == 0) {
char message&#91;256];
snprintf(message, sizeof(message), "Hello from client %d", i + 1);

send(client_sock, message, strlen(message), 0);
printf("客户端 %d 发送: %s\n", i + 1, message);

// 接收回显
char response&#91;1024];
ssize_t bytes = recv(client_sock, response, sizeof(response) - 1, 0);
if (bytes > 0) {
response&#91;bytes] = '\0';
printf("客户端 %d 接收回显: %s\n", i + 1, response);
}

sleep(1);
}

close(client_sock);
exit(0);
}
}

// 等待所有客户端完成
for (int i = 0; i < 3; i++) {
int status;
wait(&status);
}

exit(0);
}

// 运行服务器30秒
printf("服务器将运行30秒...\n");
sleep(30);

// 清理资源
server_cleanup(&server);

// 等待测试客户端结束
int status;
wait(&status);

return 0;
}

int main() {
return demo_complete_network_server();
}

recvmsg 标志参数详解

常用标志:

  • MSG_OOB: 接收带外数据

  • MSG_PEEK: 查看数据但不从队列中移除

  • MSG_WAITALL: 等待接收完整的消息

  • MSG_TRUNC: 返回数据包的实际长度(UDP)

  • MSG_CTRUNC: 控制数据被截断

高级标志:

  • MSG_DONTWAIT: 非阻塞操作

  • MSG_ERRQUEUE: 接收错误队列中的数据

  • MSG_NOSIGNAL: 接收时不产生SIGPIPE信号

使用注意事项

性能考虑:

缓冲区管理: 合理设置缓冲区大小避免频繁分配

分散缓冲区: 适当使用scatter-gather I/O提高效率

控制信息: 只在需要时启用控制信息接收

错误处理:

部分接收: 处理数据被截断的情况

连接状态: 检查连接是否正常关闭

资源清理: 及时关闭文件描述符和释放内存

安全考虑:

缓冲区溢出: 确保缓冲区大小足够且正确处理

权限检查: 验证传递的文件描述符权限

输入验证: 验证接收到的数据内容

总结

recvmsg 是Linux网络编程中最强大的接收函数,提供了:

  • 基本数据接收功能

  • 分散缓冲区接收(scatter-gather I/O)

  • 控制信息接收(时间戳、文件描述符等)

  • 地址信息接收

  • 灵活的标志控制

通过合理使用 recvmsg,可以构建高性能、功能丰富的网络应用程序。

recvmsg系统调用及示例-CSDN博客