Linux下/dev/mem和/dev/kmem及操作gpio实例

本文介绍了Linux下如何通过/dev/mem和/dev/kmem访问物理内存和内核内容,特别是针对GPIO的操作。/dev/mem用于访问物理IO设备,如显卡和GPIO,而/dev/kmem则用于查看内核变量。在Linux下操作GPIO通常需要编写驱动或利用mmap函数。文中提供了两种在用户空间操作GPIO的示例代码。

Linux下/dev/mem和/dev/kmem的区别:

/dev/mem: 物理内存的全镜像。可以用来访问物理内存。

/dev/kmem: kernel看到的虚拟内存的全镜像。可以用来访问kernel的内容。

作用:

前者用来访问物理IO设备,比如X用来访问显卡的物理内存,或嵌入式中访问GPIO。用法一般就是open,然后mmap,接着可以使用map之后的地址来访问物理内存。这其实就是实现用户空间驱动的一种方法。

后者一般可以用来查看kernel的变量,或者用作rootkit之类的。参考1和2描述了用来查看kernel变量这个问题。



 Linux 下操作gpio:

   对于在不支持虚拟内存的操作系统和根本就没有使用操作系统的系统里操作GPIO直接读写对应的GPIO寄存器就可以啦,但是在linux这样的操作系统下,内核层和应用层都是处于虚拟地址中,而GPIO的寄存器都是处于物理地址中,你必须编写一个操作GPIO的驱动,或者是使用一些变通的技巧来操作GPIO.

   目前我所知道的在linux下操作GPIO有两种方法:

1.  编写驱动,这当然要熟悉linux下驱动的编写方法和技巧,在驱动里都是虚拟地址,可以使用ioremap函数获得GPIO物理基地址指针,然后使用这个指针根据ioctl命令进行GPIO寄存器的读写,并把结果回送到应用层。这里提供一点程序片断供大家参考:

    int  init_module(void){

        printk(KERN_ALERT "ioctl load.\r\n");

    register_chrdev(254,"ioreg",&fops);

        stb_gpio = (STBX25XX_GPIO_REG *)ioremap(GPIO_BASE,GPIO_LEN);

    if(stb_gpio == NULL){

            printk(KERN_ALERT "can''t get io base.\r\n");

        return -1;

        }

    return 0;

}

int io_ioctl (struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg){

    unsigned long uGpio;

    printk(KERN_ALERT "io_ioctl cmd=%04x,arg=%04x.\r\n",cmd,(int)arg);

    switch(cmd){

        case SET_IO_DIR:{

             printk(KERN_ALERT "SET_IO_DIR\r\n");

             break;

        }

        case SET_IO_VALUE:{

             printk(KERN_ALERT "SET_IO_VALUE\r\n");

             break;

        }

        case GET_IO_VALUE:{

             printk(KERN_ALERT "GET_IO_VALUE\r\n");

             uGpio = stb_gpio->GPI;

             printk(KERN_ALERT "GPIO = %08x",(int)uGpio);

             copy_to_user((void *)arg,(const void *) &uGpio,sizeof(uGpio));

             break;

        }

        case GET_IO_DIR:{

             printk(KERN_ALERT "GET_IO_DIR\r\n");

             break;

        }

    }

    return 0;

}

 

2.  在应用层使用mmap函数在应用层获得GPIO物理基地址对应的虚拟地址指针(应用层中),让用户程序直接访问设备内存,然后使用这个指针来读写GPIO寄存器,这里提供一点程序片断供大家参考:

/dev/mem是物理内存的全映像,可以用来访问物理内存,一般用法是open("/dev/mem",O_RDWR|O_SYNC),然后mmap,接着就可以用mmap的地址来访问物理内存,这实际上就是实现用户空间驱动的一种方法。

/dev/kmem:内核看到的虚拟内存的全映像,可以用来访问kernel的内容。

 

内核空间用1G虚拟地址,用户空间用3G虚拟地址
所以ioremap当然不能分出1G地址供你用(ioreamp的空间大小是有限制的)
一个物理地址,内核调用 ioremap得到一个1G内的虚拟地址,用来操作物理内存
             应用层调用 mmap 得到一个3G内的虚拟地址,用来操作物理内存


