Четверг, 2025-01-23, 1:20 AM
Статьи - HI - TECH
Приветствую Вас Гость | RSS
Главная страница Каталог статей Регистрация Вход
Меню сайта

Категории каталога
BIOS [9]
Статьи по BIOS
CD/DVD [6]
Статьи по CD/DVD
HDD - Жёсткие диски [8]
Статьи по HDD - жестким дискам
Модем [4]
Статьи по модемам
Блоки питания [5]
Статьи о блоках питания
Windows и общие вопросы [41]
Статьи по Windows и общим вопросам
Windows VISTA [8]
Статьи по Windos Vista
Linux [20]
Статьи по Linux

Наш опрос
Какой раздел нужно пополнить (создать) ?
Всего ответов: 100

Начало » Статьи » HI - TECH » Linux

The Linux Kernel Module Programming Guide
Глава 5. Файловая система /proc
5.1. Файловая система /proc: создание файлов, доступных для чтения
Linux предоставляет ядру и модулям ядра дополнительный механизм передачи информации заинтересованным в ней процессам -- это файловая система /proc. Первоначально она создавалась с целью получения сведений о процессах (отсюда такое название). Теперь она интенсивно используется и самим ядром, которому есть что сообщить! Например, /proc/modules -- список загруженных модулей, /proc/meminfo -- статистика использования памяти.

Методика работы с файловой системой /proc очень похожа на работу драйверов с файлами устройств: вы создаете структуру со всей необходимой информацией, включая указатели на функции-обработчики (в нашем случае имеется только один обработчик, который обслуживает чтение файла в /proc). Функция init_module регистрирует структуру, а cleanup_module отменяет регистрацию.

Основная причина, по которой используется proc_register_dynamic [6] состоит в том, что номер inode, для нашего файла, заранее неизвестен, поэтому мы даем возможность ядру определить его самостоятельно, чтобы предотвратить возможные конфликты. В обычных файловых системах, размещенных на диске, не в памяти, как /proc, inode указывает на то место в дисковом пространстве, где размещена индексная запись (index node, сокращенно -- inode) о файле. Inode содержит все необходимые сведения о файле, например права доступа, указатель на первый блок с содержимым файла.

Поскольку мы не предусматриваем обработку операций открытия/закрытия файла в файловой системе /proc, то нам некуда вставлять вызовы функций try_module_get и try_module_put. Если вдруг случится так, что модуль был выгружен в то время как соответствующий файл в /proc оставался открытым, к сожалению у нас не будет возможности избежать возможных последствий. В следующем разделе мы расскажем о довольно сложном, но достаточно гибком способе защиты от подобных ситуаций.

Пример 5-1. procfs.c

/*
* procfs.c - пример создания "файла" в /proc
*/

/* Необходимо для любого модуля */
#include <linux/module.h>
/* Все-таки мы работаем с ядром! */
#include <linux/kernel.h>
/* Необходимо для работы с файловой системой /proc */
#include <linux/proc_fs.h>

struct proc_dir_entry *Our_Proc_File;

