Как
можно заметить с данными, адрес
которых передается в функцию, можно
работать только с помощью методов
get_user и put_user.
Реализация
политики безопасности
Чтобы
избежать ошибок при подключении
нескольких пользовательских процессов
к модулю ядра, а так же предотвратить
выгрузку модуля во время использования
необходимо реализовать политику безопасности.
Для
предотвращения использования модуля
двумя и более пользовательскими
приложениями введем счетчик подключений.
Счетчик будет увеличиваться на 1 при вызове
метода open и уменьшаться на единицу при
вызове метода close. При этом если счетчик
больше нуля необходимо запретить подключение
и вернуть ошибку.
- static int device_open(struct inode *inode, struct file *file)
- {
- if (Device_Open)
- return -EBUSY;
- Device_Open++;
- return SUCCESS;
- }
- static int device_release(struct inode *inode, struct file *file)
- {
- Device_Open--;
- return SUCCESS;
- }
Для
предотвращения выгрузки модуля во время
использования существуют стандартные
механизмы. Все что необходимо сделать
это вызвать методы try_get_module во время начала
использования модуля и put_module в конце
использования. Если произойдет ошибка
при вызове этих методов, модуль может
быть никогда не выгружен.
- static int device_open(struct inode *inode, struct file *file)
- {
- if (Device_Open)
- return -EBUSY;
- Device_Open++;
- try_module_get(THIS_MODULE);
- return SUCCESS;
- }
- static int device_release(struct inode *inode, struct file *file)
- {
- Device_Open--;
- module_put(THIS_MODULE);
- return SUCCESS;
- }
Организация
работы с модулем
ядра
Не
смотря на то что модуль полностью готов
и скомпилирован мы не можем его использовать
пока не будет создан файл устройства.
Для его создания вызовем в консоли стандартную
утилиту mknod. Подробно данный процесс описан
в главе «Создание файла устройства».
Заключение
В
ходе данной работы был разработан
модуль для ядра Linux 2.6.38 и пользовательское
приложение для демонстрации возможностей
работы модуля ядра. Были изучены основы
создания модулей и особенности работы
с файлами устройств.
Основные
сложности при выполнении данной
работы возникали с тем, что информация
по работе с модулями ядра быстро устаревает,
и приемы, которые работали в одной
версии, ядра уже не работают в другой.
Новые версии ядра Linux выходят очень
часто, и большая часть информации устаревает
еще до ее перевода на русский язык.
Библиографический
список
- Михаэль Кофлер
Linux. Полное руководство. – СПб.: Питер,
2011. – 800с.
- Роберт Лав Разработка
ядра Linux. – М.: Вильямс, 2008. – 448с.
- Ори Померанц
Ядро Linux. Программирование модулей. –
М.: КУДИЦ-Образ, 2002. – 112с.
- В. Б. Иванов Прикладное
программирование на С/С++. С нуля до мультимедийных
и сетевых приложений. – М.: Солон-пресс,
2008. – 240с.
- Arnold Robbins Bash Pocket
Reference. O'Reilly Media, 2010. – 132с.
Приложение
Структура
file_operations для ядра Linux 2.6.38
- 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 *);
- 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 *, 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 (*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 (*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);
- int (*setlease)(struct file *, long, struct file_lock **);
- long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t
len);
- };
Код
модуля ядра
- #include
<linux/module.h>
- #include
<linux/kernel.h>
- #include
<linux/fs.h>
- #include
<asm/uaccess.h>
- #include
"chardev.h"
- #define
SUCCESS 0
- #define
DEVICE_NAME "char_dev"
- #define
BUF_LEN 80
- static int Device_Open = 0;
- static int current_buffer;
- static int init_current_buffer(void);
- static char buf0[BUF_LEN], buf1[BUF_LEN], buf2[BUF_LEN], buf_temp[BUF_LEN];
- static char *Message_Ptr;
- static int device_open(struct inode *inode, struct file *file)
- {
- printk("device_open(%p)\n", file);
- if (Device_Open)
- return -EBUSY;
- Device_Open++;
- Message_Ptr = buf0;
- try_module_get(THIS_MODULE);
- return SUCCESS;
- }
- static int device_release(struct inode *inode, struct file *file)
- {
- printk("<1>device_release(%p,%p)\n", inode, file);
- Device_Open--;
- module_put(THIS_MODULE);
- return SUCCESS;
- }
- static ssize_t device_read(struct file *file, char __user * buffer, size_t
length, loff_t * offset)
- {
- int bytes_read = 0;
- printk("<1>device_read(%p,%p,%d)\n", file, buffer, length);
- if (*Message_Ptr == 0)
- return 0;
- while (length && *Message_Ptr) {
- put_user(*(Message_Ptr++), buffer++);
- length--;
- bytes_read++;
- }
- return bytes_read;
- }
- static ssize_t device_write(struct file *file,
-
const char __user * buffer, size_t length, loff_t * offset)
- {
- int i;
- printk("<1>device_write(%p,%s,%d)", file, buffer, length);
- switch (current_buffer) {
- case 0:
- for (i = 0; i < length && i < BUF_LEN; i++)
-
get_user(buf0[i], buffer + i);
- break;
- case 1:
- for (i = 0; i < length && i < BUF_LEN; i++)
-
get_user(buf1[i], buffer + i);
- break;
- case 2:
- for (i = 0; i < length && i < BUF_LEN; i++)
-
get_user(buf2[i], buffer + i);
- break;
- }
- init_current_buffer();
- return i;
- }
- int device_ioctl(struct inode *inode,
- struct file *file,
- unsigned int ioctl_num,
- unsigned long ioctl_param)
- {
- current_buffer = ioctl_num;
- init_current_buffer();
- printk("<1>set
buf num %d\n", ioctl_num);
- return SUCCESS;
- }
- static loff_t device_llseek(struct file *file, int loff_t)
- {
- init_current_buffer();
- Message_Ptr += loff_t;
- printk("<1>set
position %d\n", loff_t);
- return SUCCESS;
- }
- struct file_operations Fops = {
- .read = device_read,
- .write = device_write,
- .unlocked_ioctl = device_ioctl,
- .open = device_open,
- .release = device_release,
- .llseek = device_llseek
- };
- int init_module()
- {
- int ret_val;
- ret_val = register_chrdev(MAJOR_NUM, DEVICE_NAME, &Fops);
- if (ret_val < 0) {
-
printk("%s failed with %d\n",
-
"Sorry,
registering the character device ", ret_val);
- return ret_val;
- }
- printk("mknod
%s c %d 0\n", DEVICE_FILE_NAME, MAJOR_NUM);
- return 0;
- }
- static int init_current_buffer(){
- int i;
- switch (current_buffer) {
- case 0:
-
Message_Ptr = buf0;
- break;
- case 1:
-
Message_Ptr = buf1;
- break;
- case 2:
-
Message_Ptr = buf2;
- break;
- }
- return 0;
- }
- void cleanup_module()
- {
- int ret;
- unregister_chrdev(MAJOR_NUM, DEVICE_NAME);
- }