[Linux设备驱动第三版]小记 [1-3章]

来源:互联网 发布:bt4破解软件下载 编辑:程序博客网 时间:2024/04/30 12:49

Chapter 1:

Device drivers are distinct “black boxes” that make a particular piece of hardware respond to a well-defined internal programming interface; they hide completely the details of how the device works.

 

User activities are performed by means of a set of standardized calls that are independent of the specific driver; mapping those calls to device-specific operations that acton real hardware is then the role of the device driver.

 

the role of a device driver is providing mechanism, not policy.

 

write kernel code to access the hardware, but don’t force particular policies on the user, since different users have different needs.leaving all the issues about how to use the hardware to the applications.

 

Driver: it is a software layer that lies between the applications and the actual device.

 

Each piece of code that can be added to the kernel at runtime is called a module.

 

Each module is made up of object code(not linked into a complete executable) that can be dynamically linked to the running kernel by the insmod program and can be unlinked by the rmmod program.

 

Driver is generally classifiable as a char module, a block module, or a network module.

 

A character (char) device is one that can be accessed as a stream of bytes (like a file);Such a driver usually implements at least the open, close, read, and write system calls.

 

block devices are accessed by filesystem nodes in the /dev directory. A block device is a device (e.g., a disk) that can host a filesystem.

 

 A network interface is in charge of sending and receiving data packets, driven by the network subsystem of the kernel. A network driver knows nothing about individual connections; it only handles packets. Communication between the kernel and a network device driver is completely different from that used with char and block drivers.

 

In the official kernel distribution, only an authorized user can load modules; the system call init_module checks if the
invoking process is authorized to load a module into the kernel.

 

Any input received from user processes should be treated with great suspicion; never trust it unless you can verify it.

 

Be careful with uninitialized memory; any memory obtained from the kernel should be zeroed or otherwise initialized before being made available to a user process or device. Otherwise, information leakage (disclosure of data, passwords, etc.) could result.

 

a maliciously modified kernel could allow anyone to load a module, thus opening an unexpected back door via init_module.

 

the even-numbered kernel versions (i.e., 2.6.x) are the stable ones that are intended for general distribution. The odd versions (such as 2.7.x), on the contrary, are development snapshots and are quite ephemeral.

 

This book covers Version 2.6 of the kernel. Our focus has been to show all the features available to device driver writers in 2.6.10.

 

The central gathering point for Linux kernel developers is the linux-kernel mailing list.

 

 


 

Chapter 2:

Regardless of the origin of your kernel, building modules for 2.6.x requires that you have a configured and built kernel tree on your system. 2.6 modules are linked against object files found in the kernel source tree; the result is a more robust module loader, but also the requirement that those object files be available.

 

Kernel hackers typically keep a “sacrificial” system around for the purpose of testing new code.

 

MODULE_LICENSE is used to tell the kernel that this module bears a free license; without such a declaration, the kernel complains when the module is loaded.

 

The printk function is defined in the Linux kernel and made available to modules. The kernel needs its own printing function because it runs by itself, without the help of the C library.(after insmod has loaded it, the module is linked to
the kernel and can access the kernel’s public symbols)

 

The hard part of writing a module is understanding your device and how to maximize performance.

 

every kernel module just registers itself in order to serve future requests, and its initialization function terminates immediately. It’s as though the module were saying, “Here I am, and this is what I can do.”

 

the exit function of a module must carefully undo everything the init function built up, or the pieces remain around until the system is rebooted.

 

A module, is linked only to the kernel, and the only functions it can call are the ones exported by the kernel; there are no libraries to link to.

 

Because no library is linked to modules, source files should never include the usual header files, <stdarg.h> and very special situations being the only exceptions.

 

a kernel fault kills the current process at least, if not the whole system.

 

A module runs in kernel space, whereas applications run in user space.

 

most of the relevant headers live in include/linux and include/asm.

 

The role of the operating system, in practice, is to provide programs with a consistent view of the computer’s hardware.

 

Unix has two execution modes: kernel space and user space. diff: a. different privilege levels inherent in the two modes; b. each have its own memory mapping—its own address space.

 

Unix transfers execution from user space to kernel space whenever an application issues a system call or is suspended by a hardware interrupt. Usually a driver performs both the tasks.

 

concurrency: more than one of processes can be trying to use your driver at the same time. So Linux kernel code, including driver code, must be reentrant.

 

Kernel code can refer to the current process by accessing the global item current, defined in <asm/current.h>, which yields a pointer to struct task_struct, defined by <linux/sched.h>.

 

Applications must share that stack with the entire kernel-space call chain. Thus, it is never a good idea to declare large auto-matic variables. Try to allocate dynamically.

 

Pay attention to function names starting with a double underscore (__), they are generally a low-level component.

 

Kernel code cannot do floating point arithmetic.  the extra overhead is not worthwhile.

 

The files found in the Documentation/kbuild directory in the kernel source are required reading first.

 