/* Обработчик чтения из файла в /proc.
*
* Аргументы
* =========
* 1. Буфер с данными. Как его заполнить -- вы решаете сами
* 2. Указатель на указатель на строку символов.
* Если вы не желаете использовать буфер
* размещенный ядром.
* 3. Текущая позиция в файле
* 4. Размер буфера.
* 5. Признак конца файла, "1" == EOF.
* 6. Указатель на данные (необходим в случае единственного
* обработчика на несколько файлов в /proc)
*
* Порядок использования и возвращаемое значение
* =============================================
* Нулевое значение == "буфер пуст", т.е. "Конец файла".
* Отрицательное значение == код ошибки.
*
* Дополнительные сведения
* =======================
* Основные принципы реализации этой функции
* я почерпнул не из документации, а из исходных текстов
* модулей, выполняющих подобные действия.
* Меня интересовало использование
* поля get_info в структуре proc_dir_entry (Если вам это интересно
* то для поиска я пользовался утилитами find и grep),
* Интересующий меня пример я нашел в <kernel source
* directory>/fs/proc/array.c.
*
* Когда вам что-то непонятно, то лучше всего
* поискать примеры в исходных текстах ядра. В этом состоит
* огромное преимущество Linux перед другими ОС,
* так как нам доступны все исходные тексты, так что --
* пользуйтесь этим преимуществом!
*/
ssize_t
procfile_read(char *buffer,
char **buffer_location,
off_t offset, int buffer_length, int *eof, void *data)
{
printk(KERN_INFO "inside /proc/test : procfile_read\n");

int len = 0; /* Фактическое число байт */
static int count = 1;

/*
* Мы всегда должны выдавать имеющуюся информацию,
* если пользователь спрашивает -- мы должны ответить.
*
* Это очень важно, поскольку библиотечная функция read
* будет продолжать обращаться к системному вызову
* read до тех пор, пока ядро не ответит, что сведений больше нет
* или пока буфер не будет заполнен.
*/
if (offset > 0) {
printk(KERN_INFO "offset %d : /proc/test : procfile_read, \
wrote %d Bytes\n", (int)(offset), len);
*eof = 1;
return len;
}

/*
* Заполнить буфер и получить его размер
*/
len = sprintf(buffer,
"For the %d%s time, go away!\n", count,
(count % 100 > 10 && count % 100 < 14) ? "th" :
(count % 10 == 1) ? "st" :
(count % 10 == 2) ? "nd" :
(count % 10 == 3) ? "rd" : "th");
count++;

/*
* Вернуть размер буфера
*/
printk(KERN_INFO
"leaving /proc/test : procfile_read, wrote %d Bytes\n", len);
return len;
}

int init_module()
{
int rv = 0;
Our_Proc_File = create_proc_entry("test", 0644, NULL);
Our_Proc_File->read_proc = procfile_read;
Our_Proc_File->owner = THIS_MODULE;
Our_Proc_File->mode = S_IFREG | S_IRUGO;
Our_Proc_File->uid = 0;
Our_Proc_File->gid = 0;
Our_Proc_File->size = 37;

printk(KERN_INFO "Trying to create /proc/test:\n");

if (Our_Proc_File == NULL) {
rv = -ENOMEM;
remove_proc_entry("test", &proc_root);
printk(KERN_INFO "Error: Could not initialize /proc/test\n");
} else {
printk(KERN_INFO "Success!\n");
}

return rv;
}

void cleanup_module()
{
remove_proc_entry("test", &proc_root);
printk(KERN_INFO "/proc/test removed\n");
}

Пример 5-2. Makefile

obj-m += procfs.o

5.2. Файловая система /proc: создание файлов, доступных для записи
Пока мы знаем о двух способах получения информации от драйвера устройства: можно зарегистрировать драйвер и создать файл устройства, и создать файл в файловой системе /proc. Единственная проблема -- мы пока ничего не можем передать модулю ядра. Для начала попробуем организовать передачу данных модулю ядра посредством файловой системы /proc.

Поскольку файловая система /proc была написана, главным образом, для того чтобы получать данные от ядра, она не предусматривает специальных средств для записи данных в файлы. Структура proc_dir_entry не содержит указатель на функцию-обработчик записи. Поэтому, вместо того, чтобы писать в /proc напрямую, мы вынуждены будем использовать стандартный, для файловой системы, механизм.

Linux предусматривает возможность регистрации файловой системы. Так как каждая файловая система должна иметь собственные функции, для обработки inode и выполнять файловые операции, [7] то имеется специальная структура, которая хранит указатели на все необходимые функции-обработчики -- struct inode_operations, которая включает указатель на struct file_operations. Файловая система /proc, всякий раз, когда мы регистрируем новый файл, позволяет указать -- какая struct inode_operations будет использоваться для доступа к нему. В свою очередь, в этой структуре имеется указатель struct file_operations, а в ней уже находятся указатели на наши функции-обработчики.

Обратите внимание: стандартные понятия "чтение" и "запись", в ядре имеют противоположный смысл. Функции чтения используются для записи в файл, в то время как функции записи используются для чтения из файла. Причина в том, что понятия "чтение" и "запись" рассматриваются здесь с точки зрения пользователя: если процесс читает что-то из ядра -- ядро должно записать эти данные, если процесс пишет -- ядро должно прочитать то, что записано.

