Linux 结构体模块简介 (linux struct module)

在Linux系统中,结构体模块是一种重要的编程工具。结构体模块可以用来定义一组相关的变量,并将其打包到一个单独的结构体中。这样做的好处在于可以方便地管理变量,并且可以将多个变量作为一个整体来操作。

结构体模块的语法

定义结构体模块的语法如下:

“`

struct struct_name {

type1 member1;

type2 member2;

.

.

.

typeN memberN;

};

“`

其中,`struct_name`是结构体的名称,`type1`、`type2`、……、`typeN`是成员变量的类型,`member1`、`member2`、……、`memberN`是成员变量的名称。

通过上述语法定义的结构体模块可以用来定义一组相关的变量,并将其打包到一个单独的结构体中。例如,我们可以定义一个表示人的结构体模块,如下所示:

“`

struct person {

char name[20];

int age;

char gender;

float height;

};

“`

这个结构体模块包含4个成员变量,分别是姓名、年龄、性别、身高。这些成员变量可以被认为是人的属性,通过定义这个结构体模块,我们可以方便地管理这些属性,并将它们作为一个整体来操作。

使用结构体模块

使用结构体模块有两个主要的步骤:定义结构体类型和创建结构体实例。

定义结构体类型

定义结构体类型的过程实际上就是定义一个结构体模块。例如,我们可以在C语言中定义一个人的结构体类型,如下所示:

“`

struct person {

char name[20];

int age;

char gender;

float height;

};

“`

在这个例子中,我们定义了一个名为`person`的结构体类型,这个类型包含4个成员变量:姓名、年龄、性别和身高。

创建结构体实例

定义结构体类型之后,我们可以根据这个类型创建结构体实例。例如,我们可以创建一个名为`bob`的`person`实例,如下所示:

“`

struct person bob;

“`

这个语句会创建一个`person`类型的实例,名为`bob`,并且可以使用`.`运算符来访问这个实例的成员变量,例如:

“`

strcpy(bob.name, “Bob”);

bob.age = 25;

bob.gender = ‘M’;

bob.height = 1.75;

“`

这些语句分别给`bob`实例的姓名、年龄、性别和身高赋值。

结构体模块的应用

结构体模块在Linux系统中有广泛的应用,尤其是在内核编程中。下面我们来介绍几个有关结构体模块的应用。

内核模块

Linux内核中的模块是一种可以被动态加载和卸载的代码单元。模块通常包含一组相关的函数和数据结构,用来实现某个特定的功能。在内核中,结构体模块常常被用来定义内核数据结构,例如:

“`

struct task_struct {

pid_t pid;

struct list_head run_list;

struct timer_list timer;

wt_queue_head_t wt;

……

};

“`

这个结构体模块定义了内核中的进程数据结构,包括进程ID、运行队列、定时器、等待队列等成员变量。

驱动程序

Linux驱动程序是一种在系统内核空间中运行的程序,它们负责管理和控制硬件设备。在驱动程序中,结构体模块通常被用来定义和表示硬件设备,例如:

“`

struct my_device {

struct cdev cdev;

struct device *device;

int irq;

unsigned long base_addr;

unsigned long mem_addr;

……

};

“`

这个结构体模块定义了一个虚拟设备,包括字符设备、设备对象、中断号、基地址、内存地址等成员变量。

结构体模块是Linux系统中一个非常重要的编程工具,它可以用来方便地管理变量,并将多个变量作为一个整体来操作。结构体模块在内核编程和驱动程序开发中有广泛的应用,是一种非常基础和重要的编程技巧。掌握好结构体模块的使用方法对Linux编程非常有帮助。

相关问题拓展阅读:

一文搞懂 , Linux内核—— 同步管理(下)

上面讲的自旋锁,信号量和互斥锁的实现,都是使用了原子操作指令。由于原子操作会 lock,当线程在多个 CPU 上争抢进入临界区的时候,都会操作那个在多个 CPU 之间共享的数据 lock。CPU 0 操作了 lock,为了数据的一致性,CPU 0 的操作如厅雀会导致其他 CPU 的 L1 中的 lock 变成 invalid,在随后的来自其他 CPU 对 lock 的访问会导致 L1 cache miss(更准确的说是communication cache miss),必须从下一个 level 的 cache 中获取。

这就会使缓存一致性变得很糟,导致性能下降。所以内核提供一种新的同步方式:RCU(读-复制-更新)。

RCU 解决了什么

