Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

На главную -> MyLDP -> Электронные книги по ОС Linux
Цилюрик О.И. Модули ядра Linux
Назад Вперед

Операции I/O пространства пользователя

Ряд функций управления устройством может быть выполнен в пространстве пользователя. Случаем, в котором работа в пространстве пользователя может иметь смысл, является тот, когда вы начинаете иметь дело с новым и необычным оборудованием. Таким образом вы можете научиться управлять вашим оборудованием без риска подвешивания системы в целом. После того, как вы сделали это, выделение этого программного обеспечения в модуль ядра должно быть безболезненной операцией.

В качестве иллюстрации выполнения таких операций позаимствуем пример из [6] (архив user_io.tgz):

lab1_ioports.c :

	#include <stdio.h> 
	#include <unistd.h> 
	#include <sys/io.h> 
	#include <stdlib.h> 
	#include <fcntl.h> 
	#define PARPORT_BASE 0x378 
	
	int do_ioperm( unsigned long addr, unsigned long nports ) { 
	   unsigned char zero = 0, readout = 0; 
	   if( ioperm( addr, nports, 1 ) )
	      return EXIT_FAILURE; 
	   printf( "Writing: %6d  to %lx\n", zero, addr ); 
	   outb( zero, addr ); 
	   usleep( 1000 ); 
	   readout = inb( addr + 1 ); 
	   printf( "Reading: %6d from %lx\n", readout, addr + 1 ); 
	   if( ioperm( addr, nports, 0 ) )
	      return EXIT_FAILURE; 
	   return EXIT_SUCCESS; 
	} 
	
	int do_read_devport( unsigned long addr, unsigned long nports ) { 
	   unsigned char zero = 0, readout = 0; 
	   int fd; 
	   if( ( fd = open( "/dev/port", O_RDWR ) ) < 0 ) 
	      return EXIT_FAILURE; 
	   if( addr != lseek( fd, addr, SEEK_SET ) ) 
	      return EXIT_FAILURE; 
	   printf( "Writing: %6d  to  %lx\n", zero, addr ); 
	   write( fd, &zero, 1 ); 
	   usleep(1000); 
	   read( fd, &readout, 1 ); 
	   printf( "Reading: %6d from %lx\n", readout, addr + 1 ); 
	   close( fd ); 
	   return EXIT_SUCCESS; 
	} 
	
	int main( int argc, char *argv[] ) { 
	   unsigned long addr = PARPORT_BASE, nports = 2; 
	   if( argc > 1 ) 
	      addr = strtoul( argv[ 1 ], NULL, 0 ); 
	   if ( argc > 2 ) 
	      nports = atoi( argv[ 2 ] ); 
	   if( do_read_devport( addr, nports ) ) 
	      fprintf( stderr, "reading /dev/port method failed\n" ); 
	   if( do_ioperm( addr, nports ) )
	      fprintf( stderr, "ioperm method failed\n" ); 
	   return EXIT_SUCCESS; 
	} 

Программа выполняет операции записи/чтения, с небольшой задержкой друг за другом, по двум портам с последовательными адресами, причём, делается это двумя способами: через операции outb()/inb() и через устройство отображения портов /dev/port :

$ ./lab1_ioports

	reading /dev/port method failed 
	ioperm method failed 

$ sudo ./lab1_ioports

	Writing:      0  to  378 
	Reading:    120 from 379 
	Writing:      0  to  378 
	Reading:    120 from 379 

$ cat /proc/ioports

	...
	00f0-00ff : fpu 
	... 

$ sudo ./lab1_ioports 240

	Writing:      0  to  f0 
	Reading:    255 from f1 
	Writing:      0  to  f0 
	Reading:    255 from f1 

Из операций пространства пользователя, относящихся к вводу/выводу можно ещё отметить вызов:

	int ioperm( unsigned long from, unsigned long num, int turn_on ); 

- устанавливает биты привилегий для доступа к области портов ввода/вывода, где:

- from - начальный порт области;

- num - число портов в области;

- turn_on — разрешить (1) или запретить (0) привилегированные операции.

Этот вызов, главным образом, для x86 архитектуры, на большинстве других он будет возвращать ошибку. Таким образом можно изменить привилегии только для первых 0x3ff портов ввода/вывода, если нужно получить тот же результат для всех 65536 портов, нужно воспользоваться системным вызовом iopl(). Сменить уровень привилегий (вызывающей задачи) на специфицируемый уровень:

	#include <sys/io.h> 
	int iopl( int level ); 

Уровень привилегий обычного пользовательского процесса 0, уровень привилегий может быть задан от 0 до 3, иначе будет возвращена ошибка.

Естественно, что для получения привилегий процесс должен обладать правами root. После получения привилегий процесс может выполнять:

outb() / outw() / outl() - запись в указанный порт;

inb() / inw() / inl() - чтение из указанного порта;

Помимо возможности ввода/вывода, для таких пользовательских программ, как правило, нужно предотвратить выгрузку страниц программы на диск:

	#include <sys/mman.h> 
	int mlock( const void *addr, size_t len ); 
	int munlock( const void *addr, size_t len ); 
	int mlockall( int flags ); 
	int munlockall( void ); 

В наиболее нужном в этом качестве вызове mlockall(), параметр flags может быть :

MCL_CURRENT - локировать все страницы, которые на текущий момент отображены в адресное пространство процесса;

MCL_FUTURE - локировать все страницы, которые будут отображаться в будущем в адресное пространство процесса;


Предыдущий раздел: Оглавление Следующий раздел:
Сигналы   Модификация системных вызовов