Еще один интересный момент -- функция module_permission. Она вызывается всякий раз, когда процесс пытается обратиться к файлу в файловой системе /proc, и принимает решение -- разрешить доступ к файлу или нет. На сегодняшний день, решение принимается только на основе выполняемой операции и UID процесса, но в принципе возможна и иная организация принятия решения, например, разрешать ли одновременный доступ к файлу нескольким процессам и пр..

Причина, по которой для копирования данных используются функции put_user и get_user, состоит в том, что процессы в Linux (по крайней мере в архитектуре Intel) исполняются в изолированных адресных пространствах, не пересекающихся с адресным пространством ядра. Это означает, что указатель, не содержит уникальный адрес физической памяти -- он хранит логический адрес в адресном пространстве процесса.

Единственное адресное пространство, доступное процессу -- это его собственное адресное пространство. Практически любой модуль ядра, должен иметь возможность обмена информацией с пользовательскими процессами. Однако, когда модуль ядра получает указатель на некий буфер, то адрес этого буфера находится в адресном пространстве процесса. Макрокоманды put_user и get_user позволяют обращаться к памяти процесса по указанному им адресу.

Пример 5-3. procfs.c

/*
* procfs.c - Пример создания файла в /proc,
* который доступен как на чтение, так и на запись.
*/
/* Необходимо для любого модуля */
#include <linux/module.h>
/* Все-таки мы работаем с ядром! */
#include <linux/kernel.h>
/* Необходимо для работы с файловой системой /proc */
#include <linux/proc_fs.h>
/* определения функций get_user и put_user */
#include <asm/uaccess.h>

/*
* Место хранения последнего принятого сообщения,
* которое будет выводиться в файл, чтобы показать, что
* модуль действительно может получать ввод от пользователя
*/
#define MESSAGE_LENGTH 80
static char Message[MESSAGE_LENGTH];
static struct proc_dir_entry *Our_Proc_File;

#define PROC_ENTRY_FILENAME "rw_test"

static ssize_t module_output(struct file *filp,
/* см. include/linux/fs.h */
char *buffer, /* буфер с данными */
size_t length, /* размер буфера */
loff_t * offset)
{
static int finished = 0;
int i;
char message[MESSAGE_LENGTH + 30];

/*
* Для индикации признака конца файла возвращается 0.
* Если этого не сделать, процесс будет продолжать
* пытаться читать из файла,
* угодив в бесконечный цикл.
*/
if (finished) {
finished = 0;
return 0;
}

/*
* Для передачи данных из пространства ядра
* в пространство пользователя
* следует использовать put_user.
* В обратном направлении -- get_user.
*/
sprintf(message, "Last input:%s", Message);
for (i = 0; i < length && message[i]; i++)
put_user(message[i], buffer + i);

/*
* Обратите внимание: в данной ситуации мы исходим из предположения,
* что размер сообщения меньше, чем len,
* в противном случае сообщение будт обрезано.
* В реальной ситуации, если длина сообщения больше чем
* len, то возвращается len, а остаток сообщения возвращается
* на последующих вызовах.
*/
finished = 1;

return i; /* Вернуть количество "прочитанных" байт */
}

static ssize_t
module_input(struct file *filp,
const char *buff, size_t len, loff_t * off)
{
int i;
/*
* Переместить данные, полученные от пользователя в буфер,
* который позднее будет выведен функцией module_output.
*/
for (i = 0; i < MESSAGE_LENGTH - 1 && i < len; i++)
get_user(Message[i], buff + i);

Message[i] = '\0'; /* Обычная строка, завершающаяся символом \0 */
return i;
}

/*
* Эта функция принимает решение о праве на выполнение операций с файлом
* 0 -- разрешено, ненулеое значение -- запрещено.
*
* Операции с файлом могут быть:
* 0 - Исполнениеe (не имеет смысла в нашей ситуации)
* 2 - Запись (передача от пользователя к модулю ядра)
* 4 - Чтение (передача от модуля ядра к пользователю)
*
* Эта функция проверяет права доступа к файлу
* Права, выводимые командой ls -l
* могут быть проигнорированы здесь.
*/