RCU 是读写锁的高性能版本,它的核心理念是读者访问的同时,写者可以更新访问对象的副本,但写者需要等待所渣早有读者完成访问之后,才能删除老对象。读者没有任何同步开销,而写者的同步开销则取决于使用的写者间同步机制。

RCU 适用于需要频繁的读取数据,而相应修改数据并不多的情景,例如在文件系统中,经常需要查找定位伏迟目录,而对目录的修改相对来说并不多,这就是 RCU 发挥作用的更佳场景。

RCU 例子

RCU 常用的接口如下图所示:

为了更好的理解,在剖析 RCU 之前先看一个例子:

#include#include#include#include#include#include#include#includestructfoo{inta;structrcu_headrcu;};staticstructfoo*g_ptr;staticintmyrcu_reader_thread1(void*data)//读者线程1{structfoo*p1=NULL;while(1){if(kthread_should_stop())break;msleep(20);rcu_read_lock();mdelay(200);p1=rcu_dereference(g_ptr);if(p1)printk(“%s: read a=%d\n”,__func__,p1->a);rcu_read_unlock();}return0;}staticintmyrcu_reader_thread2(void*data)//读者线程2{structfoo*p2=NULL;while(1){if(kthread_should_stop())break;msleep(30);rcu_read_lock();mdelay(100);p2=rcu_dereference(g_ptr);if(p2)printk(“%s: read a=%d\n”,__func__,p2->a);rcu_read_unlock();}return0;}staticvoidmyrcu_del(structrcu_head*rh)//回收处理操作{structfoo*p=container_of(rh,structfoo,rcu);printk(“%s: a=%d\n”,__func__,p->a);kfree(p);}staticintmyrcu_writer_thread(void*p)//写者线程{structfoo*old;structfoo*new_ptr;intvalue=(unsignedlong)p;while(1){if(kthread_should_stop())break;msleep(250);new_ptr=kmalloc(sizeof(structfoo),GFP_KERNEL);old=g_ptr;*new_ptr=*old;new_ptr->a=value;rcu_assign_pointer(g_ptr,new_ptr);call_rcu(&old->rcu,myrcu_del);printk(“%s: write to new %d\n”,__func__,value);value++;}return0;}staticstructtask_struct*reader_thread1;staticstructtask_struct*reader_thread2;staticstructtask_struct*writer_thread;staticint__initmy_test_init(void){intvalue=5;printk(“figo: my module init\n”);g_ptr=kzalloc(sizeof(structfoo),GFP_KERNEL);reader_thread1=kthread_run(myrcu_reader_thread1,NULL,”rcu_reader1″);reader_thread2=kthread_run(myrcu_reader_thread2,NULL,”rcu_reader2″);writer_thread=kthread_run(myrcu_writer_thread,(void*)(unsignedlong)value,”rcu_writer”);return0;}staticvoid__exitmy_test_exit(void){printk(“goodbye\n”);kthread_stop(reader_thread1);kthread_stop(reader_thread2);kthread_stop(writer_thread);if(g_ptr)kfree(g_ptr);}MODULE_LICENSE(“GPL”);module_init(my_test_init);module_exit(my_test_exit);

执行结果是:

myrcu_reader_thread2:reada=0myrcu_reader_thread1:reada=0myrcu_reader_thread2:reada=0myrcu_writer_thread:writetonew5myrcu_reader_thread2:reada=5myrcu_reader_thread1:reada=5myrcu_del:a=0

RCU 原理

可以用下面一张图来总结,当写线程 myrcu_writer_thread 写完后,会更新到另外两个读线程 myrcu_reader_thread1 和 myrcu_reader_thread2。读线程像是订阅者,一旦写线程对临界区有更新,写线程就像发布者一样通知到订阅者那里,如下图所示。

写者在拷贝副本修改后进行 update 时,首先把旧的临界资源数据移除(Removal);然后把旧的数据进行回收(Reclamation)。结合 API 实现就是,首先使用 rcu_assign_pointer 来移除旧的指针指向,指向更新后的临界资源;然后使用 synchronize_rcu 或 call_rcu 来启动 Reclaimer,对旧的临界资源进行回收(其中 synchronize_rcu 表示同步等待回收,call_rcu 表示异步回收)。

为了确保没有读者正在访问要回收的临界资源,Reclaimer 需要等待所有的读者退出临界区,这个等待的时间叫做宽限期(Grace Period)。

Grace Period

