Салимóненко Дмитрий Александрович

Операционные системы

Задание 1: Определение характеристик всех USB-устройств, подключенных к USB-интерфейсу в операционной системе Linux

(C, Linux)

Введение


В этом задании Вы потренируетесь исследовать USB-интерфейс (узнавать перечень устройств, подключенных к шине USB, а также определять основные их характеристики) в ОС Linux. Отметим сразу, что:

1) Программа, которую Вам предстоит разработать, будет являться очень простой и может служить лишь для учебных целей; она не является драйвером,

2) Соответственно, обращение к USB-интерфейсу, как таковому (т.е. на низком уровне) осуществляться НЕ будет. Мы воспользуемся уже готовыми результатами, т.е. специальными файлами, которые создает ОС. Файлами, в которых содержится информация о подключенных к USB-шине устройствах.


I. Теория

↑ К содержанию ↑

По сути, Вы, выполняя задание, немного расширите свой кругозор об ОС Linux, в рамках работы с устройствами USB (такими, как сканер, принтер, флешь, мышь и т.д.). Вообще, ОС Linux имеет ряд команд для просмотра характеристик и перечня USB-устройств. В частности, для просмотра списка USB-устройств служит команда
lsusb

Вот типичный результат данной команды:

Bus 001 Device 017: ID 04a9:2225 Canon, Inc. CanoScan LiDE 70
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 003: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Bus 002 Device 002: ID 0e0f:0003 VMware, Inc. Virtual Mouse
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub

Видим, что к шине под номером 001 подключено устройство под номером 17 (сканер), а также устройство под номером 001 (корневой хаб USB стандарта 2.0).

К шине 002 подключены три устройства: виртуальный USB хаб, созданный виртуальной машиной, а также виртуальная мышь и корневой хаб стандарта 1.1.

Примечание 1. А почему нет физической USB-мыши? Ответ: видимо, потому, что на моем компьютере ее физически нет: я использую мышь на порт PS/2 (при покупке компьютера специально выбирал материнскую плату, имеющую такие порты). А, кстати, зачем? Ответ банальный: чтобы меньше загружать шину USB, чтобы меньше тратилось времени на опрос этой шины. Чтобы выше была скорость работы ОС. Чтобы не зависала (на некоторое время) мышь при непредвиденных ситуациях на шине USB. Ну, и последний вопрос: а какие "непредвиденные" ситуации встречаются на USB-шине? Ответ: например, те, что исходят от некорректной работы интернет-модема.

Примечание 2.  Но, почему тогда нет модема? Ведь компьютер подключен к интернету через USB-порт, из ОС linux интернет также доступен. Вероятно, потому, что модем не примонтирован к Linux, с модемом непосредственно связана лишь основная ОС. А Linux работает с интернетом через локальную сеть (ибо эта ОС загружена через виртуальную машину). Если же примонтировать модем к виртуальной машине (путем подключения его, как удаляемого устройства из списка «Removable devices»), то он должен появиться в списке USB-устройств – в результатах вывода команды lsusb и ОС Linux будет считать, что работает не с локальной сетью, а с модемом «непосредственно».

Перечень типов устройств в Linux-Ubuntu находится в файле /proc/devices. Открыв этот файл, видим примерно следующее:

Character devices:
 1 mem
 4 /dev/vc/0
 4 tty
 4 ttyS
 5 /dev/tty
 5 /dev/console

136 pts
180 usb
189 usb_device
216 rfcomm

Block devices:
 1 ramdisk
 2 fd

135 sd
252 device-mapper
253 virtblk
254 mdp

Примечание 1. Примерно такое содержание указанного файла реализовано в Ubuntu 14.10, запущенной в виртуальной машине VMware Player у меня на компьютере.

Примечание 2. В других ОС Linux названия соответствующих каталогов, а также их содержание может быть иным. Так что если Вы решили делать задание не в Ubuntu – ориентируйтесь по ситуации.

Итак, видим, что USB-устройства имеют номер идентификатора, равный 189. Кроме того, видим, что USB-устройства являются типа char, т.е. символьными (байтовыми).

Примечание. Есть еще потоковые (блочные) устройства. Подумайте, чем различаются байтовые и блочные устройства – применительно к особенностям получения/отправки данных.

Смотрим каталог /sys/dev/char. Там находим каталоги с именами 189:*

В этих каталогах находятся файлы, связанные с соответствующими USB-устройствами (каждый из каталогов соответствует одному устройству). Таким образом, по количеству подобных каталогов можно судить о количестве USB-устройств (физических  и виртуальных). Путем перебора каталогов, открывая в них файлы вида /sys/dev/char/189:*/product, можно прочитать имя (название) устройства, например:
DataTraveler 2.0

(флешка).

Специальный файл USB-устройства, например, /dev/char/189:*, представляет собой файл – интерфейс драйвера ядра. который работает с USB-устройствами.

Каталог /dev/disk/by-uuid содержит имена дисков, подключенных к компьютеру, в частности, жесткого диска, флешки.

Вообще, для того, чтобы взаимодействовать с устройствами в ОС (в том числе и с USB-устройствами), необходимо наличие соответствующего драйвера. Последний может работать как на прикладном (пользовательском) уровне, так и на уровне ядра (привилегированном). Надо сказать, что в последнее время в Linux (да и в ряде других ОС) многие драйвера функционируют на прикладном (все же – выше ядра) уровне. Это облегчает их установку.

