官术网_书友最值得收藏!

3.1.3 Linux內核空間到用戶空間的接口

Linux內核中的驅動程序,是介于硬件到用戶空間之間的部分。在大多數情況下,驅動程序需要提供內核空間(Kernel Space)到用戶空間(User Space)的接口。

Linux內核空間到用戶空間的接口情況,通常分為以下幾種類型:

系統調用(System Call)

字符設備節點

塊設備節點

網絡設備

proc文件系統

sys文件系統

無用戶空間接口

1.系統調用

系統調用是指操作系統實現的所有系統調用所構成的集合,即程序接口。

Linux中系統調用分為:進程控制、文件系統控制、系統控制、內存管理、網絡管理、控制、用戶管理、進程間通信等類型。

在Linux操作系統中,系統調用的id通常在arch/{體系結構}/include/asm/目錄的unistd.h文件中。

每種體系結構的系統調用基本相同,在某些不常用的系統調用中,可能有一些不同的地方。

系統調用是內核空間到用戶空間最直接的接口,在需要增加內核到用戶空間的功能時,增加系統調用也是一種直接的方式。

由于系統調用是UNIX標準的內容,一般情況下不使用增加系統調用的方式增加內核空間用戶空間的接口。

Android系統沒有對標準的Linux增加系統調用。

2.字符設備節點

字符設備特殊文件進行I/O操作不經過操作系統的緩沖區,進行I/O操作時每次只傳輸一個字符。典型的字符設備如:鼠標、鍵盤、串口等。字符設備可以通過字符設備文件來訪問。

文件操作file_operations表示了對一個文件的操作。其結構在Linux源文件的include/linux目錄的fs.h文件中定義。

    struct file_operations {
        struct module *owner;
        loff_t (*llseek) (struct file *, loff_t, int);
        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        int (*readdir) (struct file *, void *, filldir_t);
        unsigned int (*poll) (struct file *, struct poll_table_struct *);
        int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
        int (*mmap) (struct file *, struct vm_area_struct *);
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *, fl_owner_t id);
        int (*release) (struct inode *, struct file *);
        int (*fsync) (struct file *, struct dentry *, int datasync);
        int (*aio_fsync) (struct kiocb *, int datasync);
        int (*fasync) (int, struct file *, int);
        int (*lock) (struct file *, int, struct file_lock *);
        ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
        ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
        unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long,
  unsigned long, unsigned long);
        int (*check_flags)(int);
        int (*dir_notify)(struct file *filp, unsigned long arg);
        int (*flock) (struct file *, int, struct file_lock *);
        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t,
  unsigned int);
        ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t,
  unsigned int);
    };

對于一般的文件,可以使用通用的文件操作。在文件操作中,open函數指針對應open()系統調用;read函數指針對應read()系統調用;write函數指針對應write()系統調用;ioctl函數指針對應ioctl()系統調用;mmap函數指針對應mmap()系統調用;release函數指針對應close()系統調用,同時當文件的所有引用都被注銷后,也會被調用。

字符設備的注冊和注銷,通常使用fs.h中的以下兩個函數:

    extern int register_chrdev(unsigned int, const char *,
                    const struct file_operations *);
    extern void unregister_chrdev(unsigned int, const char *);

對于簡單的字符設備,也是驅動的設備節點,但是其文件操作和一般的文件是不一樣的,需要單獨實現。

字符設備的驅動程序結構如圖3-3所示。

圖3-3 字符設備的驅動程序結構

對于字符設備的驅動程序,可以基于字符設備的驅動程序框架構建(如A驅動程序),也可以基于特定的字符設備驅動框架來構建(如B驅動程序)。

基于字符設備的驅動程序框架構建:直接實現對字符設備的注冊函數進行注冊,這樣定義的字符設備一般具有一個主設備號,需要使用沒有分配或者沒有使用的主設備號。在注冊具體的字符設備時,構建file_operations中的各個成員函數指針,在用戶空間通過/dev/中的設備節點調用驅動程序的時候,實際上是調用內核中字符設備驅動程序的框架,字符設備驅動程序的框架再調用具體驅動程序注冊的回調函數。