static int module_permission(struct inode *inode,
int op, struct nameidata *foo)
{
/*
* Позволим любому читать файл, но
* писать -- только root-у (uid 0)
*/
if (op == 4 || (op == 2 && current->euid == 0))
return 0;

/*
* Если что-то иное -- запретить доступ
*/
return -EACCES;
}

/*
* Файл открыт -- пока нам нет нужды беспокоиться о чем-то
* единственное, что нужно сделать -- это нарастить
* счетчик обращений к модулю.
*/
int module_open(struct inode *inode, struct file *file)
{
try_module_get(THIS_MODULE);
return 0;
}

/*
* Файл закрыт -- уменьшить счетчик обращений.
*/
int module_close(struct inode *inode, struct file *file)
{
module_put(THIS_MODULE);
return 0; /* все нормально! */
}

static struct file_operations File_Ops_4_Our_Proc_File = {
.read = module_output,
.write = module_input,
.open = module_open,
.release = module_close,
};

/*
* Операции над индексной записью нашего файла. Необходима
* для того, чтобы указать местоположение структуры
* file_operations нашего файла, а так же, чтобы задать адрес
* функции определения прав доступа к файлу. Здесь можно указать адреса
* других функций-обработчиков, но нас они не интересуют.
*/

static struct inode_operations Inode_Ops_4_Our_Proc_File = {
.permission = module_permission, /* проверка прав доступа */
};

/*
* Начальная и конечная функции модуля
*/
int init_module()
{
int rv = 0;
Our_Proc_File = create_proc_entry(PROC_ENTRY_FILENAME, 0644, NULL);
Our_Proc_File->owner = THIS_MODULE;
Our_Proc_File->proc_iops = &Inode_Ops_4_Our_Proc_File;
Our_Proc_File->proc_fops = &File_Ops_4_Our_Proc_File;
Our_Proc_File->mode = S_IFREG | S_IRUGO | S_IWUSR;
Our_Proc_File->uid = 0;
Our_Proc_File->gid = 0;
Our_Proc_File->size = 80;

if (Our_Proc_File == NULL) {
rv = -ENOMEM;
remove_proc_entry(PROC_ENTRY_FILENAME, &proc_root);
printk(KERN_INFO "Error: Could not initialize /proc/test\n");
}

return rv;
}

void cleanup_module()
{
remove_proc_entry(PROC_ENTRY_FILENAME, &proc_root);
}

Хотите еще примеры работы с файловой системой /proc? Хорошо, но имейте ввиду, ходят слухи, что /proc уходит в небытие и вместо нее следует использовать sysfs. Дополнительные сведения о файловой системе /proc вы найдете в linux/Documentation/DocBook/. Дайте команду make help, она выведет инструкции по созданию документации в различных форматах, например: make htmldocs.

Глава 6. Работа с файлами устройств
Файлы устройств представляют физические устройства. В большинстве своем, физические устройства используются как для вывода, так и для ввода, таким образом необходимо иметь некий механизм для передачи данных от процесса (через модуль ядра) к устройству. Один из вариантов -- открыть файл устройства и записать в него данные, точно так же, как в обычный файл. В следующем примере, операция записи реализуется функцией device_write.

Однако, этого не всегда бывает достаточно. Допустим, что у вас есть модем, подключенный к компьютеру через последовательный порт (это может быть и внутренний модем, с точки зрения CPU он "выглядит" как модем, связанный с последовательным портом). Естественное решение -- использовать файл устройства для передачи данных модему (это могут быть команды модема или данные, которые будут посланы в телефонную линию) и для чтения данных из модема (ответы модема на команды или данные, полученные из телефонной линии). Однако, это оставляет открытым вопрос о том, как взаимодействовать непосредственно с последовательным портом, например, как настроить скорость обмена.

Ответ: в Unix следует использовать специальную функцию с именем ioctl (сокращенно от Input Output ConTroL). Любое устройство может иметь свои команды ioctl, которые могут читать (для передачи данных от процесса ядру), писать (для передачи данных от ядра к процессу), и писать и читать, и ни то ни другое, [8] Функция ioctl вызывается с тремя параметрами: дескриптор файла устройства, номер ioctl и третий параметр, который имеет тип long, используется для передачи дополнительных аргументов. [9]