insmod: the kernel doesn’t modify the module’s disk file, but rather an in-memory copy. The function sys_init_module allocates kernel memory to hold a module.

 

kernel source, system calls are prefixed with sys_.

 

modprobe, like insmod, loads a module into the kernel. It differs in that it will look at the module to be loaded to see whether it references any symbols that are not currently defined in the kernel. When modprobe finds those modules (which are needed by the module being loaded), it loads them into the kernel as well.

 

rmmod: Note that module removal fails if the kernel believes that the module is still in use.

 

The lsmod program produces a list of the modules currently loaded in the kernel, works by reading the /proc/modules.

 

Modules are strongly tied to the data structures and function prototypes defined in a particular kernel version.

 

As a general rule, code which is explicitly version (or platform)dependent should be hidden behind a low-level macro or function.

 

For a given module has been built against the proper kernel version,  the build process is to link your module against a file (called vermagic.o).

 

When a module is loaded, any symbol exported by the module becomes part of the kernel symbol table.

 

If your module needs to export symbols for other modules to use, use belows macros:

  EXPORT_SYMBOL(name);
  EXPORT_SYMBOL_GPL(name);

 

Symbols must be exported in the global part of the module’s file, outside of any function, because the macros expand to the declaration of a special-purpose variable that is expected to be accessible globally. ??

 

all module code has the following:
#include <linux/module.h> /* contains definitions of symbols and functions needed by loadable modules. */
#include <linux/init.h> /* specify your initialization and cleanup functions */

 

Most modules also include moduleparam.h to enable the passing of parameters to the module at load time.

 

Unless your module is explicitly marked as being under a free license recognized by the kernel, it is assumed to be proprietary, , and the kernel is “tainted” when the module is loaded.

 

The various MODULE_ declarations can appear anywhere within source file outside of a function. (End of file is good)

 

__init and __initdata for data used only during initialization.

 

The __exit modifier marks the code as being for module unload only (by causing the compiler to place it in a special ELF section). If your module does not define a cleanup function, the kernel does not allow it to be unloaded.

 

You really do want to take care to do the right thing when an initialization error occurs. Error recovery is sometimes best handled with the goto statement.

 

In the Linux kernel, error codes are negative numbers belonging to the set defined in <linux/errno.h>.

 

it is customary (but not usually mandatory)to unregister facilities in the reverse order used to register them.

 

always remember that some other part of the kernel can make use of any facility you register immediately after that registration has completed. Do not register any facility until all of your internal initialization needed to support that facility has been completed.

 

Parameters are declared with the module_param macro, which is defined in moduleparam.h. It takes three parameters: the name of the variable, its type, and a permissions mask to be used for an accompanying sysfs entry.

 

The module loader refuses to accept more values than will fit in the array.

 

Use S_IRUGO for a parameter that can be read by the world but cannot be changed; S_IRUGO|S_IWUSR allows root to change the parameter.

 

User space driver: One case in which working in user space might make sense is when you are beginning to deal with new and unusual hardware.

 

 


 

Chapter 3:

Each kind of device implemented by the module is referred to as a type.

 

All devices locate in /dev. Use command `ls -l`, char device is 'c' and block device is 'b'. major and minor device number is before the date.

 

Traditionally, the major number identifies the driver associated with the device.The minor number is used by the kernel to determine exactly which device is being referred to.

 

Within the kernel, the dev_t type (defined in <linux/types.h>) is used to hold device numbers—both the major and minor parts. 32-bit quantity with 12 bits set aside for the major number and 20 for the minor number.

 

for new drivers, we strongly suggest that you use dynamic allocation to obtain your major device number, rather than choosing a number randomly from the ones that are currently free.

 

Most of the fundamental driver operations involve three important kernel data structures, called file_operations(a collection of function pointers), file, and inode.

 

 __user: This annotation is noting that a pointer is a user-space address that cannot be directly dereferenced.

 

an asynchronous read—a read operation that might not complete before the function returns.

 

mmap is used to request a mapping of device memory to a process’s address space.

 

The file structure(defined in <linux/fs.h>) represents an open file. every open file in the system has an associated struct file in kernel space.

 

There can be numerous file structures representing multiple open descriptors on a single file, but they all point to a single inode structure.

 

struct cdev is the kernel’s internal structure that represents char devices.

 

as soon as cdev_add returns, your device is “live” and its operations can be called by the kernel. You should not call cdev_add until your driver is completely ready to handle operations on the device.

 

The close system call executes the release method only when the counter for the file structure drops to 0.

 

You should never pass anything to kfree that was not obtained from kmalloc. It is, however, legal to pass a NULL pointer to kfree.

 

user-space buffer pointer cannot be directly dereferenced by kernel code.

 

The user pages being addressed might not be currently present in memory, and the virtual memory sub-system can put the process to sleep while the page is being transferred into place.

 

The free command can be used to see how the amount of free memory. the strace utility to monitor the system calls issued by a program, together with their return values.

  


 

Problem:

1. printk(KERN_ALERT "Hello, world/n"); No display in terminal?

    try cat /var/log/messages | grep "Hello" or

    try dmesg | grep "Hello".

 

2. how to prepare kernel build tree?

    a. os has one if selected development tool when installing host os. Check directory: /usr/src/kernel/`uname -r`/build

    b. Or build a kernel tree from source code.

      1. download kernel version as host os from http://www.kernel.org/pub/linux/kernel/.

      2. deal with the vermagic problem: modify main Makefile's EXTRAVERSION item to the same of host os's.

          [src: http://tldp.org/LDP/lkmpg/2.6/html/x380.html ]

      3. make menuconfig, make.

 

3. modprobe not work?

    [root@sea ldd]# modprobe ./hello.ko
    FATAL: Module ./hello.ko not found.

    because modprobe looks only in the standard installed module directories.

 

4. a function marked __exit can be called only at module unload or system shutdown time; any other use is an error.??

 

5. major=$(awk "//$2= =/"$module/" {print //$1}" /proc/devices) in shell file??

 

6. container_of() 含义??

    原型:

    #define container_of(ptr, type, member) ({                      /
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    /
        (type *)( (char *)__mptr - offsetof(type,member) );})

    首先申明一个member类型的常指针__mptr, 赋为ptr. 其后减去member 在 type中的偏移量. 既为指向type的指针.(为什么不直接使用ptr?)

    附offsetof定义: #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

 

7. What's the purpose of "ln -sf ${device}0 /dev/${device}" in scull_load script?

 

8. What's the meaming? The chosen numbers are such that writing a single byte in scull consumes 8000 or
12,000 thousand bytes of memory: 4000 for the quantum and 4000 or 8000 for the
quantum set (according to whether a pointer is represented in 32 bits or 64 bits on
the target platform).

 

9. How to make sure module can install in local host machime?

    First choose a Linux kernel from web that the version is the same, then change EXTRAVERSION in main Makefile accordingly.

    it will affect -> include/linux/utsrelease.h -> vermagic.o.

 

10. How to play with scull device?

    a. first load the driver with script scull_load.

    b. play with below code and try to get the buffer status of scull through `cat /dev/scull0`.

#include <stdio.h>#include <stdlib.h>#include <string.h>int main(){    FILE *fp = NULL;    const char *str = "abcdefg\n", *dest = malloc(strlen(str));    if (NULL == (fp = fopen("/dev/scull0", "w+"))) {        printf("open fail.\n");        goto FAIL;    }       if (strlen(str) != fwrite(str, sizeof(char), strlen(str)/sizeof(char), fp)) {        printf("write failed.\n");        goto FAIL;     }       fseek(fp, 0, SEEK_SET);    if (strlen(str) != fread((void*)dest, sizeof(char), strlen(str)/sizeof(char), fp)) {        printf("read failed.\n");        goto FAIL;     }       if  (memcmp(str, dest, strlen(str))) {        printf("test failed.\n");        goto FAIL;    }       printf("test pass, dest: %s\n", dest);      free((void*)dest);     return 0;FAIL:    free((void*)dest);    return -1;          }

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>#include <string.h>#include <stdlib.h>#include <sys/ioctl.h>#define SCULL_IOC_MAGIC  'k'#define SCULL_IOCRESET    _IO(SCULL_IOC_MAGIC, 0)#define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC,  1, int)#define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC,  5, int)int main(){    int fd = -1;    const char *str = "abcdefg\n", *dest = malloc(strlen(str));    if (-1 == (fd = open("/dev/scull0", O_RDWR))) {        printf("open fail.\n");        goto FAIL;    }    if (strlen(str) != write(fd, str, strlen(str))) {        printf("write failed.\n");        goto FAIL;    }    lseek(fd, 0, SEEK_SET);    if (strlen(str) != read(fd, (void*)dest, strlen(str))) {        printf("read failed.\n");        goto FAIL;    }    if  (memcmp(str, dest, strlen(str))) {        printf("test failed.\n");        goto FAIL;    }    printf("test pass, dest: %s\n", dest);    free((void*)dest);    int quantum, tmp;    ioctl(fd, SCULL_IOCRESET); // reset     ioctl(fd, SCULL_IOCGQUANTUM, &quantum); // get default.    // printf("get quantum: %d\n", quantum);    tmp = quantum;    quantum++;    ioctl(fd, SCULL_IOCSQUANTUM, &quantum); // set to (default+1)    ioctl(fd, SCULL_IOCGQUANTUM, &quantum);    if (quantum != (tmp+1))        printf("test ioctl failed.\n");    else        printf("test ioctl success!\n");    close(fd);    return 0;FAIL:    free((void*)dest);    close(fd);    return -1;}


 


 

 

src:

http://lwn.net/Kernel/LDD3/                         ###ebook

http://oreilly.com.cn/codeexample/ldd3/      ###samplecode

http://www.cs.fsu.edu/~baker/devices/lxr/source/2.6.25/ldd-examples/    ###diff of sample code

 

原创粉丝点击