基于特定的字符設備驅動框架來構建:這些驅動程序的框架通常對字符設備框架進行封裝,實現自己的框架,并且具有了自己的注冊機制。這樣在具體的驅動程序的實現中,通過這種驅動程序框架的注冊函數進行注冊,實現相關的函數。這種情況下,驅動程序的框架一般已經實現了主設備號,具體的驅動程序實現的是次設備號。在用戶空間通過/dev/中的設備節點調用驅動程序時,實際上是先調用內核中字符設備驅動程序的框架,字符設備驅動程序的框架再調用該類驅動程序的框架,該類驅動程序的框架最后調用具體驅動程序的實現。這種方式更為常用,例如,Misc驅動程序、幀緩沖驅動程序、TTY驅動程序等。

用戶空間對字符設備的訪問,通常使用如下的文件操作接口:

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <sys/mman.h>
    int open( const char * pathname, int flags);
    int close(int fd);
    ssize_t read(int fd,void * buf ,size_t count);
    ssize_t write (int fd,const void * buf,size_t count);
    off_t lseek(int fildes,off_t offset ,int whence);
    int ioctl(int fd ,unsigned int cmd,…);
    void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offsize);

在Android系統中,包含了眾多的標準的字符設備和Android特有的字符設備,設備節點大部分在/dev/目錄中,例如在Android仿真器的系統中,使用Goldfish內核,dev目錄中的部分內容如下所示:

    # ls -l /dev/
    crw-rw-rw- root     root       5,   2 2010-02-10 06:39 ptmx
    crw------- root     root       7, 128 2010-02-10 06:39 vcsa
    crw------- root     root       7,   0 2010-02-10 06:39 vcs
    crw------- root     root     253,   2 2010-02-10 06:40 ttyS2
    crw------- root     root     253,   1 2010-02-10 06:41 ttyS1
    crw------- root     root     253,   0 2010-02-10 06:39 ttyS0
    crw------- root     root       4,   1 2010-02-10 06:09 tty1
    crw-rw---- root     system     4,   0 2010-02-10 06:09 tty0
    crw------- root     root       5,   1 2010-02-10 06:09 console
    crw-rw-rw- root     root       5,   0 2010-02-10 06:09 tty
    crw------- root     root      10,  53 2010-02-10 06:09 network_throughput
    crw------- root     root      10,  54 2010-02-10 06:09 network_latency
    crw------- root     root      10,  55 2010-02-10 06:09 cpu_dma_latency
    crw-rw-rw- root     root      10,  59 2010-02-10 06:09 binder
    crw------- root     root      10,  60 2010-02-10 06:09 device-mapper
    crw-rw-r-- system   radio     10,  61 2010-02-10 06:09 alarm
    crw------- root     root      10,   1 2010-02-10 06:09 psaux
    crw-rw-rw- root     root      10,  62 2010-02-10 06:09 ashmem
    crw-rw---- root     audio     10,  63 2010-02-10 06:09 eac
    crw------- root     root       1,  11 2010-02-10 06:09 kmsg
    crw-rw-rw- root     root       1,   9 2010-02-10 06:10 urandom
    crw-rw-rw- root     root       1,   8 2010-02-10 06:09 random
    crw-rw-rw- root     root       1,   7 2010-02-10 06:09 full
    crw-rw-rw- root     root       1,   5 2010-02-10 06:09 zero
    crw-rw-rw- root     root       1,   3 2010-02-10 06:09 null
    crw------- root     root       1,   2 2010-02-10 06:09 kmem
    crw------- root     root       1,   1 2010-02-10 06:09 mem
    crw------- root     root     254,   0 2010-02-10 06:09 rtc0

自左至右,內容依次是設備屬性、設備所屬用戶、設備所屬組、設備主設備號、設備次設備號、日期、設備名稱。