char dev_name[] = "/dev/mem";

        GPIO_REGISTER  *gpio_base;

        fd  =  open(dev_name,O_RDWR);

        if(fd<0){

               printf("open %s is error\n",dev_name);

               return -1 ;

        }

        gpio_base = (GPIO_REGISTER *)mmap( 0, 0x32, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0x40060000 );

        if(gpio_base == NULL){

                printf("gpio base mmap is error\n");

                close(fd);

                return -1;

        }

        gpio_base->or  = (gpio_base->or & 0x7fffffff);

完整的程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <string.h>
#include <time.h>

#define TIME_OUT    5      /* the timeout time, in seconds */
/**
 * Define behaviour's when the button is pressed enough time.
 */
void longtu_timeout(){
    printf("****  Pressed 5 seconds, Call RECOVERY! ****\n");
    //system("/sbin/recover_longtu.sh");
    //system("/sbin/reboot");
}

/*
 * The main function.
 */
int main(int argc, char *argv[])
{
    int mfd;
    unsigned int val=0, last_val;
    void *base;
    char *sys_pinstaterd;
    time_t t_now, t_old;
    int flag_issued = 0;
#if 0
    // uncomment these to make the program a daemon.

    pid_t pid;
        int i;
        if ( (pid=fork())<0)
                return -1;
        else if (pid!=0)
                exit(0);
        setsid();
        chdir("/");
        umask(0);
        for (i=0;i<256;i++)
                close(i);
#endif

    // open the memery mapped file.
    mfd=open("/dev/mem", O_RDWR);
    if (mfd < 0){
        printf("Cannot open /dev/mem.\n");
        exit(-1);
    }

    // Initialize the map
    base = mmap( NULL, 0x130,  PROT_READ | PROT_WRITE, MAP_SHARED, mfd, 0x1fe00000);
    if ( base < 0){
        exit(-1);
    }

    // Got the pointer to SYS_PINSTATERD register of GS32I CPU.
   
sys_pinstaterd = base + 0x011c;

    // init the temperay variables
    t_now=t_old=time(NULL);
    last_val = 0;
    while(1)
    {
        // Get status of GPIO7 pin.
        val = *( (volatile unsigned int*)sys_pinstaterd );
        val = (val&0x4) ? 1:0;
        printf("\tgpio 7 stat=%x.\n", val);
        if (val){
            // the button is pressed down !!
            if ( last_val==0 ){
                // starting time of press, log the time
                t_old = time(NULL);
                last_val=1;
                printf("Button Down\n");
            }else {
                // already pressed down! let's count the time!
                t_now = time(NULL);
                if (t_now-t_old>=TIME_OUT && flag_issued==0){
                    // Pressed LONG ENOUGH, issue the handler script!!
                    flag_issued = 1;
                    longtu_timeout();
                }
            }
        }else{
            // No button pressed.
            if (flag_issued){
                flag_issued = 0;
                printf("Button UP.\n");
            }
        }
       
        last_val = val;
        usleep(100);
    }

    munmap(base, 0x000);
    close(mfd);   
    return 0;
}