中间的黄色部分代表的就是 Grace Period,中文叫做宽限期,从 Removal 到 Reclamation,中间就隔了一个宽限期,只有当宽限期结束后,才会触发回收的工作。宽限期的结束代表着 Reader 都已经退出了临界区,因此回收工作也就是安全的操作了。

宽限期是否结束,与 CPU 的执行状态检测有关,也就是检测静止状态 Quiescent Status。

Quiescent Status

Quiescent Status,用于描述 CPU 的执行状态。当某个 CPU 正在访问 RCU 保护的临界区时,认为是活动的状态,而当它离开了临界区后,则认为它是静止的状态。当所有的 CPU 都至少经历过一次 Quiescent Status 后,宽限期将结束并触发回收工作。

因为 rcu_read_lock 和 rcu_read_unlock 分别是关闭抢占和打开抢占,如下所示:

staticinlinevoid__rcu_read_lock(void){preempt_disable();}

staticinlinevoid__rcu_read_unlock(void){preempt_enable();}

所以发生抢占,就说明不在 rcu_read_lock 和 rcu_read_unlock 之间,即已经完成访问或者还未开始访问。

Linux 同步方式的总结

资料免费领

学习直通车

在Linux内核模块中对空指针解引用,为什么内核不挂

内核模块linux源代码联系些概念我都模糊linux系统由各种各内核模块组实验源代码://my_proc.c#include#include#include猜碰则#include#include#define STRINGLEN 1024char global_buffer;struct proc_dir_entry *example_dir,*Tang_file,*Yang_file,*symlink;int proc_read_Tang(char *page,char **start,off_t off,int count,int *eof,void *data){//用户读取Tang文穗棚件内核调用函数int len;try_module_get(THIS_MODULE);//模块引用计数器len=printf(page,”Tang message:\nname: %s\npid: %d\n”,current->comm,current->pid);module_put(THIODULE);return len;}int proc_read_Yang(char *page,char **start,off_t off,int count,int *eof,void *data){//用户读取Yu文件内核调用函数int len;try_module_get(THIS_MODULE);len=printf(page,”Yang message:\n%s write: %s\n”,current->comm,global_buffer);module_put(THIODULE);return len;}int proc_write_Yang(struct file *file, const char *buffer, unsigned long count, void *data){//用户读写数据入Yang文件内核调用函数int len;try_module_get(THIS_MODULE);if(count>=STRINGLEN)len=STRINGLEN-1;elselen=count;copy_from_user(global_buffer,buffer,len);global_buffer=’\0′;module_put(THIODULE);return len;}int init_module(){/吵卖/初始化函数example_dir=proc_mkdir(“”,NULL);example_dir->owner=THIS_MODULE;Tang_file=create_proc_read_entry(“Tang”,0444,example_dir,proc_rea d_current,NULL);Tang_file->read_proc=proc_read_Tang;Tang_file->owner=THIS_MODULE;Yang_file=create_proc_entry(“Yang”,0666,example);strcpy(global_buffer,”Tang”);Yang_file->read_proc=proc_read_Yang;Yang_file->write_proc=proc_write_Yang;Yang_file->owner=THIS_MOUDLE;return 0;}void cleanup_module(){//卸载函数remove_proc_entry(“Yang”,example_dir);remove_proc_entry(“Tang”,example_dir);remove_proc_entry(“21”,NULL);}编写Makefile文件内容:obj-m := my_proc.o KERNELBUILD :=/lib/modules/$(shell uname -r)/build default: make -C $(KERNELBUILD) M=$(shell pwd) modules clean: rm -rf *.o *.ko *.mod.c .*.cmd *.markers *.order *.symvers .tmp_versions

linux 编译内核几个常见问题解决方法

之一次把自己编译的驱动模块加载进开发板,就出现问题,还好没花费多长时间,下面列举出辩戚现的问题及解决方案

1:出现inod: error inserting ‘hello.ko’: -1 Invalid module format

法一(网上的):是因为内核模块生成的环境与运行的环境不一致,用linux-2.6.27内核源代码生成的模块,可能就不能在linux-2.6.32.2内核的linux环境下加载,需要在linux-2.6.27内核的linux环境下加载。

a.执行 uname -r //查看内核版本

b.一般出错信息被记录在文件/var/log/messages中,执行下面命令看错误信息

# cat /var/log/messages |tail

若出现类似下面:

Jun 4 22:07:54 localhost kernel:hello: version magic ‘2.6.35.6-45.fc14.i686.PAE

‘ should be ‘2.6.35.13-92.fc14.i686.PAE’