在以上的設備節點中,主設備號為1的mem,null,zero,null等為Linux標準的內存設備(mem device)。主設備號為4和5的各個設備,分別為TTY終端設備,均為Linux標準的內容。

主設備號為10的各個設備,為Linux中的misc設備(雜項設備),psaux為標準的PS/2鼠標驅動,其中次設備號為53之后的各個設備如ashmem,binder等,為Android的特定設備。

另外,rtc,ttyS等為實時時鐘,串口驅動。

有一些設備在/dev/的次級目錄中,Android中幀緩沖(framebuffer)的設備節點沒有在標準的/dev/目錄中,而是在/dev/graphics目錄中,如下所示:

    # ls -l /dev/graphics
    crw-rw---- root     graphics  29,   0 2010-02-10 06:39 fb0

framebuffer設備的主設備號為29,次設備號根據設備數目依次生成。

目錄/dev/input中包含的是輸入設備的節點,如下所示:

    #  ls -l /dev/input
    crw-rw---- root     input     13,  64 2010-02-10 06:39 event0
    crw-rw---- root     input     13,  32 2010-02-10 06:39 mouse0
    crw-rw---- root     input     13,  63 2010-02-10 06:39 mice

輸入設備的主設備號為13,其中次設備號64之后的設備為Event設備。

目錄/dev/mtd中包含了mtd字符設備的節點,如下所示:

    # ls -l  /dev/mtd
    crw------- root     root      90,   5 2010-02-10 06:39 mtd2ro
    crw------- root     root      90,   4 2010-02-10 06:39 mtd2
    crw------- root     root      90,   3 2010-02-10 06:39 mtd1ro
    crw------- root     root      90,   2 2010-02-10 06:39 mtd1
    crw------- root     root      90,   1 2010-02-10 06:39 mtd0ro
    crw------- root     root      90,   0 2010-02-10 06:39 mtd0

mtd的字符設備的主設備號為90,一般包含一個只讀設備和一個可讀寫設備,

目錄/dev/log中為Android特有的log驅動程序,如下所示:

    # ls -l /dev/log
    crw-rw--w- root     log       10,  56 2010-02-10 06:39 radio
    crw-rw--w- root     log       10,  57 2010-02-10 06:39 events
    crw-rw--w- root     log       10,  58 2010-02-10 06:39 main

Android的log驅動同樣是misc驅動程序,包含3個次設備,main為主要log設備, event為事件的log設備,radio為Modem端的設備。

3.塊設備驅節點

塊設備使用隨機訪問的方式傳輸數據,并且數據總是具有固定大小的塊。為了提高數據傳輸效率,塊設備驅動程序內部采用塊緩沖技術。典型的塊設備如:光盤、硬盤、軟盤等。塊設備可以通過塊文件來訪問。

塊設備的操作在include/linux/fs.h中定義:

    struct block_device_operations {
        int (*open) (struct inode *, struct file *);
        int (*release) (struct inode *, struct file *);
        int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);
        long (*unlocked_ioctl) (struct file *, unsigned, unsigned long);
        long (*compat_ioctl) (struct file *, unsigned, unsigned long);
        int (*direct_access) (struct block_device *, sector_t,
                                void **, unsigned long *);
        int (*media_changed) (struct gendisk *);
        int (*revalidate_disk) (struct gendisk *);
        int (*getgeo)(struct block_device *, struct hd_geometry *);
        struct module *owner;
    };

塊設備的注冊和注銷函數:

    extern int register_blkdev(unsigned int, const char *);
    extern void unregister_blkdev(unsigned int, const char *);

塊設備驅動程序的結構如圖3-4所示。

圖3-4 塊設備驅動程序的結構

對于塊設備,由于也是設備節點,也可以使用文件接口來訪問。然而,根據Linux操作系統的一般使用方式,通常使用文件系統,而不是直接訪問塊設備的節點。