Существуют также готовые решения (в виде библиотек), которые позволяют осуществлять взаимодействие с USB-портами (и с устройствами, которые к этим портам подключены). К таковым относится, например, libusb. Если понадобится, Вы можете изучить эту библиотеку самостоятельно, вот, кстати, один из примеров ее применения. Однако, на занятиях мы ее применять не будем, так как это, на наш взгляд, будет неоправданным расширением изучаемого предмета "Операционные Системы".

Примечание. Если Вы задумаете поэкспериментировать с этой библиотекой самостоятельно, используйте для этих целей какую-нибудь ненужную флешь. Ибо, может статься, что Вы что-нибудь запишете на нее ТАК, что потом придется осуществлять операцию так называемой перепрошивки. Это так, на всякий случай.


II. Практическая часть

↑ К содержанию ↑

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

Вообще, написание драйвера – задача интересная, но, достаточно нетривиальная и едва ли доступная среднему студенту второго курса. Использование библиотек типа libusb - задача тоже интересная, но имеющая, скорее, технически-прикладное, чем образовательное значение. Поэтому мы с Вами ограничимся общим и, одновременно, более легким подходом – считыванием информации из специальных файлов, которые создает (ядро) OC Linux для каждого из USB-устройств. При подключении / отключении того или иного USB-устройства соответствующие файлы изменяются (появляются, исчезают, обновляются).

Вот простейший программный код, иллюстрирующий эту идею:

#include <stdio.h>   /* Стандартные объявления ввода/вывода */
#include <string.h>  /* Объявления строковых функций */
#include <unistd.h>  /* Объявления стандартных функций UNIX */
#include <fcntl.h>   /* Объявления управления файлами */
#include <errno.h>   /* Объявления кодов ошибок */
#include <sys/types.h>
#include <sys/stat.h>
// Возможно, часть библиотек являются лишней.
int fd; /* Файловый дескриптор */
char buf[512];/*размер зависит от размера строки принимаемых данных*/
int name;
int main(void)
{
fd = open("/sys/dev/char/189:16/product", O_RDONLY | O_NOCTTY | O_NDELAY);
// Вообще-то, параметры O_NOCTTY | O_NDELAY здесь не нужны, так как открывается обычный файл, не являющийся образом устройства. Эти параметры приведены справочно, для целей дальнейшей разработки возможности чтения данных с USB-устройства.
     if (fd == -1)
       {
         /*
          * Возвращает файловый дескриптор при успехе или -1 при ошибке.
          */
          perror("open_port: Unable to open /sys/dev/... - ");
       }
    else
       {
 name=read(fd,buf,80); /*чтение имени устройства, находящегося в USB- порту */
 printf("%s \n",buf);
       }
}


Как результат, программа выдаст название устройства, которому соответствует файл /sys/dev/char/189:16/product.

Примечание. Подумайте, а что произойдет, если в этот файл осуществить запись? Собственно, можете даже попробовать это реализовать.

Помимо названия, можно вывести и другие характеристики этого устройства, для чего следует использовать другие файлы, находящиеся в каталоге /sys/dev/char/189:16.

Кстати, если открыть специальный файл (устройства), в котором содержатся данные (например, информация, записанная на флеши), то можно прочитать из него или записать те или иные данные (при условии, конечно, что это устройство дает возможность реализовать операции чтения и/или записи). Правда, с записью следует поступать ОЧЕНЬ аккуратно: достаточно одного «смелого» байта, записанного, скажем, на флешь - и может произойти разрушение ее файловой системы и, соответственно, потеря данных, хранящихся на ней.


III. Самостоятельная разработка

↑ К содержанию ↑
  1. Реализовать перебор всех USB-устройств, подключенных в Linux. Для этой цели следует использовать не только каталог 189:16, но и все другие каталоги вида 189:*, находящиеся в каталоге /sys/dev/char/.
  2. Для каждого устройства вывести в консоль его название, а также какой-нибудь дополнительный параметр, например, максимальную скорость доступа. Для этого используйте соответствующий файл, где содержится такой параметр.
  3. Реализовать работу программы в резидентном режиме, т.е. чтобы опрос производился периодически (например, через 3 секунды) без необходимости ее перезапуска. Если в течение очередного интервала времени, равного 3 секунды, в Linux было подключено или отключено от него USB-устройство (одно или несколько), программа должна вывести соответствующее сообщение в консоль. Почему желательно НЕ выбирать слишком малое время, через которое производится опрос?
  4. Дополните программу следующей функцией. При исчезновении с шины USB устройства с определенным серийным номером программа делает 5 попыток поиска (в течение 5 секунд) этого устройства и, если все же не найдет его, дает команду на выключение компьютера. Для выключения компьютера можно воспользоваться, например, утилитами командной строки (консоли):
    • sudo shutdown -h now
    • sudo reboot -p
    • sudo halt
    • sudo poweroff

Вызвать утилиту командной строки в процессе выполнения программы можно путем реализации системного вызова одной из функций семейства exec.

Примечание. В Linux Ubuntu выполнение привилегированной команды (т.е. от имени администратора) осуществляется, насколько Вам известно, с использованием sudo. При этом ОС спрашивает пароль. Однако, делает она это только один раз – до тех пор, пока ОС не будет выгружена; после последующей загрузки ОС, при выполнении команд через sudo, вновь запросит пароль. Поэтому, чтобы программа срабатывала без участия пользователя, целесообразно выполнить любую операцию в ОС хотя бы один раз от имени администратора.


С уважением, Салимоненко Д.А.