[OS] EXPORT_SYMBOL()

在 Linux 内核中,EXPORT_SYMBOL() 用于将模块中的函数或变量导出,使得其他内核模块能够使用这些导出的符号。这对于模块之间共享功能或数据非常有用。给出的代码示例展示了如何使用 EXPORT_SYMBOL() 将变量和函数导出供其他模块使用。

/* ... */
int GLOBAL_VARIABLE = 1000;
EXPORT_SYMBOL(GLOBAL_VARIABLE);
// Function to print hello for num times.
void print_hello(int num)
{
while (num--) {
printk(KERN_INFO "Hello Friend!!!\n");
}
}
EXPORT_SYMBOL(print_hello);
// Function to add two passed number.
void add_two_numbers(int a, int b)
{
printk(KERN_INFO "Sum of the numbers %d", a + b);
}
EXPORT_SYMBOL(add_two_numbers);
static int __init my_init(void)
{
printk(KERN_INFO "Hello from Export Symbol 1 module.");
return 0;
}
static void __exit my_exit(void)
{
printk(KERN_INFO "Bye from Export Symbol 1 module.");
}
module_init(my_init);
module_exit(my_exit);
/* ... */

 

int GLOBAL_VARIABLE = 1000;
EXPORT_SYMBOL(GLOBAL_VARIABLE);
  • GLOBAL_VARIABLE 是一个全局变量,初始值为 1000。通过 EXPORT_SYMBOL(GLOBAL_VARIABLE),该变量被导出,使得其他模块可以通过它访问或修改这个变量。
  • 用途: 这在多个模块需要共享同一个全局变量时非常有用。例如,如果多个模块需要共享一个状态变量,它们可以通过导出这个全局变量实现。
2. 导出函数 print_hello()
void print_hello(int num) {while (num--) {printk(KERN_INFO "Hello Friend!!!\n");}
}
EXPORT_SYMBOL(print_hello);

 

  • print_hello() 函数用于打印指定次数的 "Hello Friend!!!" 消息。
  • 通过 EXPORT_SYMBOL(print_hello),该函数也被导出,使得其他模块可以调用 print_hello() 函数。
  • 用途: 这在需要其他模块执行类似任务时很有用。例如,一个通用的日志输出功能可以通过导出函数供多个模块使用。
3. 导出函数 add_two_numbers()
void add_two_numbers(int a, int b) {printk(KERN_INFO "Sum of the numbers %d", a + b);
}
EXPORT_SYMBOL(add_two_numbers);
  • add_two_numbers() 函数用于打印传入的两个整数的和。
  • 通过 EXPORT_SYMBOL(add_two_numbers),该函数也被导出,使得其他模块可以调用它来计算两个数字的和并输出结果。
  • 用途: 当多个模块需要类似的简单计算时,这样的功能可以被复用。
4. 模块的初始化和退出函数

  • my_init():这是模块加载时执行的初始化函数。它在加载时输出一条消息,表明模块已被加载。
  • my_exit():这是模块卸载时执行的清理函数。它在模块卸载时输出一条消息,表明模块已被卸载。
  • module_init()module_exit() 用来注册模块的初始化和退出函数。

比喻:

可以把 EXPORT_SYMBOL() 想象成把某些工具放在一个“共享工具箱”中,供其他模块(类似于工程师)使用。每个工程师(模块)都可以从工具箱里取出这些工具(导出的函数或变量)来完成任务,而不需要自己重新造轮子。

导出的 API 在另一个模块中的使用:

假设我们有另一个模块 myModule2,这个模块想要使用 myModule1 中导出的 GLOBAL_VARIABLEprint_hello()