Android的塊設備在/dev/block目錄中,主要的內容如下所示:

    # ls -l /dev/block
    brw------- root     root      31,   2 2010-02-10 06:39 mtdblock2
    brw------- root     root      31,   1 2010-02-10 06:39 mtdblock1
    brw------- root     root      31,   0 2010-02-10 06:39 mtdblock0
    brw------- root     root       7,   3 2010-02-10 06:39 loop3
    brw------- root     root       7,   2 2010-02-10 06:39 loop2
    brw------- root     root       7,   1 2010-02-10 06:39 loop1
    brw------- root     root       7,   0 2010-02-10 06:39 loop0
    brw------- root     root       1,   2 2010-02-10 06:39 ram2
    brw------- root     root       1,   1 2010-02-10 06:39 ram1
    brw------- root     root       1,   0 2010-02-10 06:39 ram0
    brw------- root     root     179,   0 2010-02-10 06:39 mmcblk0

其中,主設備號為1的為各個內存塊設備,主設備號為7的為各個回環塊設備,主設備號為31的為mtd設備中的塊設備,mmcblk0為SD卡的塊設備。

塊設備的主要使用方式是文件系統。在Android中,可以使用mount命令查看系統中各個被掛接的文件系統,如下所示:

    # mount
    rootfs / rootfs ro 0 0
    tmpfs /dev tmpfs rw,mode=755 0 0
    devpts /dev/pts devpts rw,mode=600 0 0
    proc /proc proc rw 0 0
    sysfs /sys sysfs rw 0 0
    none /acct cgroup rw,cpuacct 0 0
    tmpfs /mnt/asec tmpfs rw,mode=755,gid=1000 0 0
    none /dev/cpuctl cgroup rw,cpu 0 0
    /dev/block/mtdblock0 /system yaffs2 ro 0 0
    /dev/block/mtdblock1 /data yaffs2 rw,nosuid,nodev 0 0
    /dev/block/mtdblock2 /cache yaffs2 rw,nosuid,nodev 0 0
    /dev/block/vold/179:0  /mnt/sdcard  vfat  rw,dirsync,nosuid,nodev,noexec,uid=1000,
gid=1015,fmask=0702,dmask=0702,allow_utime=0020,codepage=cp437,iocharset=iso8859-1,
shortname=mixed,utf8,errors=remount-ro 0 0
    /dev/block/vold/179:0  /mnt/secure/asec  vfat  rw,dirsync,nosuid,nodev,noexec,uid=
1000,gid=1015,fmask=0702,dmask=0702,allow_utime=0020,codepage=cp437,iocharset=iso88
59-1,shortname=mixed,utf8,errors=remount-ro 0 0
    tmpfs /mnt/sdcard/.android_secure tmpfs ro,size=0k,mode=000 0 0

根據以上的內容可見,/dev/block/mtdblock0、/dev/block/mtdblock1 和/dev/block/mtdblock2這3個mtd塊設備分別為掛接到/system、/data和/cache目錄中。在實際的系統中, MTD設備通常是基于Flash設備的,這些設備使用的是yaffs的文件系統。

/dev/block/vold/179:0設備實際上就是/dev/mmcblk0設備被vold程序使用的結果,它被掛接到/sdcard中,這在實際的系統中是由SD卡構建的,在仿真器的環境中,使用的是文件模擬的方式。它使用的是vfat格式的文件系統。

在Android系統中,同樣可以使用df查看系統中各個盤的情況:

    # df
    /dev: 47048K total, 0K used, 47048K available (block size 4096)
    /mnt/asec: 47048K total, 0K used, 47048K available (block size 4096)
    /system: 65536K total, 56876K used, 8660K available (block size 4096)
    /data: 65536K total, 22476K used, 43060K available (block size 4096)
    /cache: 65536K total, 1156K used, 64380K available (block size 4096)
    /mnt/sdcard: 64504K total, 1K used, 64502K available (block size 512)
    /mnt/secure/asec: 64504K total, 1K used, 64502K available (block size 512)

4.網絡設備

