
在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内核—— 同步管理(下)
- 在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 编译内核几个常见问题解决方法的信息别忘了在本站进行查找喔。
香港服务器首选树叶云,2H2G首月10元开通。
树叶云(www.IDC.Net)提供简单好用,价格厚道的香港/美国云服务器和独立服务器。IDC+ISP+ICP资质。ARIN和APNIC会员。成熟技术团队15年行业经验。
文章来源网络,作者:运维,如若转载,请注明出处:https://shuyeidc.com/wp/185380.html<