Номер ioctl содержит комбинацию бит, составляющих старший номер устройства, тип команды и тип дополнительного параметра. Обычно номер ioctl создается макроопределением (_IO, _IOR, _IOW или _IOWR, в зависимости от типа) в файле заголовка. Этот заголовочный должен подключаться директивой #include, к исходным файлам программы, которая использует ioctl для обмена данными с модулем. В примере, приводимом ниже, представлены файл заголовка chardev.h и программа, которая взаимодействует с модулем ioctl.c.

Если вы предполагаете использовать ioctl в ваших собственных модулях, то вам надлежит обратиться к файлу Documentation/ioctl-number.txt с тем, чтобы не "занять" зарегистрированные номера ioctl.

Пример 6-1. chardev.c

/*
* chardev.c - Пример создания символьного устройства
* доступного на запись/чтение
*/

#include <linux/module.h> /* Необходимо для любого модуля */
#include <linux/kernel.h> /* Все-таки мы работаем с ядром! */
#include <linux/fs.h>
#include <asm/uaccess.h> /* определения функций get_user и put_user */

#include "chardev.h"
#define SUCCESS 0
#define DEVICE_NAME "char_dev"
#define BUF_LEN 80

/*
* Устройство уже открыто? Используется для
* предотвращения конкурирующих запросов к устройству
*/
static int Device_Open = 0;

/*
* Ответ устройства на запрос
*/
static char Message[BUF_LEN];

/*
* Позиция в буфере.
* Используется в том случае, если сообщение оказывется длиннее
* чем размер буфера.
*/
static char *Message_Ptr;

/*
* Вызывается когда процесс пытается открыть файл устройства
*/
static int device_open(struct inode *inode, struct file *file)
{
#ifdef DEBUG
printk("device_open(%p)\n", file);
#endif

/*
* В каждый конкретный момент времени только
* один процесс может открыть файл устройства
*/
if (Device_Open)
return -EBUSY;

Device_Open++;
/*
* Инициализация сообщения
*/
Message_Ptr = Message;
try_module_get(THIS_MODULE);
return SUCCESS;
}

static int device_release(struct inode *inode, struct file *file)
{
#ifdef DEBUG
printk("device_release(%p,%p)\n", inode, file);
#endif

/*
* Теперь мы готовы принять запрос от другого процесса
*/
Device_Open--;

module_put(THIS_MODULE);
return SUCCESS;
}

/*
* Вызывается когда процесс, открывший файл устройства
* пытается считать из него данные.
*/
static ssize_t device_read(struct file *file, /* см. include/linux/fs.h*/
char __user * buffer, /* буфер для сообщения */
size_t length, /* размер буфера */
loff_t * offset)
{
/*
* Количество байт, фактически записанных в буфер
*/
int bytes_read = 0;

#ifdef DEBUG
printk("device_read(%p,%p,%d)\n", file, buffer, length);
#endif

/*
* Если достигнут конец сообщения -- вернуть 0
* (признак конца файла)
*/
if (*Message_Ptr == 0)
return 0;

/*
* Собственно запись данных в буфер
*/
while (length && *Message_Ptr) {

/*
* Поскольку буфер располагается в пространстве пользователя,
* обычное присвоение не сработает. Поэтому
* для записи данных используется put_user,
* которая копирует данные из пространства ядра
* в пространство пользователя.
*/
put_user(*(Message_Ptr++), buffer++);
length--;
bytes_read++;
}

#ifdef DEBUG
printk("Read %d bytes, %d left\n", bytes_read, length);
#endif

/*
* Вернуть количество байт, помещенных в буфер.
*/
return bytes_read;
}

/*
* Вызывается при попытке записи в файл устройства
*/
static ssize_t
device_write(struct file *file,
const char __user * buffer, size_t length, loff_t * offset)
{
int i;

#ifdef DEBUG
printk("device_write(%p,%s,%d)", file, buffer, length);
#endif

for (i = 0; i < length && i < BUF_LEN; i++)
get_user(Message[i], buffer + i);

Message_Ptr = Message;

/*
* Вернуть количество принятых байт
*/
return i;
}