網絡設備是一種特殊的設備,與字符設備和塊設備不同,網絡設備并沒有文件系統的節點,也就是說網絡設備沒有設備文件。在Linux的網絡系統中,使用UNIX的socket機制。系統與驅動程序之間通過專有的數據結構進行訪問。系統內部支持數據的收發,對網絡設備的使用需要通過socket,而不是文件系統的節點。

網絡設備的核心是include/linux目錄中的頭文件netdevice.h中定義的struct net_device結構體。

網絡設備的注冊和注銷函數如下所示:

    extern int          register_netdev(struct net_device *dev);
    extern void        unregister_netdev(struct net_device *dev);

網絡驅動和字符設備驅動、塊設備的最大不同點是它沒有文件系統的節點。Linux網絡驅動程序的調用,要通過struct net_device數據結構。一般的網絡驅動程序不會由應用程序調用,而是由Linux內核的網絡模塊調用。用戶空間的程序通過socket等通用網絡接口,間接調用網絡驅動程序。網絡設備的驅動程序結構如圖3-5所示。

圖3-5 網絡設備的驅動程序結構

對于網絡設備的訪問,通常需要使用Socket(套接字)相關的幾個函數:socket(),bind (),listen (),accept(),connect()。

    #include <sys/types.h>
    #include <sys/socket.h>
        int socket (int family, int type, int protocol);
        int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
        int listen( int s, int backlog );
        int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
        int  connect( int  sockfd, const  struct  sockaddr  *serv_addr,
                  socklen_t addrlen );

其中socket()的返回類型也是一個文件描述符,和open返回的類型一樣,表示一個在進程中打開的文件。這個文件描述符也可以用通常的文件操作函數來操作。網絡中使用的參數與幾個主要結構體sockaddr、sockaddr_in、in_addr,和網絡的地址、協議類型等有關系。

在Android的仿真器的環境中,使用ifconfig命令可以查詢系統中的網絡設備,仿真器中具有以太網,因此可以使用如下的命令得到。

    # ifconfig eth0
    eth0: ip 10.0.2.15 mask 255.255.255.0 flags [up broadcast running multicast]

在實際的系統中,可能具有Wifi網絡和電話網絡,同樣可以使用ifconfig命令得到。

5.proc文件系統接口

proc文件系統常常被放置于Linux系統上的/proc目錄中。常常用于查看有關硬件、進程的狀態,可以通過操作proc文件系統進行控制工作。

在include/proc_fs.h中,定義創建proc文件系統的函數:

    extern struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
                                struct proc_dir_entry *parent);
    struct proc_dir_entry *proc_create_data(const char *name, mode_t mode,
                      struct proc_dir_entry *parent,
                      const struct file_operations *proc_fops,
                      void *data);
    extern void remove_proc_entry(const char *name, struct proc_dir_entry *parent);

proc_dir_entry結構表示一個目錄項的入口,定義如下所示:

    struct proc_dir_entry {
        unsigned int low_ino;
        unsigned short namelen;
        const char *name;
        mode_t mode;
        nlink_t nlink;
        uid_t uid;
        gid_t gid;
        loff_t size;
        const struct inode_operations *proc_iops;         /* 文件系統操作 */
        const struct file_operations *proc_fops;          /* 文件系統節點操作 */
        struct module *owner;
        struct proc_dir_entry *next, *parent, *subdir;
        void *data;
        read_proc_t *read_proc;
        write_proc_t *write_proc;
        atomic_t count;         /* use count */
        int pde_users;     /* number of callers into module in progress */
        spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
        struct completion *pde_unload_completion;
        struct list_head pde_openers;   /* who did ->open, but not ->release */
    };

其中讀、寫相關的兩個函數指針類型如下所示:

    typedef  int (read_proc_t)(char *page, char **start, off_t off,
                    int count, int *eof, void *data);
    typedef  int (write_proc_t)(struct file *file, const char __user *buffer,
                    unsigned long count, void *data);

在通常情況下,實現read_proc和write_proc可以分別實現對proc文件系統中的文件進行查看和控制的功能。

