THIS MODULE/struct module【需完善...】

转载地址:

[1] http://blog.youkuaiyun.com/a954423389/article/details/6101369

正文:

结构体struct module在内核中代表一个内核模块,通过insmod(实际执行init_module系统调用)把自己编写的内核模块插入内核时,模块便与一个 struct module结构体相关联,并成为内核的一部分。下面是结构体struct module的完整定义,接下来会逐个解释:
struct module
    {
        enum module_state state;
        struct list_head list;
        char name[MODULE_NAME_LEN];

        struct module_kobject mkobj;
        struct module_param_attrs *param_attrs;
        const char *version;
        const char *srcversion;

        const struct kernel_symbol *syms;
        unsigned int num_syms;
        const unsigned long *crcs;

        const struct kernel_symbol *gpl_syms;
        unsigned int num_gpl_syms;
        const unsigned long *gpl_crcs;

        unsigned int num_exentries;
        const struct exception_table_entry *extable;

        int (*init)(void);
        void *module_init;
        void *module_core;
        unsigned long init_size, core_size;
        unsigned long init_text_size, core_text_size;
        struct mod_arch_specific arch;
        int unsafe;
        int license_gplok;

#ifdef CONFIG_MODULE_UNLOAD
        struct module_ref ref[NR_CPUS];
        struct list_head modules_which_use_me;
        struct task_struct *waiter;
        void (*exit)(void);
#endif

#ifdef CONFIG_KALLSYMS
        Elf_Sym *symtab;
        unsigned long num_symtab;
        char *strtab;
        struct module_sect_attrs *sect_attrs;
#endif
        void *percpu;
        char *args;
    };
我们插入一个内核模块,一般会使用工具insmod,该工具实际上调用了系统调用init_module,在该系统调用函数中,首先调用 load_module,把用户空间传入的整个内核模块文件创建成一个内核模块,返回一个struct module结构体。内核中便以这个结构体代表这个内核模块。
state是模块当前的状态。它是一个枚举型变量,可取的值 为:MODULE_STATE_LIVE,MODULE_STATE_COMING,MODULE_STATE_GOING。分别表示模块当前正常使用中 (存活状态),模块当前正在被加载,模块当前正在被卸载。load_module函数中完成模块的部分创建工作后,把状态置为 MODULE_STATE_COMING,sys_init_module函数中完成模块的全部初始化工作后(包括把模块加入全局的模块列表,调用模块本 身的初始化函数),把模块状态置为MODULE_STATE_LIVE,最后,使用rmmod工具卸载模块时,会调用系统调用 delete_module,会把模块的状态置为MODULE_STATE_GOING。这是模块内部维护的一个状态。
list是作为一个列表的成员,所有的内核模块都被维护在一个全局链表中,链表头是一个全局变量struct module *modules。任何一个新创建的模块,都会被加入到这个链表的头部,通过modules->next即可引用到。
name是模块的名字,一般会拿模块文件的文件名作为模块名。它是这个模块的一个标识。
另外,还要介绍一下宏THIS_MODULE,它的定义如下是#define THIS_MODULE (&__this_module),__this_module是一个struct module变量,代表当前模块,跟current有几分相似。可以通过THIS_MODULE宏来引用模块的struct module结构,试试下面的模块:
#include <linux/module.h>

    MODULE_LICENSE("Dual BSD/GPL");

    static int hello_init(void)
    {
        unsigned int cpu = get_cpu();
        struct module *mod;
        printk(KERN_ALERT "this module: %p==%p/n", &__this_module, THIS_MODULE );
        printk(KERN_ALERT "module state: %d/n", THIS_MODULE->state );
        printk(KERN_ALERT "module name: %s/n", THIS_MODULE->name );
        list_for_each_entry(mod, *(&THIS_MODULE->list.prev), list )
                printk(KERN_ALERT "module name: %s/n", mod->name );
        return 0;
    }

    static void hello_exit(void)
    {
        printk(KERN_ALERT "module state: %d/n", THIS_MODULE->state );
    }

    module_init(hello_init);
    module_exit(hello_exit);