myModule2 示例代码:
#include <linux/init.h>
#include <linux/module.h>extern int GLOBAL_VARIABLE;  // 声明外部导出的全局变量
extern void print_hello(int num);  // 声明外部导出的函数static int __init my_module_init(void) {printk(KERN_INFO "Hello from myModule2.\n");// 使用导出的变量和函数printk(KERN_INFO "GLOBAL_VARIABLE is: %d\n", GLOBAL_VARIABLE);print_hello(5);  // 打印5次 "Hello Friend!!!"return 0;
}static void __exit my_module_exit(void) {printk(KERN_INFO "Bye from myModule2.\n");
}module_init(my_module_init);
module_exit(my_module_exit);MODULE_LICENSE("GPL");
  • extern 关键字:

    • 使用 extern 关键字来声明在 myModule1 中导出的变量和函数。这告诉内核这个符号已经在其他模块中定义,可以直接引用。
  • 使用导出的变量和函数:

    • 通过 GLOBAL_VARIABLEmyModule2 可以访问 myModule1 中的全局变量。
    • 通过调用 print_hello(5)myModule2 可以调用 myModule1 中的函数,并打印 5 次 "Hello Friend!!!"。

 

/* ... */
extern void print_hello(int);
extern void add_two_numbers(int, int);
extern int GLOBAL_VARIABLE;
/*
* Call functions which are in other module.
*/
static int __init my_init(void)
{
printk(KERN_INFO "Hello from Hello Module");
print_hello(2);
add_two_numbers(5, 6);
printk(KERN_INFO "Value of GLOBAL_VARIABLE %d", GLOBAL_VARIABLE);
return 0;
}
static void __exit my_exit(void)
{
printk(KERN_INFO "Bye from Hello Module");
}
module_init(my_init);
module_exit(my_exit);
/* ... */

在 Linux 内核中,模块之间的加载顺序非常重要,尤其当一个模块依赖另一个模块导出的符号时。如果依赖的模块没有先加载,依赖模块将无法找到其需要的符号,导致加载错误。

为什么会出错:

在你给出的场景中:

  • myModule1.ko 导出了全局变量和函数(如 GLOBAL_VARIABLEprint_hello())。
  • myModule2.ko 依赖 myModule1.ko 中导出的符号,并通过 extern 引用它们。

如果你尝试先加载 myModule2.ko,会发生错误,因为在加载 myModule2.ko 时,内核找不到 GLOBAL_VARIABLEprint_hello() 等符号——这些符号还没有被 myModule1.ko 导出。

内核模块加载的过程类似于以下步骤:

  1. 内核会首先检查模块的依赖项,并查看该模块是否需要使用其他模块导出的符号。
  2. 如果依赖的符号没有找到(即所需的模块尚未加载),内核会报错,并拒绝加载该模块。