/*
* Вызывается, когда процесс пытается
* выполнить операцию ioctl над файлом устройства.
* Кроме inode и структуры file функция получает
* два дополнительных параметра:
* номер ioctl и дополнительные аргументы.
*
*/
int device_ioctl(struct inode *inode, /* см. include/linux/fs.h */
struct file *file, /* то же самое */
unsigned int ioctl_num, /* номер и аргументы ioctl */
unsigned long ioctl_param)
{
int i;
char *temp;
char ch;

/*
* Реакция на различные команды ioctl
*/
switch (ioctl_num) {
case IOCTL_SET_MSG:
/*
* Принять указатель на сообщение (в пространстве пользователя)
* и переписать в буфер.
* Адрес которого задан в дополнительно аргументе.
*/
temp = (char *)ioctl_param;

/*
* Найти длину сообщения
*/
get_user(ch, temp);
for (i = 0; ch && i < BUF_LEN; i++, temp++)
get_user(ch, temp);

device_write(file, (char *)ioctl_param, i, 0);
break;

case IOCTL_GET_MSG:
/*
* Передать текущее сообщение вызывающему процессу -
* записать по указанному адресу.
*/
i = device_read(file, (char *)ioctl_param, 99, 0);

/*
* Вставить в буфер завершающий символ \0
*/
put_user('\0', (char *)ioctl_param + i);
break;

case IOCTL_GET_NTH_BYTE:
/*
* Этот вызов является вводом (ioctl_param) и
* выводом (возвращаемое значение функции) одновременно
*/
return Message[ioctl_param];
break;
}

return SUCCESS;
}

/* Объявлнеия */

/*
* В этой структуре хранятся адреса функций-обработчиков
* операций, производимых процессом над устройством.
* Поскольку указатель на эту структуру хранится в таблице устройств,
* она не может быть локальной для init_module.
* Отсутствующие указатели в структуре забиваются значением NULL.
*/
struct file_operations Fops = {
.read = device_read,
.write = device_write,
.ioctl = device_ioctl,
.open = device_open,
.release = device_release, /* оно же close */
};

/*
* Инициализация модуля - Регистрация символьного устройства
*/
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("%s The major device number is %d.\n",
"Registeration is a success", MAJOR_NUM);
printk("If you want to talk to the device driver,\n");
printk("you'll have to create a device file. \n");
printk("We suggest you use:\n");
printk("mknod %s c %d 0\n", DEVICE_FILE_NAME, MAJOR_NUM);
printk("The device file name is important, because\n");
printk("the ioctl program assumes that's the\n");
printk("file you'll use.\n");

return 0;
}

/*
* Завершение работы модуля - дерегистрация файла в /proc
*/
void cleanup_module()
{
int ret;

/*
* Дерегистрация устройства
*/
ret = unregister_chrdev(MAJOR_NUM, DEVICE_NAME);

/*
* Если обнаружена ошибка -- вывести сообщение
*/
if (ret < 0)
printk("Error in module_unregister_chrdev: %d\n", ret);
}

Пример 6-2. chardev.h

/*
* chardev.h - определения ioctl.
*
* Определения, которые здесь находятся,
* должны помещаться в заголовочный файл потому,
* что они потребуются как модулю ядра (chardev.c), так и
* вызывающему процессу (ioctl.c)
*/

#ifndef CHARDEV_H
#define CHARDEV_H

#include <linux/ioctl.h>

/*
* Старший номер устройства. В случае использования ioctl,
* мы уже лишены возможности воспользоваться динамическим номером,
* поскольку он должен быть известен заранее.
*/
#define MAJOR_NUM 100

/*
* Операция передачи сообщения драйверу устройства
*/
#define IOCTL_SET_MSG _IOR(MAJOR_NUM, 0, char *)
/*
* _IOR означает, что команда передает данные
* от пользовательского процесса к модулю ядра
*
* Первый аргумент, MAJOR_NUM -- старший номер устройства.
*
* Второй аргумент -- код команды
* (можно указать иное значение).
*
* Третий аргумент -- тип данных, передаваемых в ядро
*/

/*
* Операция получения сообщения от драйвера устройства
*/
#define IOCTL_GET_MSG _IOR(MAJOR_NUM, 1, char *)
/*
* Эта команда IOCTL используется для вывода данных.
* Нам по прежнему нужен буфер, размещенный в адресном пространстве
* вызывающего процесса, куда это сообщение должно быть переписано.
*/