//以下摘自usb那些事:
owner是一个struct module *类型的结构体指针,现在告诉你的是每个struct module结构体在内核里都代表了一个内核模块,就像十七大里的每个代表都代表了一批人,至于代表了什么人,选他们的人才知道,同样,每个struct module结构体代表了什么模块,对它进行初始化的模块才知道。当然,初始化这个结构不是写驱动的人该做的事,是在刚才略过的那个从insmod或 modprobe到你驱动的xxx_init函数的曲折过程中做的事。insmod命令执行后,会调用kernel/module.c里的一个系统调用 sys_init_module,它会调用load_module函数,将用户空间传入的整个内核模块文件创建成一个内核模块,并返回一个struct module结构体,从此,内核中便以这个结构体代表这个内核模块。
再看看THIS_MODULE宏是什么意思,它在include/linux/module.h里的定义是
#define THIS_MODULE (&__this_module)
是 一个struct module变量,代表当前模块,与那个著名的current有几分相似,可以通过THIS_MODULE宏来引用模块的struct module结构,比如使用THIS_MODULE->state可以获得当前模块的状态。现在你应该明白为啥在那个岁月里,你需要毫不犹豫毫不迟 疑的将struct usb_driver结构里的owner设置为THIS_MODULE了吧,这个owner指针指向的就是你的模块自己。那现在owner咋就说没就没了 那?这个说来可就话长了,咱就长话短说吧。不知道那个时候你有没有忘记过初始化owner,反正是很多人都会忘记,大家都把注意力集中到probe、 disconnect等等需要动脑子的角色上面了,这个不需要动脑子,只需要花个几秒钟指定一下的owner反倒常常被忽视,这个就是容易得到的往往不去 珍惜,不容易得到的往往日日思量着去争取。于是在2006年的春节前夕,在咱们都无心工作无心学习等着过春节的时候,Greg坚守一线,去掉了 owner,于是千千万万个写usb驱动的人再也不用去时刻谨记初始化owner了。咱们是不用设置owner了,可core里不能不设置,struct usb_driver结构里不是没有owner了么,可它里面嵌的那个struct device_driver结构里还有啊,设置了它就可以了。于是Greg同时又增加了usb_register_driver()这么一 层,usb_register()可以通过将参数指定为THIS_MODULE去调用它,所有的事情都挪到它里面去做。反正usb_register() 也是内联的,并不会增加调用的开销。
<think>根据用户的问题,用户在使用鸿蒙开发时遇到了启动弹窗报错"Error message: is not callable"。结合引用内容,问题可能出现在使用@Link双向绑定时,特别是在将弹窗封装到单独方法中的情况下。引用[1]提到,当自定义弹窗封装到方法中时,使用@Link双向绑定会报错;而不放在单独方法里则可以正常使用。引用[2]则提到了使用promptAction.openCustomDialog的替代方案。 用户的问题核心在于如何解决这个报错。根据引用内容,有两个可能的解决方案: 1. 避免将弹窗封装到单独的方法中,直接在要的地方使用@Link双向绑定。 2. 使用promptAction.openCustomDialog方法来打开弹窗,避免this指向问题。 接下来,我将逐步分析问题并提供解决方案: 1. **错误原因分析**:报错"is not callable"通常表示尝试调用一个不可调用的对象(如未定义的函数)。在引用[1]的场景中,当弹窗被封装到方法showYearDialog()里时,RadioDialog中的@Link双向绑定可能因为this指向改变而无法正确访问组件的属性(如this.year),导致绑定失败。 2. **解决方案一(推荐)**:避免在方法内创建弹窗,改为在组件的build()或声明时直接创建。这样可以确保@Link绑定的上下文正确。 ```typescript // 正确示例:不在单独方法内创建弹窗 private controller: CustomDialogController = new CustomDialogController({ builder: RadioDialog({ lastSelect: this.year, // 直接绑定到组件的year属性 // ...其他参数 }), // ...控制器配置 }); ``` 3. **解决方案二**:使用promptAction.openCustomDialog(如引用[2]所示)。这种方法可以避免this指向问题,因为它不要在方法内部维护controller实例。 ```typescript import promptAction from '@ohos.promptAction'; showDialog() { promptAction.openCustomDialog({ builder: RadioDialog({ lastSelect: this.year, // ...其他参数 }), // ...其他配置 }).then(() => { // 弹窗关闭后的处理 }); } ``` 4. **注意事项**: - 如果使用方案一,确保CustomDialogController的实例在组件的顶层作用域中创建(而非在方法内部),以保持this上下文。 - 如果使用方案二,注意promptAction.openCustomDialog是系统提供的API,可能更稳定,但确认是否支持完全自定义的弹窗内容。 5. **代码修正示例**(基于用户提供的showYearDialog方法): ```typescript // 错误示例(在方法内创建controller): showYearDialog() { this.controller = new CustomDialogController({ ... }); // 这里this可能指向错误 // ... } // 修正为方案一(在组件类中直接初始化controller): private controller: CustomDialogController = new CustomDialogController({ builder: RadioDialog({ lastSelect: this.year, defaultSelect: '全部年份', list: this.filBean.yearList, click: { onClick: (item) => { if (item.desc) { this.year = item.desc === '全部年份' ? '' : item.desc; // 直接修改组件的year // ...其他逻辑 } this.controller.close(); } } }), offset: { dx: 0, dy: this.postionY }, // ...其他配置 }); // 然后在点击事件中直接调用this.controller.open() ``` 6. **为什么这样修改**: - 将controller的初始化移到组件类的作用域中,确保RadioDialog内的this指向组件实例,从而@Link绑定的year属性能够正确响应更新。 - 避免了在方法内部重新创建controller导致的上下文丢失问题。 如果用户仍遇到问题,可能要检查RadioDialog组件的实现,确保它正确使用了@Link装饰器(如`@Link lastSelect: string`)并在父组件中通过`$`符号传递(如`lastSelect: $year`)。 最后,生成相关问题供用户进一步探索。</think>### 鸿蒙启动弹窗报错 "Error message: is not callable" 解决方案 #### 🔍 问题分析 根据引用内容[^1],报错 `Error message: is not callable` 通常出现在以下场景: 1. **`@Link` 双向绑定位置错误**:当自定义弹窗封装在独立方法(如 `showYearDialog()`)中时,`@Link` 绑定的属性无法正确访问组件上下文 2. **`this` 指向丢失**:方法内部的 `this` 可能指向弹窗控制器而非组件实例,导致属性调用失败 3. **生命周期错位**:弹窗创建时机导致数据绑定失效 #### ✅ 解决方案(二选一) ##### 方案一:避免封装到独立方法(推荐) > 直接初始化 `CustomDialogController`,不封装到方法内[^1] ```typescript // 在组件顶部声明控制器 private controller: CustomDialogController = new CustomDialogController({ builder: RadioDialog({ lastSelect: $year, // 使用$符号保持双向绑定 defaultSelect: '全部年份', list: $filBean.yearList, // 绑定到组件属性 click: { onClick: (item) => { if (item.desc) { this.year = item.desc === '全部年份' ? '' : item.desc; this.reqSearch(); } this.controller.close(); } } }), offset: { dx: 0, dy: this.postionY }, maskColor: Color.Transparent }); // 直接调用open openDialog() { this.controller.open(); // 直接使用已初始化的控制器 } ``` ##### 方案二:使用 `promptAction.openCustomDialog` API > 通过系统API规避 `this` 指向问题[^2] ```typescript import promptAction from '@ohos.promptAction'; showYearDialog() { promptAction.openCustomDialog({ builder: { builder: () => { return RadioDialog({ lastSelect: this.year, defaultSelect: '全部年份', list: this.filBean.yearList }) } }, extra: { // 通过回调传递数据 onItemSelect: (item) => { this.year = item.desc; this.reqSearch(); } } }); } ``` #### ⚠️ 关键注意事项 1. **`@Link` 使用规范**: ```typescript @Component struct RadioDialog { @Link lastSelect: string; // 父组件用$year传递 } ``` 2. **`this` 指向修复**: ```typescript // 方法内使用箭头函数保持this指向 onClick: (item) => { this.handleSelect(item); // ✅ this指向组件实例 } ``` 3. **控制器生命周期**: ```typescript aboutToDisappear() { this.controller?.close(); // 组件销毁时关闭弹窗 } ``` #### 💡 调试建议 若仍报错,检查以下配置: ```typescript // 确保在自定义组件声明中启用双向绑定 @CustomDialog struct RadioDialog { controller: CustomDialogController } ``` 通过上述方案可解决 `is not callable` 错误,本质是保持 `@Link` 绑定在正确的组件上下文中执行[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值