此外proc_dir_entry中包含了file_operations(文件操作)和inode_operations(文件系統節點操作)類型的結構,實現這兩個結構可以完成更加深入復雜的操作。事實上,可以使用proc實現和設備節點類似的功能。

Android系統對proc文件系統增加的內容不多。例如:ramconsole驅動可以利用proc中的文件查看系統的printk信息。

6.sys文件系統接口

sys文件系統是Linux內核中設計的較新的一種基于內存的文件系統,它的作用與proc有些類似,但除了與proc相同的具有查看和設定內核參數功能之外,還有為Linux統一設備模型作為管理之用。

sys文件系統只有讀和寫兩個接口,比較簡單,因此不能支持復雜的操作,例如:復雜的參數傳遞、大規模數據塊操作等。

sys文件系統被掛接到Linux文件系統的/sys目錄中,各個項目的內容如下所示。

/sys/block:系統中當前所有的塊設備所在。

/sys/bus:這是內核設備按總線類型分層放置的目錄結構,devices中的所有設備都連接于某種總線之下。

/sys/class:這是按照設備功能分類的設備模型。

/sys/devices:內核對系統中所有設備的分層次表達模型。

/sys/dev:字符設備和塊設備的主次號,鏈接到真正的設備(/sys/devices)中。

/sys/fs:按照設計是用于描述系統中所有文件系統。

/sys/kernel:內核所有可調整參數的位置。

/sys/module:系統中所有模塊的信息。

/sys/power:系統中電源選項。

可以通過sys文件系統操作其中的文件實現兩方面的功能:

顯示信息:可以通過cat命令。

控制:可以通過cat命令或者echo命令實現,使用重定向的方式輸入。

例如,在Linux標準的sys文件系統中,查看系統所支持的各種休眠模式的命令如下所示:

    $ cat /sys/power/state
    standby mem disk

控制進入休眠的命令如下所示:

    $ echo standby > /sys/power/state

sys文件系統的構建比較容易,主要使用include/linux/sysfs.h目錄中的__ATTR和__ATTR_RO宏來完成。

    #define __ATTR(_name,_mode,_show,_store) { \
            .attr = {.name = __stringify(_name), .mode = _mode },   \
            .show   = _show,                                          \
            .store  = _store,                                         \
    }
    #define __ATTR_RO(_name) { \
            .attr   = { .name = __stringify(_name), .mode = 0444 }, \
            .show   = _name##_show,                                   \
    }

創建sysfs中的文件,在內核中可以使用如下函數:

    int __must_check sysfs_create_file(struct kobject *kobj,
                                  const struct attribute *attr);

其中,在文件被讀取的時候將調用show接口,在文件被寫入的使用將調用store函數,其中字符串的內容,將有實現者解析和構成。

在Android系統中,一個重要的sys文件系統內容是/sys/power/中有關電源管理的內容,如下所示:

    #  ls -l /sys/power/
    -rw-rw---- radio    system       4096 2010-02-10 06:39 state
    -rw-rw---- radio    system       4096 2010-02-10 06:39 wake_lock
    -rw-rw---- radio    system       4096 2010-02-10 06:39 wake_unlock

wake_lock和wake_unlock為Android中特有屬性,用于控制系統是否可以睡眠。

7.無直接用戶空間接口

某些驅動程序只對Linux內核或者驅動程序的框架提供接口,不對用戶空間直接提供接口。

主站蜘蛛池模板: 黄石市| 清河县| 龙门县| 阳山县| 临汾市| 双峰县| 莒南县| 仁布县| 海宁市| 辽阳市| 南岸区| 乌鲁木齐市| 江油市| 肇源县| 确山县| 施甸县| 无极县| 龙胜| 怀柔区| 乐亭县| 甘泉县| 筠连县| 黄大仙区| 尖扎县| 海南省| 德保县| 榕江县| 普定县| 宕昌县| 乌兰察布市| 武乡县| 伊金霍洛旗| 陈巴尔虎旗| 延庆县| 刚察县| 虞城县| 金华市| 宁城县| 永宁县| 涪陵区| 乌兰浩特市|