则把 Makefile里的KDIR :=/lib/modules/2.6.35.6-45.fc14.i686.PAE/build1 改为

KDIR :=/lib/modules/2.6.35.13-92.fc14.i686.PAE/build1 //改成自己内核源码路径

(这里的build1是一个文件链接,链接到/usr/src/kernels/2.6.35.6-45.fc14.i686.PAE和13-92的)

然并卵,我的fedora 14 /usr/src/kernels下并没有2.6.35.13-92.fc14.i686.PAE,只有2.6.35.13-92.fc14.i686,虽然不知道两者有什么区别,但改成2.6.35.13-92.fc14.i686还是不行,照样这个问题,还好后来在看教学视频的到启发

法二:改的还是那个位置

KDIR :=/opt/FriendlyARM/linux-2.6.32.//把这里改成你编译生成kernel的那个路径

all:

$ (MAKE) -C $ (KDIR) M = $ (PWD) modules ARCH=arm CROSS_COMPILE=arm-linux- //加这句

2. hello: module license ‘unspecified’ taints kernel.

Disabling lock debugging due to kernel taint

方法:在模块程序中加入: MODULE_LICENSE(“GPL”);

3. rmmod: chdir(2.6.32.2-FriendlyARM): No such file or directory 错误解决

方法:lod 可查看模块信息

即无法删除对应的模块。

就是必须在/lib/modules下建立错误提示的对应的目录((2.6.32.2)即可。

必须创建/lib/modules/2.6.32.2这样一个空目录,否则不能卸载ko模块.

# rmmod nls_cp936

rmmod: chdir(/lib/modules): No such file or directory

但是这样倒是可以卸载nls_cp936,不过会一直有这样一个提示:

rmmod: module ‘nls_cp936’ not found

初步发现,原来这是编译kernel时使用make modules_install生成的一个目录,

但是经测试得知,rmmod: module ‘nls_cp936’ not found来自于busybox,并不是来自判渣kernel

1).创建/lib/modules/2.6.32.2空目录

2).使用如下源码生成rmmod命令,就掘灶悄可以没有任何提示的卸载ko模块了

#include

#include

#include

#include

#include

#include

int main(int argc, char *argv)

{

const char *modname = argv;

int ret = -1;

int maxtry = 10;

while (maxtry– > 0) {

ret = delete_module(modname, O_NONBLOCK | O_EXCL);//系统调用sys_delete_module

if (ret #include

#include

#include

#include /* Necessary because we use the proc fs */

#define procfs_name “proctest”

MODULE_LICENSE(“GPL”);

struct proc_dir_entry *Our_Proc_File;

int procfile_read(char *buffer,char **buffer_location,off_t offset, int buffer_length, int *eof, void *data)

{int ret;

ret = sprintf(buffer, “HelloWorld!\n”);

return ret;

}

int proc_init()

{Our_Proc_File = create_proc_entry(procfs_name, 0644, NULL);

if (Our_Proc_File == NULL) {

remove_proc_entry(procfs_name, NULL);

printk(KERN_ALERT “Error: Could not initialize /proc/%s\n”,procfs_name);

return -ENOMEM;}

Our_Proc_File->read_proc = procfile_read;//

//Our_Proc_File->owner = THIS_MODULE;

Our_Proc_File->mode = S_IFREG | S_IRUGO;

Our_Proc_File->uid = 0;

Our_Proc_File->gid = 0;

Our_Proc_File->size = 37;

printk(“/proc/%s created\n”, procfs_name);

return 0;

}

void proc_exit()

{remove_proc_entry(procfs_name, NULL);

printk(KERN_INFO “/proc/%s removed\n”, procfs_name);

}

module_init(proc_init);

module_exit(proc_exit);

view plain copy

ifneq ($(KERNELRELEASE),)

obj-m :=proc.o

else

KDIR :=/opt/FriendlyARM/linux-2.6.32.2

#KDIR :=/lib/modules/2.6.35.13-92.fc14.i686.PAE/build1

PWD :=$(shell pwd)

all:

$(MAKE) -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-

clean:

rm -f *.ko *.o *.mod.o *.mod.c *.symvers

endif

make后生成proc.ko,再在开发板上inod proc.ko即可

linux struct module的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于linux struct module,Linux 结构体模块简介,一文搞懂 , Linux内核—— 同步管理(下),在Linux内核模块中对空指针解引用,为什么内核不挂,linux 编译内核几个常见问题解决方法的信息别忘了在本站进行查找喔。


数据运维技术 » Linux 结构体模块简介 (linux struct module)