zoukankan      html  css  js  c++  java
  • linux驱动面试题整理

    1、字符型驱动设备你是怎么创建设备文件的,就是/dev/下面的设备文件,供上层应用程序打开使用的文件?

    答:mknod命令结合设备的主设备号和次设备号,可创建一个设备文件。

    评:这只是其中一种方式,也叫手动创建设备文件。还有UDEV/MDEV自动创建设备文件的方式,UDEV/MDEV是运行在用户态的程序,可以动态管理设备文件,包括创建和删除设备文件,运行在用户态意味着系统要运行之后。那么在系统启动期间还有devfs创建了设备文件。一共有三种方式可以创建设备文件。

     

    2、写一个中断服务需要注意哪些?如果中断产生之后要做比较多的事情你是怎么做的?
    答:中断处理例程应该尽量短,把能放在后半段(tasklet,等待队列等)的任务尽量放在后半段。
    评:写一个中断服务程序要注意快进快出,在中断服务程序里面尽量快速采集信息,包括硬件信息,然后推出中断,要做其它事情可以使用工作队列或者tasklet方式。也就是中断上半部和下半部。
    第二:中断服务程序中不能有阻塞操作。为什么?大家可以讨论。      

    第三:中断服务程序注意返回值,要用操作系统定义的宏做为返回值,而不是自己定义的OK,FAIL之类的。

     

    3、自旋锁和信号量在互斥使用时需要注意哪些?在中断服务程序里面的互斥是使用自旋锁还是信号量?还是两者都能用?为什么?
    答:使用自旋锁的进程不能睡眠,使用信号量的进程可以睡眠。中断服务例程中的互斥使用的是自旋锁,原因是在中断处理例程中,硬中断是关闭的,这样会丢失可能到来的中断。

    4、原子操作你怎么理解?为了实现一个互斥,自己定义一个变量作为标记来作为一个资源只有一个使用者行不行?
    答:原子操作指的是无法被打断的操作。我没懂第二句是什么意思,自己定义一个变量怎么可能标记资源的使用情况?其他进程又看不见这个变量
    评:第二句话的意思是:
      定义一个变量,比如 int flag =0;
       if(flag == 0)
      {
           flag = 1;
           操作临界区;
          flag = 0;

       }这样可否?

     

    5、insmod 一个驱动模块,会执行模块中的哪个函数?rmmod呢?这两个函数在设计上要注意哪些?遇到过卸载驱动出现异常没?是什么问题引起的?
    答:insmod调用init函数,rmmod调用exit函数。这两个函数在设计时要注意什么?卸载模块时曾出现卸载失败的情形,原因是存在进程正在使用模块,检查代码后发现产生了死锁的问题。

    评:要注意在init函数中申请的资源在exit函数中要释放,包括存储,ioremap,定时器,工作队列等等。也就是一个模块注册进内核,退出内核时要清理所带来的影响,带走一切不留下一点痕迹。

     

    6、在驱动调试过程中遇到过oops没?你是怎么处理的?

     

    7、ioctl和unlock_ioctl有什么区别?

     

    8、驱动中操作物理绝对地址为什么要先ioremap?
    答:因为内核没有办法直接访问物理内存地址,必须先通过ioremap获得对应的虚拟地址。

    9、设备驱动模型三个重要成员是?platfoem总线的匹配规则是?在具体应用上要不要先注册驱动再注册设备?有先后顺序没?

     

    10、linux中内核空间及用户空间的区别?用户空间与内核通信方式有哪些?


    11、linux中内存划分及如何使用?虚拟地址及物理地址的概念及彼此之间的转化,高端内存概念?


    12、linux中中断的实现机制,tasklet与workqueue的区别及底层实现区别?为什么要区分上半部和下半部?


    13、linux中断的响应执行流程?中断的申请及何时执行(何时执行中断处理函数)?


    14、linux中的同步机制?spinlock与信号量的区别?


    15、linux中RCU原理?


    16、linux中软中断的实现原理?


    17、linux系统实现原子操作有哪些方法? 


    18、MIPS Cpu中空间地址是怎么划分的?如在uboot中如何操作设备的特定的寄存器?

     

    19、linux中系统调用过程?如:应用程序中read()在linux中执行过程即从用户空间到内核空间?


    20、linux内核的启动过程(源代码级)?


    21、linux调度原理?


    22、linux网络子系统的认识?

     

    23、linux内核里面,内存申请有哪几个函数,各自的区别?
    Kmalloc()   __get_free_page()  mempool_create()  
     
    24. IRQ和FIQ有什么区别,在CPU里面是是怎么做的?
     
    25. 中断的上半部分和下半部分的问题:讲下分成上半部分和下半部分的原因,为何要分?讲下如何实现?

    上半部分执行与硬件相关的处理要求快, 而有些驱动在中断处理程序中又需要完成大量工作,这构成矛盾,所以Linux有所谓的bottom half机制,中断处理程序中所有不要求立即完成的,在开中断的环境下,由底半程序随后完成.
    Linux的底半处理实际上是建立在内核的软中断机制上的.
    Linux 的底半 机制主要有Tasklet 和 work queue 以及 softirq ( 2.4内核则有BH , Task queue , softirq , tasklet 没有work queue),其实底半可以理解成一种工作的延迟。所以实际使用时跟timer机制基本上一个意思。

    26. 内核函数mmap的实现原理,机制?
    mmap函数实现把一个文件映射到一个内存区域,从而我们可以像读写内存一样读写文件,他比单纯调用read/write也要快上许多。在某些时候我们可以把内存的内容拷贝到一个文件中实现内存备份,当然,也可以把文件的内容映射到内存来恢复某些服务。另外,mmap实现共享内存也是其主要应用之一,mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。
     
    27. 驱动里面为什么要有并发、互斥的控制?如何实现?讲个例子?
    并发(concurrency)指的是多个执行单元同时、并行被执行,而并发的执行单元对 共 享资源(硬件资源和软件上的全局变量、静态变量等)的访问则很容易导致竞态(race conditions) 。 解决竞态问题的途径是保证对共享资源的互斥访问, 所谓互斥访问就是指一个执行单 元 在访问共享资源的时候,其他的执行单元都被禁止访问。 访问共享资源的代码区域被称为临界区, 临界区需要以某种互斥机 制加以保护, 中断屏蔽, 原子操作,自旋锁,和信号量都是 linux 设备驱动中可采用的互斥途径。

     
    28. spinlock自旋锁是如何实现的?

    自旋锁在同一时刻只能被最多一个内核任务持有,所以一个时刻只有一个线程允许存在于临界区中。这点可以应用在多处理机器、或运行在单处理器上的抢占式内核中需要的锁定服务。
      这里也介绍下信号量的概念,因为它的用法和自旋锁有相似的地方。linux中的信号量是一种睡眠锁。如果有一个任务试图获得一个已被持有的信号量时,信号量会将其推入等待队列,然后让其睡眠。这时处理器获得自由去执行其它代码。当持有信号量的进程将信号量释放后,在等待队列中的一个任务将被唤醒,从而便可以获得这个信号量。
     
    29. 任务调度的机制?
     
    30. 嵌入式linux和wince操作系统的特点和特性?
     
    31. 嵌入式linux中tty设备驱动的体系结构?
     
    32. 嵌入式设备,为加快启动速度,可以做哪些方面的优化?
     
    33. USB设备的枚举过程?

    (1) Get Device Descriptor。主机的第一个命令要求得到设备描述符,此SETUP 包为8 个字节数据(80,06,00,01,00,00,40,00),发向地址0,端口0。“40”表示返回数据长度最大为40H 个字节。实际上,只返回一个包,即数组DEV_DESC[ ]中的前8 个字节,用于说明设备的描述符的真实长度和设备的类型。
    (2) Set Address。接着是设置设备地址处理事件,主机发送一个含有指定地址的数据包(00,05,02,00,00,00,00,00),在主机只有一个USB 设备的时候,这个地址一般会是2,最大地址127,USB 协议中可以连接127 个设备。设置地址事件处理结束后,设备进入地址状态,主机以后会在新的指定地址处访问设备。
    (3) Get Device Descriptor。主机再次发送请求得到设备描述符的数据包(80,06,00,01,00,00,12,00),与上次不同的是,要求的数据的长度是实际的数据长度,同时是发送到Set Address命令所设置的地址。
    (4) 读取全部Configuration Descriptor。接着主机要求得到设备全部的配置描述符、接口描述符和节点描述符(80,06,00,02,00,00,40,00),由于主机不知道设备描述符的真实长度,因此它要求得到64个字节。
    (5) Set Interface,主机发送数据包(01,0B,00,00,00,00,00,00),设置接口值为0。
    (6) Set Conifguration,确定USB设备工作在哪一个配置下。对于U盘设备来说,一般只有1个配置值,其值为01。主机发送数据包(00,09,01,00,00,00,00,00)。
    (7) 如果以上步骤都正确,主机将找到新设备,并且配置成功,该设备可以正常使用,可以进行后续的U盘枚举过程了。
    (8) 用busHound观察计算机对于U盘的枚举过程,发现上述步骤后还有一个GetMaxLun的操作,但是实际上对于U盘来说忽略该步骤也没有问题。
     
    34. PSRAM、SDRAM、DDR、DDR2的时序特性?

    35.什么是GPIO?
    general purpose input/output  
    GPIO是相对于芯片本身而言的,如某个管脚是芯片的GPIO脚,则该脚可作为输入或输出高或低电平使用,当然某个脚具有复用的功能,即可做GPIO也可做其他用途。 
    也就是说你可以把这些引脚拿来用作任何一般用途的输入输出,例如用一根引脚连到led的一极来控制它的亮灭,也可以用一根(一些)引脚连到一个传感器上以获得该传感器的状态,这给cpu提供了一个方便的控制周边设备的途经。如果没有足够多的gpio管脚,在控制一些外围设备时就会力有不逮,这时可采取的方案是使用CPLD来帮助管理。


    36.触摸屏的硬件原理?
    触摸屏的主要三大种类是:电阻技术触摸屏、 表面声波技术触摸屏、 电容技术触摸屏。
    电阻触摸屏的主要部分是一块与显示器表面非常配合的电阻薄膜屏, 这是一种多层的复合薄膜,它以一层玻璃或硬塑料平板作为基层,表面图有一层透明氧化金属 (ITO氧化铟,透明的导电电阻) 导电层,上面在盖有一层外表面硬化处理、光滑防擦的塑料层 、它的内表面也涂有一层ITO涂层 、在他们之间有许多细小的(小于1/1000英寸)的透明隔离点把两层导电层隔开绝缘 。当手指触摸屏幕时,两层导电层在触摸点位置就有了接触,控制器侦测到这一接触并计算出(X,Y )的位置,再根据模拟鼠标的方式运作。这就是电阻技术触摸屏的最基本的原理。
    表面声波技术是利用声波在物体的表面进行传输,当有物体触摸到表面时,阻碍声波的传输,换能器侦测到这个变化,反映给计算机,进而进行鼠标的模拟。
    电容技术触摸屏利用人体的电流感应进行工作 。用户触摸屏幕时 ,由于人体电场,用户和触摸屏表面形成以一个耦合电容, 对于高频电流来说,电容是直接导体,于是手指从接触点吸走一个很小的电流

    37.在Linux C中,ls这个命令是怎么被执行的?
    使用fork创建一个进程或exec函数族覆盖原进程。


    38.在一个只有128M内存并且没有交换分区的机器上,说说下面两个程序的运行结果
    1
    #define MEMSIZE 1024*1024
    int count = 0;
    void *p = NULL;
    while(1) {
      p = (void *)malloc(MEMSIZE);
      if (!p) break;
      printf("Current allocation %d MB ", ++count);
    }
    2
    while(1) {
      p = (void *)malloc(MEMSIZE);
      if (!p) break;
      memset(p, 1, MEMSIZE);
      printf("Current allocation %d MB ", ++count);
    }
    第一道程序分配内存但没有填充,编译器可能会把内存分配优化掉,程序死循环;第二道,程序分配内存并进行填充,系统会一直分配内存,直到内存不足,退出循环。

    39.请定义一个宏,比较两个数a、b的大小,不能使用大于、小于、if语句
    搞的比较复杂。主要思想就是a-b的值的最高位是否为0;但是又得考虑整数溢出的问题,所以很复杂。不知道哪位大侠有更好的办法,指点指点。
    #include<stdio.h>
    #define ZHENG(i)((i>> 31)== 0)
    #define FU(i)((i>> 31)!= 0)
    #define COMPARE(a,b)((ZHENG(a)&& FU(b))||(((ZHENG(a)&& ZHENG(b))||(FU(a)&&FU(b)))&&((((a)-(b))>> 31)== 0)))
    void main()
    {
        int a = 0x80000001;
        int b = 0x6FFFFFFF;
        if(COMPARE(a,b))
        {
            printf("a >= b ");
        }
        else
        {
            printf("a < b ");
        }
    }
     
    40、LINUX下的Socket套接字和Windows下的WinSock有什么共同点?请从C/C++语言开发的角度描述,至少说出两点共同点。
    参考答案:
    第1题,答中一个得5分,答出其它正确答案的,也得5分。
    a)都基于TCP/IP协议,都提供了面向连接的TCP SOCK和无连接的UDP SOCK。
    b)都是一个sock结构体。
    c)都是使用sock文件句柄进行访问。
    d)都具有缓冲机制。


    41、请编写一个标准Shell脚本testd,实现如下功能:
    A、在Linux操作系统启动的时候,自动加载/mnt/test/test程序。
    B、当test异常退出之后,自动重新启动。
    C、当test程序重启次数超过100次,自动复位操作系统。
    假设你所拥有的资源:
    A、目标机器是一台具有标准shell的嵌入式计算机,CPU为ARM7 56MB,内存16MB,软件环境基于Linux2.6.11和BusyBox1.2构建。
    B、当前已有11个用户进程在运行,占用了大部分的CPU时间和内存,你可使用的内存只有2MB左右,CPU时间由系统分派。
    本题是考查LINUX和嵌入式编程功底的,写出程序来的不少,但是95%以上的人竟无视我假设的资源,不知道在重启test程序的时候需要加上一个适当的掩饰时间,以便资源紧张的操作系统有时间回收资源。85%的人不知道写完testd之后,要在init里边加载这个脚本,才能实现启动时自动加载的功能。
    参考答案:
    ########################################
    #testd is a daemon script to start an watch the program test
    ########################################
    #!/bin/sh
    #load *.so that may need
    if [ -r /sbin/ldconfig ]; then
    ldconfig
    fi
    #add the libs PATH that may need
    export LD_LIBRARY_PATH="/lib"
    #count is the counter of test started times
    count=0
    #main loop
    while [ 1 ] ;do
    #add execute property for /mnt/test/test
    chmod +x /mnt/test/test
    #start test
    /mnt/test/test
    #the running times counter
    let count=count+1
    echo "test running times is $count"
    #Is test running too many times?
    if [ "$count" -gt 100 ]; then
    echo "Will reboot because of test running too many times"
    reboot
    fi
    #wait for test stoping...

    sleep 3
    done
    #########################################
     

    42.你平常是怎么用C写嵌入式系统的死循环的?

     

    43.写一条命令,实现在dir以及其子目录下找出所有包含“hello world”字符串的文件

    44.下面的两段程序中,循环能否执行?为什么?

    A: unsigned short i; unsigned short index = 0; for(i = 0; i <index-1; i++){     printf(“a ”); }
    B: unsigned short i; unsigned long index = 0; for(i = 0; i <index-1; i++){      printf(“b ”); }  
                    
    45.一个计划跑LINUX系统的ARM系统把bootloader烧录进去后,上电后串口上没有任何输出,硬件和软件各应该去检查什么?   
    提示: 1.跑LINUX的系统一般都需要外扩DRAM,一般的系统也经常有NOR或NAND FLASH          
     
    476列举最少3种你所知道的嵌入式的体系结构,并请说明什么是ARM体系结构。
     
    47.请简述下面这段代码的功能

    mov r12, #0x0        

    ldr r13, =0x30100000 

    mov r14, #4096 

    loop:   

    ldmia      r12!, {r0-r11}  

    stmia      r13!, {r0-r11}    

    cmp       r12, r14   

    bl          loop

    48.嵌入式中常用的文件系统有哪些?说出它们的主要特点和应用场合? 
     
    49.某外设寄存器rGpioBase的地址是0x56000000,寄存器的0~15位有效,请写出给外设寄存器高八位(8~`15位)设置成0xc3的代码
     
    50.如何编写一个LINUX驱动? 

    提示:主要说字符设备的编写过程  

     

    51.简述LINUX驱动中字符设备和块设备的区别? 

     

    52.试总结单片机底层开发与LINUX驱动开发有哪些异同?
     
    53.请从网卡、USB HOST、LCD驱动器、NAND FLASH、WIFI 、音频芯片中选择一个或者2个(可以以具体的芯片为例),对下面的问题做答: 

    1)如果是外部扩展芯片,请说出你用的芯片的型号 
    2)画出上题中你选定相应硬件模块与CPU的主要引脚连线 

    3) 编写上题中你选定相应硬件模块相应LINUX驱动的流程? 

      
    54、linux驱动分类

    Linux设备驱动的分类
      (1)字符设备。
      (2) 块设备。
      (3) 网络设备。
      字符设备指那些必须以串行顺序依次进行访问的设备,如触摸屏、磁带驱动器、鼠标等。块设备可以用任意顺序进行访问,以块为单位进行操作,如硬盘、软驱等。字符设备不经过系统的快速缓冲,而块设备经过系统的快速缓冲。但是,字符设备和块设备并没有明显的界限,如对于Flash设备,符合块设备的特点,但是我们仍然可以把它作为一个字符设备来访问。网络设备在Linux里做专门的处理。Linux的网络系统主要是基于BSD unix的socket 机制。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据的传递。系统里支持对发送数据和接收数据的缓存,提供流量控制机制,提供对多协议的支持。


    55、信号量与自旋锁

    自旋锁
      自旋锁是专为防止多处理器并发而引入的一种锁,它应用于中断处理等部分。对于单处理器来说,防止中断处理中的并发可简单采用关闭中断的方式,不需要自旋锁。
      自旋锁最多只能被一个内核任务持有,如果一个内核任务试图请求一个已被争用(已经被持有)的自旋锁,那么这个任务就会一直进行忙循环——旋转——等待锁重新可用。要是锁未被争用,请求它的内核任务便能立刻得到它并且继续进行。自旋锁可以在任何时刻防止多于一个的内核任务同时进入临界区,因此这种锁可有效地避免多处理器上并发运行的内核任务竞争共享资源。
      事实上,自旋锁的初衷就是:在短期间内进行轻量级的锁定。一个被争用的自旋锁使得请求它的线程在等待锁重新可用的期间进行自旋(特别浪费处理器时间),所以自旋锁不应该被持有时间过长。如果需要长时间锁定的话, 最好使用信号量。但是自旋锁节省了上下文切换的开销。
    自旋锁的基本形式如下:
      spin_lock(&mr_lock);
      //临界区
      spin_unlock(&mr_lock);
      因为自旋锁在同一时刻只能被最多一个内核任务持有,所以一个时刻只有一个线程允许存在于临界区中。这点很好地满足了对称多处理机器需要的锁定服务。在单处理器上,自旋锁仅仅当作一个设置内核抢占的开关。如果内核抢占也不存在,那么自旋锁会在编译时被完全剔除出内核。
      简单的说,自旋锁在内核中主要用来防止多处理器中并发访问临界区,防止内核抢占造成的竞争。另外自旋锁不允许任务睡眠(持有自旋锁的任务睡眠会造成自死锁——因为睡眠有可能造成持有锁的内核任务被重新调度,而再次申请自己已持有的锁),它能够在中断上下文中使用。
    死锁:假设有一个或多个内核任务和一个或多个资源,每个内核都在等待其中的一个资源,但所有的资源都已经被占用了。这便会发生所有内核任务都在相互等待,但它们永远不会释放已经占有的资源,于是任何内核任务都无法获得所需要的资源,无法继续运行,这便意味着死锁发生了。自死琐是说自己占有了某个资源,然后自己又申请自己已占有的资源,显然不可能再获得该资源,因此就自缚手脚了。递归使用一个自旋锁就会出现这种情况。
     
    信号量
    信号量是一种睡眠锁。如果有一个任务试图获得一个已被持有的信号量时,信号量会将其推入等待队列,然后让其睡眠。这时处理器获得自由去执行其它代码。当持有信号量的进程将信号量释放后,在等待队列中的一个任务将被唤醒,从而便可以获得这个信号量。
      信号量的睡眠特性,使得信号量适用于锁会被长时间持有的情况;只能在进程上下文中使用,因为中断上下文中是不能被调度的;另外当代码持有信号量时,不可以再持有自旋锁。
    信号量基本使用形式为:
      static DECLARE_MUTEX(mr_sem);//声明互斥信号量
      if(down_interruptible(&mr_sem))
               //可被中断的睡眠,当信号来到,睡眠的任务被唤醒
               //临界区
           up(&mr_sem);  

    信号量和自旋锁区别
      从严格意义上讲,信号量和自旋锁属于不同层次的互斥手段,前者的实现有赖于后者。
    注意以下原则:
           如果代码需要睡眠——这往往是发生在和用户空间同步时——使用信号量是唯一的选择。由于不受睡眠的限制,使用信号量通常来说更加简单一些。如果需要在自旋锁和信号量中作选择,应该取决于锁被持有的时间长短。理想情况是所有的锁都应该尽可能短的被持有,但是如果锁的持有时间较长的话,使用信号量是更好的选择。另外,信号量不同于自旋锁,它不会关闭内核抢占,所以持有信号量的代码可以被抢占。这意味者信号量不会对影响调度反应时间带来负面影响。
     
    自旋锁对信号量
    需求              建议的加锁方法
    低开销加锁           优先使用自旋锁
    短期锁定            优先使用自旋锁
    长期加锁            优先使用信号量
    中断上下文中加锁        使用自旋锁
    持有锁是需要睡眠、调度     使用信号量 


    56、platform总线设备及总线设备如何编写


    57、kmalloc和vmalloc的区别

    kmalloc()和vmalloc()介绍
    kmalloc()
    用于申请较小的、连续的物理内存
    1. 以字节为单位进行分配,在<linux/slab.h>中
    2. void *kmalloc(size_t size, int flags) 分配的内存物理地址上连续,虚拟地址上自然连续
    3. gfp_mask标志:什么时候使用哪种标志?如下:
    ———————————————————————————————-
    情形        相应标志
    ———————————————————————————————-
    进程上下文,可以睡眠 GFP_KERNEL
    进程上下文,不可以睡眠 GFP_ATOMIC
    中断处理程序 GFP_ATOMIC
    软中断 GFP_ATOMIC
    Tasklet GFP_ATOMIC
    用于DMA的内存,可以睡眠 GFP_DMA | GFP_KERNEL
    用于DMA的内存,不可以睡眠 GFP_DMA | GFP_ATOMIC
    ———————————————————————————————-
    4. void kfree(const void *ptr)
    释放由kmalloc()分配出来的内存块
    vmalloc()
    用于申请较大的内存空间,虚拟内存是连续的
    1. 以字节为单位进行分配,在<linux/vmalloc.h>中
    2. void *vmalloc(unsigned long size) 分配的内存虚拟地址上连续,物理地址不连续
    3. 一般情况下,只有硬件设备才需要物理地址连续的内存,因为硬件设备往往存在于MMU之外,根本不了解虚拟地址;但为了性能上的考虑,内核中一般使用 kmalloc(),而只有在需要获得大块内存时才使用vmalloc(),例如当模块被动态加载到内核当中时,就把模块装载到由vmalloc()分配 的内存上。
    4.void vfree(void *addr),这个函数可以睡眠,因此不能从中断上下文调用。

    malloc(), vmalloc()和kmalloc()区别
    [*]kmalloc和vmalloc是分配的是内核的内存,malloc分配的是用户的内存
    [*]kmalloc保证分配的内存在物理上是连续的,vmalloc保证的是在虚拟地址空间上的连续,malloc不保证任何东西(这点是自己猜测的,不一定正确)
    [*]kmalloc能分配的大小有限,vmalloc和malloc能分配的大小相对较大
    [*]内存只有在要被DMA访问的时候才需要物理上连续
    [*]vmalloc比kmalloc要慢

    58、module_init的级别


    59、添加驱动

    静态加载和动态加载:
    静态加载是系统启动的时候由内核自动加载的,这个要事先将驱动编译进内核才行;
    动态加载,也就是模块加载方式,这种方式下驱动以模块的形式存放在文件系统中,需要时动态载入内核,这种主要用在调试的时候,比较方便灵活。insmod module.ko

    60、IIC原理,总线框架,设备编写方法,i2c_msg


    61、kernel panic


    62、USB总线,USB传输种类,urb等

    USB总线:
    USB总线属于一种轮询式总线,主机控制端口初始化所有的数据传输。每一总线动作最多传送三个数据包,包括令牌(Token)、数据(Data)、联络(HandShake)。按照传输前制定好的原则,在每次传送开始时,主机送一个描述传输动作的种类、方向、USB设备地址和终端号的USB数据包,这个数据包通常被称为令牌包(TokenPacket)。USB设备从解码后的数据包的适当位置取出属于自己的数据。数据传输方向不是从主机到设备就是从设备到主机。在传输开始时,由标志包来标志数据的传输方向,然后发送端开始发送包含信息的数据包或表明没有数据传送。接收端也要相应发送一个握手的数据包表明是否传送成功。发送端和接收端之间的USB数据传输,在主机和设备的端口之间,可视为一个通道。USB中有一个特殊的通道一缺省控制通道,它属于消息通道,设备一启动即存在,从而为设备的设置、状态查询和输入控制信息提供一个入口。

    USB总线的四种传输类型:
    1、中断传输:由OUT事务和IN事务构成,用于键盘、鼠标等HID设备的数据传输中 2、批量传输:由OUT事务和IN事务构成,用于大容量数据传输,没有固定的传输速率,也不占用带宽,当总线忙时,USB会优先进行其他类型的数据传输,而暂时停止批量转输。 3、同步传输:由OUT事务和IN事务构成,有两个特别地方,第一,在同步传输的IN和OUT事务中是没有返回包阶段的;第二,在数据包阶段任何的数据包都为DATA0 4、控制传输:最重要的也是最复杂的传输,控制传输由三个阶段构成(初始配置阶段、可选数据阶段、状态信息步骤),每一个阶段能够看成一个的传输,也就是说控制传输其实是由三个传输构成的,用来于USB设备初次加接到主机之后,主机通过控制传输来交换信息,设备地址和读取设备的描述符,使得主机识别设备,并安装相应的驱动程式,这是每一个USB研发者都要关心的问题。
    URB:
    USB请求块(USB request block,urb)是USB设备驱动中用来描述与USB设备通信所用的基本载体和核心数据结构,非常类似于网络设备驱动中的sk_buff结构体,是USB主机与设备通信的“电波”。
    63、同步和互斥
    同步和互斥
      相交进程之间的关系主要有两种,同步与互斥。所谓互斥,是指散步在不同进程之间的若干程序片断,当某个进程运行其中一个程序片段时,其它进程就不能运行它们之中的任一程序片段,只能等到该进程运行完这个程序片段后才可以运行。所谓同步,是指散步在不同进程之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。
      显然,同步是一种更为复杂的互斥,而互斥是一种特殊的同步。也就是说互斥是两个线程之间不可以同时运行,他们会相互排斥,必须等待一个线程运行完毕,另一个才能运行,而同步也是不能同时运行,但他是必须要安照某种次序来运行相应的线程(也是一种互斥)!
    总结:
      互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
      同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源

    64、 Linux设备中字符设备与块设备有什么主要的区别?请分别列举一些实际的设备说出它们是属于哪一类设备。
     答:字符设备:字符设备是个能够像字节流(类似文件)一样被访问的设备,由字符设备驱动程序来实现这种特性。字符设备驱动程序通常至少实现open,close,read和write系统调用。字符终端、串口、鼠标、键盘、摄像头、声卡和显卡等就是典型的字符设备。
    块设备:和字符设备类似,块设备也是通过/dev目录下的文件系统节点来访问。块设备上能够容纳文件系统,如:u盘,SD卡,磁盘等。
        字符设备和块设备的区别仅仅在于内核内部管理数据的方式,也就是内核及驱动程序之间的软件接口,而这些不同对用户来讲是透明的。在内核中,和字符驱动程序相比,块驱动程序具有完全不同的接口


    65、查看驱动模块中打印信息应该使用什么命令?如何查看内核中已有的字符设备的信息?如何查看正在使用的有哪些中断号?
    答:1) 查看驱动模块中打印信息的命令:dmesg
         2) 查看字符设备信息可以用lsmod 和modprobe,lsmod可以查看模块的依赖关系,modprobe在加载模块时会加载其他依赖的模块。
         3)显示当前使用的中断号cat /proc/interrupt

    66、Linux中引入模块机制有什么好处?
    答:首先,模块是预先注册自己以便服务于将来的某个请求,然后他的初始化函数就立即结束。换句话说,模块初始化函数的任务就是为以后调用函数预先作准备。
    好处:
    1) 应用程序在退出时,可以不管资源的释放或者其他的清除工作,但是模块的退出函数却必须仔细此撤销初始化函数所作的一切。
    2) 该机制有助于缩短模块的开发周期。即:注册和卸载都很灵活方便。


    67、copy_to_user()和copy_from_user()主要用于实现什么功能?一般用于file_operations结构的哪些函数里面?
    答:由于内核空间和用户空间是不能互相访问的,如果需要访问就必须借助内核函数进行数据读写。copy_to_user():完成内核空间到用户空间的复制,copy_from_user():是完成用户空间到内核空间的复制。一般用于file_operations结构里的read,write,ioctl等内存数据交换作用的函数。当然,如果ioctl没有用到内存数据复制,那么就不会用到这两个函数。

    68、请简述主设备号和次设备号的用途。如果执行mknod chartest c 4 64,创建chartest设备。请分析chartest使用的是那一类设备驱动程序。
    答:
    1)主设备号:主设备号标识设备对应的驱动程序。虽然现代的linux内核允许多个驱动程序共享主设备号,但我们看待的大多数设备仍然按照“一个主设备对应一个驱动程序”的原则组织。
      次设备号:次设备号由内核使用,用于正确确定设备文件所指的设备。依赖于驱动程序的编写方式,我们可以通过次设备号获得一个指向内核设备的直接指针,也可将此设备号当作设备本地数组的索引。
    2)chartest 由驱动程序4管理,该文件所指的设备是64号设备。(感觉类似于串口终端或者字符设备终端)。


    69、设备驱动程序中如何注册一个字符设备?分别解释一下它的几个参数的含义。
    答:注册一个字符设备驱动有两种方法:
    1) void cdev_init(struct cdev *cdev, struct file_operations *fops)
    该注册函数可以将cdev结构嵌入到自己的设备特定的结构中。cdev是一个指向结构体cdev的指针,而fops是指向一个类似于file_operations结构(可以是file_operations结构,但不限于该结构)的指针.
    2) int register_chrdev(unsigned int major, const char *namem , struct file)operations *fopen);
    该注册函数是早期的注册函数,major是设备的主设备号,name是驱动程序的名称,而fops是默认的file_operations结构(这是只限于file_operations结构)。对于register_chrdev的调用将为给定的主设备号注册0-255作为次设备号,并为每个设备建立一个对应的默认cdev结构。

    70、请简述中断与DMA的区别。Linux设备驱动程序中,使用哪个函数注册和注销中断处理程序?
    答:1)DMA:是一种无须CPU的参与就可以让外设与系统内存之间进行双向数据传输的硬件机制,使用DMA可以使系统CPU从实际的I/O数据传输过程中摆脱出来,从而大大提高系统的吞吐率。
    中断:是指CPU在执行程序的过程中,出现了某些突发事件时CPU必须暂停执行当前的程序,转去处理突发事件,处理完毕后CPU又返回源程序被中断的位置并继续执行。
    所以中断和MDA的区别就是MDA不需CPU参与而中断是需要CPU参与的。
    2)中断注册函数和中断注销函数
    注册中断:
    int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *dev_name, void *dev_id);
    参数意义依次是:中断号,中断处理函数,中断管理有关的掩码,中断请求设备名,中断信号线。
    过程是:dev_name设备请求中断->cpu分配中断号->设置中断管理的掩码->分配中断信号线->处理中断函数->完成之后再根据设置情况返回原处理程序处继续处理程序。
    注销中断;
    Void free_irq(unsigned int irq, void *dev_id);
    释放中断和中断信号线

    71、中断和轮询哪个效率高?怎样决定是采用中断方式还是采用轮询方式去实现驱动?
    答:中断是CPU处于被中状态下来接受设备的信号,而轮询是CPU主动去查询该设备是否有请求。凡事都是两面性,所以,看效率不能简单的说那个效率高。如果是请求设备是一个频繁请求cpu的设备,或者有大量数据请求的网络设备,那么轮询的效率是比中断高。如果是一般设备,并且该设备请求cpu的频率比较底,则用中断效率要高一些。

    72、简单描述在cs8900的驱动设计中, 发送数据frame和接收数据frame的过程。
    答:1)发送流程如下:
    (1) 网络设备驱动程序从上层协议传递过来的sk_buff参数获得数据包的有效数据和长度,将有效数据放入临时缓冲区。
    (2) 对于以太网,如果有效数据的长度小于以太网冲突检测所要求的数据桢的最小长度,则给临时缓冲区的末尾填充0
    (3) 设置硬件寄存器,驱使网络设备进行数据发送操作。
       2)接收流程
       网络设备接收数据主要是由中断引发设备的中断处理函数,中断处理函数判断中断类型,如果为接收中断,则读取接受到的数据,分配sk_buff数据结构和数据缓冲区,将接收到的数据复制到数据缓冲区,并调用netif_rx()函数将sk_buff传递给上层协议。


    73、cs8900.c的驱动中,发送数据frame的过程为什么需要关中断?接收数据frame的过程为什么不需要关中断?
    答:在发送过程中是不能被打断的,在发送的过程中,不关中断,这时候如果有一个中断到来,那么cpu有可能会去相应该中断,如果该中断需要改写的数据是发送数据的缓冲区,那么缓冲区将被改写,这样即使cpu相应完毕该中断,再发送数据,接收方也不认识该数据不能接收。
    在接收数据的时候,需要打开中断,是因为要及时的相应接收到的数据。如果关闭该中断,那么接收方有可能因为相应优先级高的中断而接收不到该数据。


    74、简单描述skbuff这个数据结构在网络结构中所起到的作用,为什么需要一个skbuff,它的分配和释放主要都在什么部位
    答:sk_buff结构非常重要,它的含义为“套接字缓冲区”,用于在linux网络子系统中的各层之间传递数据。
    当发送数据包时,linux内核的网络处理模块必须建立一个包含要传输的数据包的sk_buff,然后将sk_buff递交给下层,各层在sk_buff中添加不同的协议头直至交给网络设备发送。同样的,当网络设备从网络媒介上接受到数据包后,它必须将接受到的数据转换为sk_buff数据结构并传递给上层,盖层不抛去相应的协议头直至交给用户。分配sk_buff在接收一开始就应该分配,在发送完毕数据之后可以释放sk_buff。

    linux driver面试题 1

    [预处理器(Preprocessor)]

    1. 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)

    #define SECONDS_PER_YEAR (60 * 60 * 24 *365)UL

    我在这想看到几件事情:

    1). #define 语法的基本知识(例如:不能以分号结束,括号的使用,等等)

    2). 懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。

    3). 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。

    4). 如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。记住,第一印象很重要。

    2. 写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。

    #define MIN(A,B) ((A) <(B) ?(A) :(B)) 

    CC++试题集 CC++ Development这个测试是为下面的目的而设的:

    1). 标识#define在宏中应用的基本知识。这是很重要的,因为直到嵌入(inline)操作符变为标准C的一部分,宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。

    2). 三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优化的代码,了解这个用法是很重要的。

    3). 懂得在宏中小心地把参数用括号括起来

    4). 我也用这个问题开始讨论宏的副作用,例如:当你写下面的代码时会发生什么事?

    Least = MIN(*p++, b);

    3. 预处理器标识#error的目的是什么?

    停止编译并显示出错误信息

    [死循环(Infinite loops)]

    4. 嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?

    这个问题用几个解决方案。我首选的方案是:

    while(1) { }

    一些程序员更喜欢如下方案:

    for(;;) { }

    这个实现方式让我为难,因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案,我将用这个作为一个机会去探究他们这样做的

    基本原理。如果他们的基本答案是:“我被教着这样做,但从没有想到过为什么。”这会给我留下一个坏印象。

    第三个方案是用 goto

    Loop:

    goto Loop;

    应试者如给出上面的方案,这说明或者他是一个汇编语言程序员(这也许是好事)或者他是一个想进入新领域的BASIC/FORTRAN程序员。

    [数据声明(Data declarations)]

    5. 用变量a给出下面的定义

    a) 一个整型数(An integer)

    b) 一个指向整型数的指针(A pointerto an integer)

    c) 一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an integer)

    d) 一个有10个整型数的数组(Anarray of 10 integers)

    e) 一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to integers)

    f) 一个指向有10个整型数数组的指针(Apointer to an array of 10 integers)

    g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument andreturns an integer)

    h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer argumentand return an integer )

    答案是:

    a) int a; // An integer

    b) int *a; // A pointer to an integer

    c) int **a; // A pointer to a pointer to aninteger

    d) int a[10]; // An array of 10 integers

    e) int *a[10]; // An array of 10 pointersto integers

    f) int (*a)[10]; // A pointer to an arrayof 10 integers

    g) int (*a)(int); // A pointer to afunction a that takes an integer argument and returns an integer

    h) int (*a[10])(int); // An array of 10pointers to functions that take an integer argument and return an integer

    人们经常声称这里有几个问题是那种要翻一下书才能回答的问题,我同意这种说法。当我写这篇文章时,为了确定语法的正确性,我的确查了一下书。

    但是当我被面试的时候,我期望被问到这个问题(或者相近的问题)。因为在被面试的这段时间里,我确定我知道这个问题的答案。应试者如果不知道

    所有的答案(或至少大部分答案),那么也就没有为这次面试做准备,如果该面试者没有为这次面试做准备,那么他又能为什么出准备呢?

    [Static]

    6. 关键字static的作用是什么?

    这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用:

    1). 在函数体,一个被声明为静态的变量在这一函数被调用过程中只会初始化一次。

    2). 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。

    3). 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

    大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。这是一个应试者的严重的缺点,因为他显然不懂得本地化数据和代码范围的好处和重要性。

    linux驱动面试题2

    1.什么是GPIO?

    general purpose input/output  

    GPIO是相对于芯片本身而言的,如某个管脚是芯片的GPIO脚,则该脚可作为输入或输出高或低电平使用,当然某个脚具有复用的功能,即可做GPIO也可做其他用途。 

    也就是说你可以把这些引脚拿来用作任何一般用途的输入输出,例如用一根引脚连到led的一极来控制它的亮灭,也可以用一根(一些)引脚连到一个传感器上以获得该传感器的状态,这给cpu提供了一个方便的控制周边设备的途经。如果没有足够多的gpio管脚,在控制一些外围设备时就会力有不逮,这时可采取的方案是使用CPLD来帮助管理。


    2.触摸屏的硬件原理?
    http://www.51touch.com/Article/Maintain/Technology/175945932.htm
    http://www.cublog.cn/u2/78837/showart_1186312.html
    触摸屏的主要三大种类是:电阻技术触摸屏、 表面声波技术触摸屏、 电容技术触摸屏。

    电阻触摸屏的主要部分是一块与显示器表面非常配合的电阻薄膜屏, 这是一种多层的复合薄膜,它以一层玻璃或硬塑料平板作为基层,表面图有一层透明氧化金属 (ITO氧化铟,透明的导电电阻) 导电层,上面在盖有一层外表面硬化处理、光滑防擦的塑料层 、它的内表面也涂有一层ITO涂层 、在他们之间有许多细小的(小于1/1000英寸)的透明隔离点把两层导电层隔开绝缘 。当手指触摸屏幕时,两层导电层在触摸点位置就有了接触,控制器侦测到这一接触并计算出(X,Y )的位置,再根据模拟鼠标的方式运作。这就是电阻技术触摸屏的最基本的原理。

    表面声波技术是利用声波在物体的表面进行传输,当有物体触摸到表面时,阻碍声波的传输,换能器侦测到这个变化,反映给计算机,进而进行鼠标的模拟。

    电容技术触摸屏利用人体的电流感应进行工作 。用户触摸屏幕时 ,由于人体电场,用户和触摸屏表面形成以一个耦合电容, 对于高频电流来说,电容是直接导体,于是手指从接触点吸走一个很小的电流

     
    3.在Linux C中,ls这个命令是怎么被执行的?
    使用fork创建一个进程或exec函数族覆盖原进程。

    4.在一个只有128M内存并且没有交换分区的机器上,说说下面两个程序的运行结果
    1,
    #define MEMSIZE 1024*1024
    int count = 0;
    void *p = NULL;
    while(1) {
      p = (void *)malloc(MEMSIZE);
      if (!p) break;
      printf("Current allocation %d MB ", ++count);
    }

    2,
    while(1) {
      p = (void *)malloc(MEMSIZE);
      if (!p) break;
      memset(p, 1, MEMSIZE);
      printf("Current allocation %d MB ", ++count);
    }

    第一道程序分配内存但没有填充,编译器可能会把内存分配优化掉,程序死循环;第二道,程序分配内存并进行填充,系统会一直分配内存,直到内存不足,退出循环。

    5.请定义一个宏,比较两个数a、b的大小,不能使用大于、小于、if语句

    搞的比较复杂。主要思想就是a-b的值的最高位是否为0;但是又得考虑整数溢出的问题,所以很复杂。不知道哪位大侠有更好的办法,指点指点。

    1. #include<stdio.h>
    2. #define ZHENG(i)((i>> 31)== 0)
    3. #define FU(i)((i>> 31)!= 0)
    4. #define COMPARE(a,b)((ZHENG(a)&& FU(b))||(((ZHENG(a)&& ZHENG(b))||(FU(a)&&FU(b)))&&((((a)-(b))>> 31)== 0)))
    5. void main()
    6. {
    7.     int a = 0x80000001;
    8.     int b = 0x6FFFFFFF;
    9.     if(COMPARE(a,b))
    10.     {
    11.         printf("a >= b ");
    12.     }
    13.     else
    14.     {
    15.         printf("a < b ");
    16.     }
    17. }

     

     

     

    1、LINUX下的Socket套接字和Windows下的WinSock有什么共同点?请从C/C++语言开发的角度描述,至少说出两点共同点。(10分,说得好的每点加5分,没有上限。精通SOCK编程者破格录用。)



    本题的目的就是考考应试者的SOCK功底,以及应用能力。答案可谓是五花八门,但是答对的少得可怜。其实答案很多,也很好回答。可见面试者功底确实一般,对SOCK编程只是一知半解。更有甚者,把TCP/IP的原理搬出来讲了一大堆,却无一条符合要求。



    参考答案:

    第1题,答中一个得5分,答出其它正确答案的,也得5分。

    a)都基于TCP/IP协议,都提供了面向连接的TCP SOCK和无连接的UDP SOCK。

    b)都是一个sock结构体。

    c)都是使用sock文件句柄进行访问。

    d)都具有缓冲机制。



    2、请编写一个标准Shell脚本testd,实现如下功能:

    A、在Linux操作系统启动的时候,自动加载/mnt/test/test程序。

    B、当test异常退出之后,自动重新启动。

    C、当test程序重启次数超过100次,自动复位操作系统。

    假设你所拥有的资源:

    A、目标机器是一台具有标准shell的嵌入式计算机,CPU为ARM7 56MB,内存16MB,软件环境基于Linux2.6.11和BusyBox1.2构建。

    B、当前已有11个用户进程在运行,占用了大部分的CPU时间和内存,你可使用的内存只有2MB左右,CPU时间由系统分派。

    (本题满分20分,程序15分,注释5分。程序不能运行的0分,能够运行但有BUG的10分,能够正确无误的运行的15分。清楚编写注释的5分。)



    本题是考查LINUX和嵌入式编程功底的,写出程序来的不少,但是95%以上的人竟无视我假设的资源,不知道在重启test程序的时候需要加上一个适当的
    掩饰时间,以便资源紧张的操作系统有时间回收资源。85%的人不知道写完testd之后,要在init里边加载这个脚本,才能实现启动时自动加载的功能。
    有人甚至在脚本开头使用bash作为脚本解析器,我已经清清楚楚说明了用“标准shell”!用sh不就完了吗?是习惯作祟吗?



    参考答案:

    ########################################

    #testd is a daemon script to start an watch the program test

    ########################################

    #!/bin/sh



    #load *.so that may need

    if [ -r /sbin/ldconfig ]; then

    ldconfig

    fi



    #add the libs PATH that may need

    export LD_LIBRARY_PATH="/lib"



    #count is the counter of test started times

    count=0



    #main loop

    while [ 1 ] ;do

    #add execute property for /mnt/test/test

    chmod +x /mnt/test/test

    #start test

    /mnt/test/test

    #the running times counter

    let count=count+1

    echo "test running times is $count"

    #Is test running too many times?

    if [ "$count" -gt 100 ]; then

    echo "Will reboot because of test running too many times"

    reboot

    fi

    #wait for test stoping...

    sleep 3

    done

    #########################################

     

     


    就业模拟测试题-LINUX驱动、系统底层工程师职位   1.你平常是怎么用C写嵌入式系统的死循环的? [3分]  2.写一条命令,实现在dir以及其子目录下找出所有包含“hello world”字符串的文件[2分] 

     

    3.下面的两段程序中,循环能否执行?为什么?

    A: unsigned short i; unsigned short index = 0; for(i = 0; i <index-1; i++){     printf(“a ”); }
                           B: unsigned short i; unsigned long index = 0; for(i = 0; i <index-1; i++){      printf(“b ”); }  
                    

    4.一个计划跑LINUX系统的ARM系统把bootloader烧录进去后,上电后串口上没有任何输出,硬件和软件各应该去检查什么?   

     提示: 1.跑LINUX的系统一般都需要外扩DRAM,一般的系统也经常有NOR或NAND FLASH          

     

    2.bootloader一般是由汇编和C编写的裸奔程序[5分] 

     

     

     5.列举最少3种你所知道的嵌入式的体系结构,并请说明什么是ARM体系结构。[7分] 

     

    6.请简述下面这段代码的功能 mov r12, #0x0        ldr r13, =0x30100000 mov r14, #4096 loop:   ldmia      r12!, {r0-r11}  stmia      r13!, {r0-r11}    cmp       r12, r14   blo          loop  [2分]

     

     


     7.嵌入式中常用的文件系统有哪些?说出它们的主要特点和应用场合?[5分] 

     

     

     8.某外设寄存器rGpioBase的地址是0x56000000,寄存器的0~15位有效,请写出给外设寄存器高八位(8~`15位)设置成0xc3的代码[7分] 

     

    9.根据时序图和说明编写程序:


      GPIO已经设置好,只需要调用函数gpio_seet_level(int gpio, int level)即课使某个GPIO输出高电平或者低电平。图中用于产生时序的gpio已经分别定义为SSP_XCS,SSP_SCLK,SSP_DIN,level的定义分别为GPIO_LO和GPIO_HI,需要编写函数的原型为:void ssp_io_write_word(u32 command),该函数用来输出一个字(如上图中的A0到C0一组9位),这9个位是在参数command中的低9位. [5分]  10.简述LINUX系统从上电开始到系统起来的主要流程?  提示: 1.可以uboot、内核和文件系统的主要功能去总结         

     

    2.这个题主要是在笔试之后的面试,需要在3~5分钟之内表述清楚[8分] 

     

     

    11.如何编写一个LINUX驱动?

     

    提示:主要说字符设备的编写过程 [7分]    12.简述LINUX驱动中字符设备和块设备的区别?[5分]   

     

     

    13.试总结单片机底层开发与LINUX驱动开发有哪些异同?[4分] 

     

     

     14.请从网卡、USB HOST、LCD驱动器、NAND FLASH、WIFI 、音频芯片中选择一个或者2个(可以以具体的芯片为例),对下面的问题做答: 
    1.如果是外部扩展芯片,请说出你用的芯片的型号 
                          [每空5分] 

     

    15.画出上题中你选定相应硬件模块与CPU的主要引脚连线[5分]  16. 编写上题中你选定相应硬件模块相应LINUX驱动的流程?[6分] 

     

     

     

     

     

     

     
    int *ptr = (int *)0xaa66; *ptr = 0x67a9; 
    我感觉自己写的应该不算错吧(自我感觉,还请达人指正),我写的适合裸机下用,当做寄存器用,而答案就是适合在操作系统下的写法。 
       
    1. linux内核里面,内存申请有哪几个函数,各自的区别?  
    2. IRQ和FIQ有什么区别,在CPU里面是是怎么做的?  3.   
    int *a;  
    char *b;  
    a 和 b本身是什么类型?  
    a、b里面本身存放的只是一个地址,难道是这两个地址有不同么?    
    4. 中断的上半部分和下半部分的问题:  
    讲下分成上半部分和下半部分的原因,为何要分?讲下如何实现?  
    5. 内核函数mmap的实现原理,机制?  
    6. 驱动里面为什么要有并发、互斥的控制?如何实现?讲个例子?  
    7. spinlock自旋锁是如何实现的?  
    8. 任务调度的机制?  
    【二、本人碰到】  
    1. 嵌入式linux和wince操作系统的特点和特性?  
    2. 嵌入式linux中tty设备驱动的体系结构?  
    3. 嵌入式设备,为加快启动速度,可以做哪些方面的优化?  

     

     


     
    4. USB设备的枚举过程?  
    5. PSRAM、SDRAM、DDR、DDR2的时序特性?  
    6. I2C触摸屏芯片与CPU的数据传输流程?画出相关图例?(这题目记得不是太清楚了,大概是考查I2C设备驱动的数据传输过程) 

     

     

    1、linux驱动分类

    2、信号量与自旋锁

    3、platform总线设备及总线设备如何编写

    4、kmalloc和vmalloc的区别

    5、module_init的级别

    6、添加驱动

    7、IIC原理,总线框架,设备编写方法,i2c_msg

    8、kernel panic

    9、USB总线,USB传输种类,urb等

    10、android boot 流程

    11、android init解析init.rc

    驱动工程师 笔试题

    一、选择题

    1:main() 
    { int x=1,y=1; 
      y=x-- ; 
      printf( “ %d,%d ”,x,y); 
    }运行结果为(  ) ;
    A.0 , 0                            B.0 , 1 
    C.1 , 0                             D.1 , 1


    2:某文件中定义的静态全局变量(或称静态外部变量)其作用域是(  ) 
    A.只限某个函数                B.本文件 
    C.跨文件                     D.不限制作用域


    3:设 int a[10],*p=a ;则对数组元素的正确引用是(  )  
    A.a[p]                       B.p[a] 
    C.*(p+2)                     D.p+2


    4:C语言中,系统自动打开的文件是(  ) 
    A.二进制文件                     B.随机文件 
    C.非缓冲文件                     D.设备文件


    5:下列程序的运行结果是(  )  

    main( ) 
    { int a[][4]={1,3,5,7,9,11,13,15,17,19,21,23};

      int (*p)[4],i=2,j=1;

      p=a;

      printf(″%d ″,*(*(p+i)+j));


    A.9      B.11        C.17         D.19


    6:在软件生命周期中,下列哪个说法是不准确的?(  )  
    A.软件生命周期分为计划、开发和运行三个阶段 
    B.在计划阶段要进行问题确认和需求分析 
    C.在开发后期才能进行编写代码和软件测试 
    D.在运行阶段主要是进行软件维护


    7:下列语句定义整型指针p1、p2,(  )  是正确的。 
    A.int p1,p2;                    B.int*p1,*p2; 
    C.int *p1,p2;                   D.int**p1,p2;

    8:下列程序的运行结果是(  )

    main()
    {
      int a[5] = {1,2,3,4,5};
      int *ptr =  (int*)(&a+1);

      printf("%d %d" , *(a+1),*(ptr-1) );

    }
    A. 2  2                                  B. 2  1
    C.2  5                                  D.以上均不是

    二、简答题

    8、下面的程序或程序段存在一个错误或不妥处请在其下划一条线,并将改正的内容写到每小题后的空白处 
    main() 
    { char cl,c2; 
      cl='9'; 
      c2='10'; 
      printf(”cl=%c,c2=%c ”,c1,c2); 
    }

     

     

     

    9、下面的代码输出是什么,为什么?
    void foo(void)
    {
    unsigned int a = 6;
    int b = -20;
    (a+b > 6) ? puts("> 6") : puts("<= 6");
    }

     

     

     

    10、中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展 ―让标准 C支持中断。具代表事实是,产生了一个新的关键字__interrupt,下面的代码就使用了 __interrupt关键字去定义了一个中断服务子程序 (ISR),请评论一下这段代码,找出错误并改正.

    __interruptdouble compute_area (double radius)

    {

    doublearea = PI * radius * radius;

    printf(" Area= %f", area);

    returnarea;

    }

     

     

     

     

    三、内核驱动题

    11、请简述arm linux内核启动流程。

     

     

     

     

    12、驱动里面为什么要有并发、互斥的控制?如何实现?举例说明。

     

     

     

     

     

    13、请简述linux内核终端处理分成上半部分和下半部分的原因,为何要分?如何实现?

     

     

     

     

    四、编程实现题

     

    14、设计并实现一个在linux平台下简单的内存FIFO字符设备驱动,并简述该驱动的验证方法。

    驱动软件工程师笔试题

    1、写一段 C 代码让程序跳转到地址是 0x8000 0000 的位置执行

    答:(*(void(*)(void))0x100000)();或者((void(*)(void))0x100000)();

     

    2、简述static 关键字的作用。

    答:1)在函数体内,static 变量的作用范围为该函数体,不同于auto 变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值。

    2)在模块内的static 全部变量可以被模块内所有函数访问,但不能被模块外其他函数访问。

    3)在模块内的static 函数只可被这一模块内的其他函数调用,这个函数的使用范围被限制在声明它的模块内。

     

    3、简要叙述进程和线程这两个概念。

    答:进程是指一个程序在一个数据集合上的一次运行过程。

    线程是进程中的一个实体,是被系统独立调度和执行的基本单位。

    进程是程序的一次执行。线程可以理解为进程中执行的一段程序片段。

     

    4、请简述自旋锁和信号量两个概念并对他们作出比较。

    答:1)自旋锁
    自旋锁是专为防止多处理器并发而引入的一种锁,它在内核中大量应用于中断处理等部分(对于单处理器来说,防止中断处理中的并发可简单采用关闭中断的方式,不需要自旋锁)。
    自旋锁最多只能被一个内核任务持有,如果一个内核任务试图请求一个已被争用(已经被持有)的自旋锁,那么这个任务就会一直进行忙循环——旋转——等待锁重新可用。要是锁未被争用,请求它的内核任务便能立刻得到它并且继续进行。自旋锁可以在任何时刻防止多于一个的内核任务同时进入临界区,因此这种锁可有效地避免多处理器上并发运行的内核任务竞争共享资源。
    事实上,自旋锁的初衷就是:在短期间内进行轻量级的锁定。一个被争用的自旋锁使得请求它的线程在等待锁重新可用的期间进行自旋(特别浪费处理器时间),所以自旋锁不应该被持有时间过长。如果需要长时间锁定的话, 最好使用信号量。
    自旋锁的基本形式如下:
    spin_lock(&mr_lock);
    //临界区
    spin_unlock(&mr_lock);
    因为自旋锁在同一时刻只能被最多一个内核任务持有,所以一个时刻只有一个线程允许存在于临界区中。这点很好地满足了对称多处理机器需要的锁定服务。在单处理器上,自旋锁仅仅当作一个设置内核抢占的开关。如果内核抢占也不存在,那么自旋锁会在编译时被完全剔除出内核。
    简单的说,自旋锁在内核中主要用来防止多处理器中并发访问临界区,防止内核抢占造成的竞争。另外自旋锁不允许任务睡眠(持有自旋锁的任务睡眠会造成自死锁——因为睡眠有可能造成持有锁的内核任务被重新调度,而再次申请自己已持有的锁),它能够在中断上下文中使用。
    死锁:假设有一个或多个内核任务和一个或多个资源,每个内核都在等待其中的一个资源,但所有的资源都已经被占用了。这便会发生所有内核任务都在相互等待,但它们永远不会释放已经占有的资源,于是任何内核任务都无法获得所需要的资源,无法继续运行,这便意味着死锁发生了。自死琐是说自己占有了某个资源,然后自己又申请自己已占有的资源,显然不可能再获得该资源,因此就自缚手脚了。

    2)信号量
    Linux中的信号量是一种睡眠锁。如果有一个任务试图获得一个已被持有的信号量时,信号量会将其推入等待队列,然后让其睡眠。这时处理器获得自由去执行其它代码。当持有信号量的进程将信号量释放后,在等待队列中的一个任务将被唤醒,从而便可以获得这个信号量。
    信号量的睡眠特性,使得信号量适用于锁会被长时间持有的情况;只能在进程上下文中使用,因为中断上下文中是不能被调度的;另外当代码持有信号量时,不可以再持有自旋锁。
    信号量基本使用形式为:
    static DECLARE_MUTEX(mr_sem);//声明互斥信号量
    if(down_interruptible(&mr_sem))
    //可被中断的睡眠,当信号来到,睡眠的任务被唤醒
    //临界区
    up(&mr_sem);

    3)信号量和自旋锁区别
    虽然听起来两者之间的使用条件复杂,其实在实际使用中信号量和自旋锁并不易混淆。注意以下原则:
    如果代码需要睡眠——这往往是发生在和用户空间同步时——使用信号量是唯一的选择。由于不受睡眠的限制,使用信号量通常来说更加简单一些。如果需要在自旋锁和信号量中作选择,应该取决于锁被持有的时间长短。理想情况是所有的锁都应该尽可能短的被持有,但是如果锁的持有时间较长的话,使用信号量是更好的选择。另外,信号量不同于自旋锁,它不会关闭内核抢占,所以持有信号量的代码可以被抢占。这意味者信号量不会对影响调度反应时间带来负面影响。

  • 相关阅读:
    DHT(Distributed Hash Table) Translator
    Introducing shard translator
    【转】shell脚本中echo显示内容带颜色
    javac 错误: 编码GBK的不可映射字符
    一致性哈希(consistent hashing)
    在bash shell中使用getfattr查看文件扩展属性
    css3在不同型号手机浏览器上的兼容一览表
    META是什么意思?
    JS异步加载的三种方式
    AJAX中的同步加载与异步加载
  • 原文地址:https://www.cnblogs.com/alantu2018/p/8994603.html
Copyright © 2011-2022 走看看