因此,必须先加载导出符号的模块(myModule1.ko,然后再加载依赖模块(myModule2.ko)。

如何解决:

为了解决模块加载顺序问题,确保在插入内核模块时遵循正确的依赖顺序:

  1. 先插入 myModule1.ko

    • 运行 insmod myModule1.ko 或者 modprobe myModule1,首先将导出符号的模块加载到内核中。
    • 这样,内核会将 myModule1.ko 中的符号(GLOBAL_VARIABLEprint_hello() 等)导出并使其在整个内核中可用。
  2. 再插入 myModule2.ko

    • myModule1.ko 成功加载后,再运行 insmod myModule2.komodprobe myModule2,这时内核可以找到 myModule1.ko 导出的符号,myModule2.ko 将能够正确加载。

错误示例:

如果你反过来加载模块,先加载 myModule2.ko,会看到类似以下的错误:

insmod: error inserting 'myModule2.ko': -1 Unknown symbol in module

 这个错误通常表示模块中有未解析的符号,原因是这些符号(GLOBAL_VARIABLEprint_hello())还没有被内核注册,因为 myModule1.ko 尚未加载。

//Insert myModule1.ko then myModule2.ko and you can see the following:
$ sudo insmod myModule1.ko
$ sudo insmod myModule2.ko
[15606.692155] Hello from Export Symbol 1 module.
[15612.175760] Hello from Hello Module
[15612.175764] Hello Friend!!!
[15612.175766] Hello Friend!!!
[15612.175780] Sum of the numbers 11
[15612.175782] Value of GLOBAL_VARIABLE 1000

5. EXPORT_SYMBOL — Linux Kernel Workbook 1.0 documentation (lkw.readthedocs.io)

Linux World: Exporting symbols from module (tuxthink.blogspot.com)

c - How to prevent "error: 'symbol' undeclared here" despite EXPORT_SYMBOL in a Linux kernel module? - Stack Overflow

c - How to call exported kernel module functions from another module? - Stack Overflow 

 How to define a function in one linux kernel module and use it in another? - Stack Overflow

How to create a working thread 

In our case, the working function is my_fork(), so my_fork() needs to do all the job.

How to create a working process

在 Linux 内核中,kernel_clone() 函数是用来创建一个新进程或线程的底层系统调用。这类似于用户空间的 fork()clone(),但在内核中允许更细粒度的控制。提供的代码片段展示了如何使用 kernel_clone() 来创建一个新进程,并让该进程执行指定的函数(如 hello())。

struct kernel_clone_args clone_args = {.flags = SIGCHLD,.pidfd = NULL,.child_tid = NULL,.parent_tid = NULL,.exit_signal = SIGCHLD,.stack = (unsigned long) &hello,.stack_size = 0,.tls = 0
};
pid_t pid = kernel_clone(&clone_args);

字段解释:

  1. flags:

    • 作用: 用来控制新进程或线程的行为。
    • 在这个例子中,flags = SIGCHLD 表示在子进程终止时会发送 SIGCHLD 信号给父进程。这是常见的用于通知父进程子进程终止的机制。
    • 其他可能的标志
      • CLONE_VM: 子进程共享父进程的地址空间。
      • CLONE_FS: 子进程共享父进程的文件系统信息。
  2. pidfd:

    • 作用: 如果不为 NULL,则存储一个文件描述符(PID file descriptor),该文件描述符指向子进程的 PID。这在进程控制中很有用。
    • 在你的例子中,pidfd = NULL,表示不使用 PID 文件描述符。
  3. parent_tidchild_tid:

    • 作用: 用于线程(而不是进程)的同步。如果不为 NULL,则将父/子线程的 TID(线程 ID)存储到指定的地址中。
    • 在你的例子中,这两个字段都被设置为 NULL,表示不使用线程 ID。
  4. exit_signal:

    • 作用: 指定子进程终止时父进程接收到的信号。在这个例子中,exit_signal = SIGCHLD 表示当子进程终止时,父进程会接收到 SIGCHLD 信号。
    • 这与 fork() 系统调用的默认行为一致。
  5. stack:

    • 作用: 该字段指定新进程的栈起始地址。
    • 在这个例子中,stack = (unsigned long) &hello,即将 hello 函数的地址作为栈地址传递。这意味着当新进程启动时,它将执行 hello() 函数。
    • 注意:在一般情况下,stack 通常用于指定用户态进程的栈地址。而在这里,hello() 函数将被用作新进程的执行入口。
  6. stack_size:

    • 作用: 用于指定栈的大小。
    • 在这个例子中,stack_size = 0,表示默认的栈大小。这意味着内核会使用系统默认的栈大小。
  7. tls:

    • 作用: 这是线程局部存储(Thread Local Storage)的指针,用于在多线程环境中给每个线程分配独立的存储区域。
    • 在这个例子中,tls = 0,表示不使用线程局部存储。

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

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

相关文章

Dolma:包含三万亿Token的语言模型预训练研究开放语料库

前言 原论文&#xff1a;Dolma: an Open Corpus of Three Trillion Tokens for Language Model Pretraining Research 摘要 关于训练当前最佳性能语言模型的预训练语料库的信息很少被讨论——商业模型很少详细说明它们的数据&#xff0c;即使是开源模型也往往在没有训练数据…

Ubuntu开机进入紧急模式处理

文章目录 Ubuntu开机进入紧急模式处理一、问题描述二、解决办法参考 Ubuntu开机进入紧急模式处理 一、问题描述 Ubuntu开机不能够正常启动&#xff0c;自动进入紧急模式&#xff08;You are in emergency mode&#xff09;。具体如下所示&#xff1a; 二、解决办法 按CtrlD进…

基于开源大型lmm模型生成标签对InternVL2-1B等轻量lmm模型进行微调

基于开源大型lmm模型生成标签对InternVL2-1B等轻量lmm模型进行微调,提升InternVL2-1B等轻量lmm模型的能力。本实验在window下,基于3060 12g显卡进行实验。基于qwen2-vl 7b模型生成标签(电脑显存大的话可以考虑qwen2-vl 72b模型),然后对InternVL2-1B进行Lora微调。以voc201…

Perl 子程序(函数)

Perl 子程序&#xff08;函数&#xff09; Perl 是一种高级、解释型、动态编程语言&#xff0c;广泛用于CGI脚本、系统管理、网络编程、 finance, bioinformatics, 以及其他领域。在Perl中&#xff0c;子程序&#xff08;也称为函数&#xff09;是组织代码和重用代码块的重要方…

《机器学习》周志华-CH10(降维与度量学习)

10.1k近邻学习 k k k近邻(k-Nearest Neighbor,简称kNN)&#xff0c;监督学习。 工作机制&#xff1a;给定测试样本&#xff0c;基于某种距离度量找出训练集中与其最靠近的 k k k个训练样本&#xff0c;基于这些”邻居“预测。 { 分类任务&#xff1a;选择”投票法“。 k 个样本…

MySQL之复合查询与内外连接

目录 一、多表查询 二、自连接 三、子查询 四、合并查询 五、表的内连接和外连接 1、内连接 2、外连接 前面我们讲解的mysql表的查询都是对一张表进行查询&#xff0c;即数据的查询都是在某一时刻对一个表进行操作的。而在实际开发中&#xff0c;我们往往还需要对多个表…

如何使用MATLAB代码生成器生成ADRC跟踪微分器(TD) PLC源代码(SCL)

ADRC线性跟踪微分器TD详细测试 ADRC线性跟踪微分器TD详细测试(Simulink 算法框图+CODESYS ST+博途SCL完整源代码)-CSDN博客文章浏览阅读383次。ADRC线性跟踪微分器(ST+SCL语言)_adrc算法在博途编程中scl语言-CSDN博客文章浏览阅读784次。本文介绍了ADRC线性跟踪微分器的算法和…

关于启动flask应用,其他主机无法访问flask应用的错误记录

目录 前言解决办法 前言 因为一些原因&#xff0c;接触到了web应用框架flask。在一台主机上启动flask app&#xff0c;尝试过将app.run中的host参数设置为127.0.0.1;0.0.0.0。甚至是服务器主机的ip&#xff0c;可结果是只有服务器本机才能访问服务&#xff0c;其他不管是外网&…

Bolt.new:终极自动化编程工具

兄弟们&#xff0c;终极写代码工具来了—— Bolt.new&#xff01;全方位的编程支持&#xff1a; StackBlitz 推出了 Bolt․new&#xff0c;这是一款结合了 AI 与 WebContainers 技术的强大开发平台&#xff0c;允许用户快速搭建并开发各种类型的全栈应用。 它的主要特点是无需…

Anaconda的安装与环境设置

文章目录 一、Anaconda介绍二、Anaconda环境搭建1. 下载Anaconda(1)官网下载(2)清华大学镜像 2. 安装Anaconda3.配置环境变量4.检验conda是否安装成功5.更改镜像源6.若菜单栏没有conda prompt 三、虚拟环境1.创建、查看、删除虚拟环境2.激活、退出虚拟环境 四、CUDA、Pytorch、…

JS 入门

文章目录 JS 入门一、JS 概述1、JS 特点2、JS 组成3、JS 初体验4、HTML引入JS 二、JS 基础语法1、变量声明2、基本数据类型3、引用数据类型1&#xff09;数组2&#xff09;对象3&#xff09;函数4&#xff09;null 4、运算符5、条件判断6、循环语句 三、JS 函数0、JS 函数特点1…

上传文件失败,请检查阿里云配置信息:[The specified bucket is not valid.

-- 十一假期结束 -- 去年今日此门中&#xff0c;人面挑花相应红。 -- 人面不知何处去&#xff0c;桃花依旧笑春风。

Pikachu-unsafe upfileupload-getimagesize

什么是getimagesize()&#xff1f; getimagesize()是PHP中用于获取图像的大小和格式的函数。它可以返回一个包含图像的宽度、高度、类型和MIME类型的数组。 由于返回的这个类型可以被伪造&#xff0c;如果用这个函数来获取图片类型&#xff0c;从而判断是否时图片的话&#xff…

虚拟机 VMware 安装 macOS

macOS 界面 MAC OS IOS下载&#xff1a; amacOS Monterey by Techrechard.comwmacOS Monterey by Techrechard.com 下载&#xff1a;Unlocker-v2.0.1-x64 Mac OS X 虚拟机中更改屏幕分辨率 终端输入命令&#xff1a; sudo defaults write /Library/Preferences/com.apple.w…

vim编辑器安装,并修改配置使其默认显示行数

centOS默认是未安装vim编辑器的&#xff0c;而vim编辑器相比vi编辑器更易用一些&#xff0c;如需使用vim编辑器&#xff0c;需要进行安装。 1.需要先配置本地yum源&#xff0c;参见如下链接&#xff1a; 点击查看如何配置本地yum源 2.安装vim编辑器&#xff0c;并修改配置。…

昇思学习打卡营学习记录:Pix2Pix实现图像转换

按照提示&#xff0c;运行实训代码 进入实训平台&#xff1a;https://xihe.mindspore.cn/projects 选择“jupyter 在线编辑器” 启动“Ascend开发环境” &#xff1a;Ascend开发环境需要申请&#xff0c;大家可以申请试试看 启动开发环境后&#xff0c;在左边的文件夹中&am…

kafka和zookeeper单机部署

安装kafka需要jdk和zookeeper环境&#xff0c;因此先部署单机zk的测试环境。 zookeeper离线安装 下载地址&#xff1a; zookeeper下载地址&#xff1a;Index of /dist/zookeeper 这里下载安装 zookeeper-3.4.6.tar.gz 版本&#xff0c;测试环境单机部署 上传服务器后解压缩 …

贪心算法相关知识

目录 基础 定义 工作原理 步骤一&#xff1a;分解问题 步骤二&#xff1a;确定贪心策略 步骤三&#xff1a;求解子问题 步骤四&#xff1a;合并结果 适用场景 活动安排问题 找零问题 哈夫曼编码 局限性 高级 与动态规划的对比 决策方式 最优性保证 时间复杂度和…

Elasticsearch基础_5.ES聚合功能

文章目录 一、数据聚合1.1、桶聚合1.1.1、单维度桶聚合1.1.2、聚合结果排序1.1.3、限定聚合范围 1.2、Metric聚合 二、聚合总结 本文只记录ES聚合基本用法&#xff0c;后续有更复杂的需求可以查看相关书籍&#xff0c;如《Elasticsearch搜索引擎构建入门与实战》 一、数据聚合…

幂,你去哪儿了-《分析模式》漫谈37

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 “Analysis Patterns”的第3章的图3.5&#xff0c;原文的图是&#xff1a; 2004&#xff08;机械工业出版社&#xff09;中译本的图是&#xff1a; direct翻译成分子&#xff0c;inv…