# Linux内核开发示例

\[TOC]

## 内核驱动

### 结构体

* inode结构体

  inode结构体是用来在内核中表示一个设备的，用户层看不到这个结构体，我们需要初始化它内部的一个结构体cdev：

  ```
  struct cdev *pcdev_reversion;
  pcdev_reversion = cdev_alloc(); //inode 结构的一个结构体，在内核内部表示设备
  pcdev_reversion->owner = THIS_MODULE;
  pcdev_reversion->ops = &file_ops;
  cdev_add(pcdev_reversion, dev, number_reversion);
  ```

  如果我们没有用动态创建的方式来创建这个结构体，那么就需要使用cdev\_init来初始化它，这里cdev\_alloc在分配后就立即初始化了设备，所以我们就不用再次初始化了。

  注销此设备：

  ```
  cdev_del(pcdev_reversion);
  ```
* file结构体

  文件结构体是内核暴露给用户层的，用户层通过这个文件结构体来访问内核里的cdev设备：

  ```
  static struct file_operations file_ops = {
    .owner                 = THIS_MODULE,
    .open                 = open_reversion,
    .write                 = write_reversion,
    .read                 = read_reversion,
    .unlocked_ioctl     = ioctl_reversion
  };
  ```
* 设备号结构体dev\_t

  这个结构体是一个32位的整数，里面存放了设备的设备号，设备号是所有设备在内核中的标识，是唯一键，为了避免冲突，我们使用动态分配的方式来注册设备号：

  ```
        if(major_reversion){
        dev = MKDEV(major_reversion, minor_reversion);
        ret = register_chrdev_region(dev, number_reversion, REVERSION_NAME);
    } else {
        ret = alloc_chrdev_region(&dev, minor_reversion, number_reversion, REVERSION_NAME);
        major_reversion = MAJOR(dev);
    }
    if(ret < 0){
        printk(KERN_WARNING "reversion:can't get major %d\n", major_reversion);
        return ret;
    }
  ```

  释放设备号：

  ```
  unregister_chrdev_region(MKDEV(major_reversion, minor_reversion), number_reversion);//unregion device
  ```

### 文件操作

#### 读操作

```
ssize_t read_reversion(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
    if(copy_to_user(buf, "kernel", sizeof("kernel"))){
        printk(KERN_ERR "reversion:can't set date to user space\n");
        return EFAULT;
    }
    return 0;
}
```

#### 写操作

```
ssize_t write_reversion(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
    char auBuf[128] = {0};
    if(copy_from_user(auBuf, buf, count)){
        printk(KERN_ERR "reversion:can't get date form user space\n");
        return EFAULT;
    }
    printk(KERN_DEBUG "reversion:get data form user space:%s\n", auBuf);
    return 0;
}
```

#### 控制操作

```
long ioctl_reversion(struct file *filp, unsigned int cmd, unsigned long arg)
{
    return 0;
}
```

## 用户层代码

### 创建设备

我们在将驱动加载后，就可以在/proc/devices中看到设备的设备号了：

```
pct@ubuntu:~$ cat /proc/devices | grep reversion
246 reversion
```

然后我们根据这个设备号创建设备：

```
mknod [OPTION]... NAME TYPE [MAJOR MINOR]
```

首先获取设备号，用awk命令可获取到

```
pct@ubuntu:~$ cat /proc/devices | awk '{if($2=="reversion")print $1}' 
246
```

我们动态分配得到的是设备的主设备号，次设备号是我们代码里写的0，然后创建设备：

```
pct@ubuntu:~$ sudo mknod /dev/reversion c 246 0
[sudo] password for pct: 
pct@ubuntu:~$ ls /dev/reversion -l
crw-r--r-- 1 root root 246, 0 Jan  3 17:24 /dev/reversion
```

创建成功。 代码如下：

```
#define DEV "/dev/reversion"


int dev_file = 0;


int dev_open()
{
    if(access(DEV,F_OK)){
        FILE *fp = popen("cat /proc/devices | awk '{if($2==\"reversion\")print $1}'", "r");
        char auMajor[5] = {0};
        if(0 == fread(auMajor, 1, sizeof(auMajor), fp))
        {
            printf("fread error:%s\n", strerror(errno));
            return -1;
        }
        int iMajor = atoi(auMajor);
        printf("the device major is:%d\n", iMajor);

        char auCommand[256] = {0};
        snprintf(auCommand, sizeof(auCommand), "mknod /dev/reversion c %d 0", iMajor);
        printf("command:%s\n", auCommand);
        system(auCommand);
    }
    dev_file = open(DEV,O_RDWR);
    if(dev_file == -1){
        printf("open error:%s\n", strerror(errno));
        return -1;
    }


    return 0;
}
```

### 读取设备

读取设备的操作就和读取文件一样：

```
    char auRead[256] = {0};
    if(read(dev_file, auRead, sizeof(auRead)) == -1){
        printf("read error:%s\n", strerror(errno));
        return -1;
    }
    printf("read:%s\n", auRead);
```

返回值是我们在内核中写入的东西。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://plainchant.gitbook.io/plainchant/linux-kai-fa/linux-nei-he-kai-fa-shi-li.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