/*
* Команда получения n-ного байта сообщения
*/
#define IOCTL_GET_NTH_BYTE _IOWR(MAJOR_NUM, 2, int)
/*
* Здесь команда IOCTL работает как на ввод, так и на вывод.
* Она принимает от пользователя номер байта (n),
* и возвращает n-ный байт сообщения (Message[n]).
*/

/*
* Имя файла устройства
*/
#define DEVICE_FILE_NAME "char_dev"

#endif

Пример 6-3. ioctl.c

/*
* ioctl.c - Пример программы, использующей
* ioctl для управления модулем ядра
*
* До сих пор мы ползовались командой cat,
* для передачи данных в/из модуля.
* Теперь же мы должны написать свою программу,
* которая использовала бы ioctl.
*/

/*
* Определения старшего номера устройства и коды операций ioctl
*/
#include "chardev.h"

#include <fcntl.h> /* open */
#include <unistd.h> /* exit */
#include <sys/ioctl.h> /* ioctl */

/*
* Функции работы с драйвером через ioctl
*/

ioctl_set_msg(int file_desc, char *message)
{
int ret_val;

ret_val = ioctl(file_desc, IOCTL_SET_MSG, message);

if (ret_val < 0) {
printf("Ошибка при вызове ioctl_set_msg: %d\n", ret_val);
exit(-1);
}
}

ioctl_get_msg(int file_desc)
{
int ret_val;
char message[100];

/*
* Внимание - ядро понятия не имеет -- какой длины буфер мы используем
* поэтому возможна ошибка, связанная с переполнением буфера.
* В реальных проектах вам необходимо предусмотреть
* передачу в ioctl двух дополнительных параметров:
* собственно буфера сообщения и его длину
*/
ret_val = ioctl(file_desc, IOCTL_GET_MSG, message);

if (ret_val < 0) {
printf("Ошибка при вызове ioctl_get_msg: %d\n", ret_val);
exit(-1);
}

printf("Получено сообщение (get_msg): %s\n", message);
}

ioctl_get_nth_byte(int file_desc)
{
int i;
char c;

printf("N-ный байт в сообщении (get_nth_byte): ");

i = 0;
while (c != 0) {
c = ioctl(file_desc, IOCTL_GET_NTH_BYTE, i++);

if (c < 0) {
printf
("Ошибка при вызове ioctl_get_nth_byte на %d-м байте.\n", i);
exit(-1);
}

putchar(c);
}
putchar('\n');
}

/*
* Main - Проверка работоспособности функции ioctl
*/
main()
{
int file_desc, ret_val;
char *msg = "Это сообщение передается через ioctl\n";

file_desc = open(DEVICE_FILE_NAME, 0);
if (file_desc < 0) {
printf("Невозможно открыть файл устройства: %s\n", DEVICE_FILE_NAME);
exit(-1);
}

ioctl_get_nth_byte(file_desc);
ioctl_get_msg(file_desc);
ioctl_set_msg(file_desc, msg);

close(file_desc);
}

Пример 6-4. Makefile

obj-m += chardev.o

Для облегчения сборки примера, предлагается скрипт, который выполнит эту работу за вас:

Пример 6-5. build.sh

#/bin/sh

# сборка пользовательского приложения
gcc -o ioctl ioctl.c

# создание файла устройства
mknod char_dev c 100 0

Категория: Linux | Добавил: webmaster (2006-12-15)
Просмотров: 437 | Рейтинг: 0.0 |

Всего комментариев: 0
Имя *:
Email *:
Код *:
Форма входа

Сервисы

Поиск по каталогу

Друзья сайта

| Ссылки 1 | Ссылки 2 | Ссылки 3 |
www.webmaster.clan.su Каталог+поисковая система be number one Bakililar.az Top Sites Сервис авто регистрации в
каталогах, статьи про раскрутку сайтов, web дизайн, flash, 
photoshop, хостинг, рассылки; форум, баннерная сеть, каталог 
сайтов, услуги продвижения и рекламы сайтов Скрипт для определения тиц (Яндекс CY: индекс цитирования). Определение pr (Google Pagerank). Проверить тиц pr сайта.
Copyright WebMaster.Clan © 2006 Бесплатный хостинг uCoz