<think>好的,我现在需要解决用户在Vue中组件未正确更新的问题,尤其是在点击“复制”按钮后弹窗内容没有及时显示,而是延迟加载的情况。用户提到希望通过修改key值或其他方法强制重渲染子组件。让我先理清楚可能的原因和解决方案。 首先,根据用户提供的引用内容,特别是引用[1]中的例子,他们之前通过设置key为时间戳来强制子组件刷新。这似乎是一个有效的方法,因为Vue的响应式系统在key改变时会重新创建组件实例。不过用户现在遇到了类似的问题,可能需要同样的方法,但需要确认是否正确应用。 然后,引用[2]提到Vue的异步更新队列,可能导致状态变化不会立即触发DOM更新。如果在同一个事件循环中多次修改状态,Vue可能只会处理最后一次的变更。这可能意味着用户在点击“复制”按钮后,某些状态没有被及时更新,导致弹窗内容没有正确显示。 接下来,我需要考虑可能的解决方案。除了修改key值之外,还有其他方法可以强制组件重新渲染,比如使用this.$forceUpdate()或者改变组件的某个prop。但修改key值通常是推荐的方法,因为它更直接地触发组件的销毁和重新创建,确保状态完全重置。 用户的问题具体是在点击“复制”按钮时弹窗内容没有正确显示。可能的情况是,弹窗组件依赖于某些数据,而这些数据在按钮点击后没有及时触发更新。例如,可能在打开弹窗时,数据还未被正确赋值,或者由于Vue的异步更新机制,DOM没有及时渲染。 我需要检查用户可能的代码结构。假设弹窗组件是一个子组件,当点击“复制”按钮时,父组件会修改某些数据,然后显示弹窗。如果弹窗的内容依赖于这些数据,但数据的变化没有被及时检测到,或者弹窗组件没有重新渲染,就会导致内容显示不正确。 根据引用[1]中的例子,用户之前通过给子组件设置:key="timer",并在打开弹窗时更新timer的值,从而强制子组件重新渲染。同样的方法可能适用于当前的问题。即在点击“复制”按钮时,更新key的值,确保子组件每次打开时都重新创建,而不是复用之前的实例。 另外,需要注意在关闭弹窗时,是否正确地重置了相关的数据和key的值。例如,在弹窗关闭事件中,将timer置为null或新的时间戳,以确保下次打开时key值不同,触发重新渲染。 还有可能涉及到Vue的响应式数据更新问题。如果用户的数据结构较为复杂,或者使用了嵌套对象,Vue可能无法检测到数据的变化。这时候可能需要使用Vue.set或this.$set来确保属性的响应式更新。 此外,引用[3]中提到的主进程和渲染进程之间的通信机制,可能不直接相关,但如果用户的应用涉及Electron或其他类似框架,可能需要考虑跨进程通信对状态更新的影响,不过用户当前的问题集中在Vue组件本身,暂时可以排除这方面的因素。 总结可能的解决步骤: 1. 在弹窗子组件上使用:key属性,并将其绑定到一个响应式的变量(如timer)。 2. 在点击“复制”按钮时,更新timer的值,通常使用时间戳,确保每次点击时key值不同。 3. 在弹窗关闭时,重置timer或其他相关状态,确保下次打开时能再次触发更新。 4. 检查数据更新的时机,确保在打开弹窗前数据已经正确赋值,可能需要使用this.$nextTick来确保DOM更新。 需要验证用户是否正确地应用了这些步骤。例如,是否在点击按钮时更新了key的值,是否在关闭时重置,是否有其他因素导致数据未及时更新。 另外,如果用户的数据更新存在异步操作,比如从API获取数据,可能需要确保在数据加载完成后再打开弹窗,或者使用v-if指令控制弹窗的显示,结合key的变化来强制重渲染。 最后,还要考虑是否有其他优化手段,比如使用计算属性或侦听器来响应数据变化,但关键点还是通过修改key来强制组件重新渲染,确保内容正确显示。</think>在Vue中强制子组件重渲染的解决方案如下: ### 一、通过修改key值强制重渲染 1. **绑定动态key属性** 在弹窗子组件上绑定`:key`属性,值为响应式变量: ```vue <codemirror-editor ref="editor" :key="componentKey" v-model="content" ></codemirror-editor> ``` 2. **点击操作时更新key值** 在"复制"按钮事件中修改key值触发重建: ```javascript handleCopy() { this.componentKey = new Date().getTime(); // 时间戳更新key this.showModal = true; // 显示弹窗 } ``` 3. **关闭弹窗时重置状态** 弹窗关闭时清空关键数据: ```javascript closeModal() { this.componentKey = null; // 可选重置操作 this.content = ''; this.showModal = false; } ``` ### 二、其他辅助优化方法 1. **使用$nextTick确保DOM更新** 在异步操作后强制更新: ```javascript this.$nextTick(() => { this.componentKey = new Date().getTime(); }); ``` 2. **通过v-if控制生命周期** 结合`v-if`与`key`双重保障: ```vue <codemirror-editor v-if="showModal" :key="componentKey" ></codemirror-editor> ``` 3. **使用Vue.set处理深层次对象** 当数据层级较深时: ```javascript Vue.set(this.dataObj, 'nestedProp', newValue); ``` ### 三、原理说明 Vue通过虚拟DOM的diff算法判断组件是否需要更新。当`key`值改变时,会销毁旧组件实例并创建新实例,达到强制重渲染的目的[^1]。这种方法比`$forceUpdate()`更彻底,后者仅触发重新渲染但不重置组件状态[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值