|
|
|
РАЗРАБОТКИ
|
Линукс модули
|
|
|
|
|
|
от PxL(22-02-2007)
рейтинг (52)
[ добре ]
[ зле ]
Вариант за отпечатване .::Linux драйвъри::.
.:: Кой? Къде? Защо?
Преди известно време бях много запален да напиша драйвър за ядрото на Linux.Известно ми беше горе-долу как се пише драйвър и как работи такъв, съответно имах и известни познания по C.Общо взето това би трябвало да е изискването за писане на драйвъри за устройства. Като цяло подобни материали не липсват в интернет, но не съм срещал на български.
.:: Нива на комуникация
Както предполагам е известно на повечето Linux потребители, устройствата там са представени като файлове, намират се в /dev часта...Отсъства портовата комуникация...? По-скоро комуникацията с физическите устройства се осъществява на две нива. Реалната комуникация с хардуерното устройството се съществява от т.нар. kernel space ниво, където ядрото и модулите му се грижат да предават информацията от и за физическите устройства към съответстващите им такива представени от файлове. Другата част се нарича user space, където апликациите комуникират с тези файлове.
.:: Kernel space / User space
Като начало можем да кажем накратко, че модулите са приложения, чиято идея е да се разшири и лесно да се добавя функционалност към ядрото. В зависимост от типа на ядрото (в конкретният случай визирам Линукс ядро) модулите могат да се добавят динамично, т.е. не е нужна прекомпилация и рестарт на самото ядро за да бъде добавен нов модул. По-специално ни интересуват т.нар. monolitic тип ядра, тъй като Linux ядрото е такъв тип. При monolitic типовете ядра (за разлика например от microkernel типовете) модулите използват паметта заделена за ядрото. Това означава, че всички глобални данни са видими за всички модули. Именно поради това писането на модули трябва да е много строго стандартизирано и е за предпочитане да се ползват static променливи..
Както споменах по-горе целта на драйвърите в kernel space е да свържат файловете на устройствата в /dev със самит ехардуерни устройства.Модулите в kernel space (LKM - Loadable Kernel Modul) обменят данни с потребителските апликации в user space чрез callback функции в ядрото.Представено визуално това би изглеждало така:
За да не объркам някого или себе си направо ще дам пример за прост kernel модул.
simple.c |
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Dimiter Todorov Dimitrov");
MODULE_DESCRIPTION("Simple kernel-space module");
MODULE_SUPPORTED_DEVICE("testdevice"); |
Както забелязвате използвам няколко макроса, чрез които определяме лиценз и информация за модула. Дефинирани са в module.h необходимост за всеки един kernel модул (vim /usr/include/linux/module.h).
За да компилираме ще ни трябва елементарен Makefile
Makefile |
KDIR:=/lib/modules/$(shell uname -r)/build
obj-m:=simple.o
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
$(RM) .*.cmd *.mod.c *.o *.ko -r .tmp* *.symvers |
Необходимо е за компилация да се ползва ядрото, в което ще заредите модула. Добавил съм и опция за clean след компилация.
Запазваме и компилираме:
Примерен код |
jorko tut $ make; ls
make -C /lib/modules/2.6.18/build SUBDIRS=/pxl/Devel/tut modules
make[1]: Entering directory `/usr/src/linux-2.6.18'
CC [M] /pxl/Devel/tut/simple.o
Building modules, stage 2.
MODPOST
CC /pxl/Devel/tut/simple.mod.o
LD [M] /pxl/Devel/tut/simple.ko
make[1]: Leaving directory `/usr/src/linux-2.6.18'
Makefile simple.c simple.mod.c simple.o
Module.symvers simple.ko simple.mod.o
jorko tut $ |
Компилираният модул simple.ko можем да заредим от user space в ядрото, чрез insmod, и да разгледаме с lsmod.
Примерен код |
jorko tut
jorko tut
jorko tut
simple 2176 0
jorko tut |
Mодула е зареден и работи в kernel space. Можем да го премахнем с rmmod.
.:: Device файлове
В горният пример създадохме модул, който дефакто не прави нищо освен да се зареди в kernel space. При зареждане на модули в повечето случай е необходимо да извършите операция, например инициализация на устройство, данни и т.н. За целта са предоставени две функций: module_init() и module_exit().
Нека разширим нашият пример:
Примерен код |
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Dimiter Todorov Dimitrov");
MODULE_DESCRIPTION("Simple kernel-space module");
MODULE_SUPPORTED_DEVICE("testdevice");
static int simple_init(void) {
printk("Simple module sayes: Hello!\n");
return 0;
}
static void simple_exit(void) {
printk("Simple module unloaded\n");
}
module_init(simple_init);
module_exit(simple_exit); |
Използваме callback функциите предоставени от ядрото, за да изпълним операция при зареждане и премахване на нашият модул. В случая ползвам kernel функцията printk, която запазва системни съобщения в системният лог. За да ги видим можем да ползваме dmesg. По аналогичен начин както първият път зареждам модула и търся за съобщенията в лог-а:
Примерен код |
jorko tut
jorko tut
Simple module sayes: Hello!
jorko tut
jorko tut
Simple module sayes: Hello!
Simple module unloaded
jorko tut |
.::Device файлове
Както споменах вече апликациите от user space комуникират с модулите в ядрото чрез файл-ове намиращи се в /dev частта. За всяко устройство предоставено в /dev отговаря съответен модул в ядрото. За да се /назначи/ даден модул към даден файл се използват уникални числа (желателно е те да са уникални, за да не се объркват модулите, но е възможно да се дублират, което би довело до най-различни /неприятни/ последствия). Тези числа са наречени major numbers, чрез тях ядрото знае кой модул за кой файл отговаря.Всяко устройство има и minor number, което не играе роля при ядрото, то е необходимо на самият модул да различава свойте устроства. Важно е да знаем, че тези устройства могат да са два вида: character и block.Главната разликата както можеби се досещате е, че block устройствата предават данните на отделни фиксирани в зависимост от типа на устройството блокове, докато character устройствата нямат фиксиран размер на предаваните данни. Ето един пример:
Примерен код |
jorko tut $ ls -la /dev/hda*
brw-rw---- 1 root disk 3, 0 Feb 18 13:18 /dev/hda
brw-rw---- 1 root disk 3, 1 Feb 18 13:18 /dev/hda1
brw-rw---- 1 root disk 3, 2 Feb 18 13:18 /dev/hda2
brw-rw---- 1 root disk 3, 5 Feb 18 13:18 /dev/hda5
brw-rw---- 1 root disk 3, 6 Feb 18 13:18 /dev/hda6
brw-rw---- 1 root disk 3, 7 Feb 18 13:18 /dev/hda7
brw-rw---- 1 root disk 3, 8 Feb 18 13:18 /dev/hda8 |
Виждате типа на устройството [color=blue]b[/color]rw-rw----. Както и неговите major и minor номера brw-rw---- 1 root disk [color=red]3[/color], [color=blue]8[/color] Feb 18 13:18 /dev/hda8
Можете да забележите от горният пример, че за тези устройства отговаря един модул, тъй като имат един и същи major номер. Съответно minor номерата са различни, за да може модулът да различава устройствата.
За да знаем кога процес се опитва да чете или пише в дадено устройство е необходимо да ползваме структура наречена file_operations, дефинирана в fs.h.
Примерен код |
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 (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
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 *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, 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 (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
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 (*dir_notify)(struct file *filp, unsigned long arg);
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);
}; |
Както сами забелязвате структурата предоставя доста указатели, като повечето от тях в случая няма да ни интересуват, така че не се стряскайте. За целта можем да ползваме GCC разширеният начин за дефиниране на тази структура:
Примерен код |
struct file_operations fops = {
read: device_read,
write: device_write,
open: device_open,
release: device_release
}; |
или C99 стандарта:
Примерен код |
struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
}; |
Недефинираните членове на структурата ще се дефинират автоматично от gcc като NULL.
В примера, ще използвам коментари директно, за да е по-разбираемо:
Примерен код |
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Dimiter Todorov Dimitrov");
MODULE_DESCRIPTION("Simple kernel-space module");
MODULE_SUPPORTED_DEVICE("testdevice");
static int simple_init(void);
static void simple_exit(void);
static int simple_open(struct inode *inode, struct file *file);
static int simple_release(struct inode *inode, struct file *file);
static ssize_t simple_read(struct file *filp, char *buffer, size_t length, loff_t *offset);
static ssize_t simple_write(struct file *filp, const char *buff, size_t len, loff_t *off);
module_init(simple_init);
module_exit(simple_exit);
struct file_operations fops = {
read: simple_read,
write: simple_write,
open: simple_open,
release: simple_release
};
static int Major;
static int Device_Open = 0;
static char msg[BUF_LEN];
static char *msgPtr;
static int simple_init(void) {
Major = register_chrdev(0, "simple", &fops);
if (Major < 0) {
printk ("Registering the character device failed with %d\n", Major);
return Major;
}
printk("Simple module loaded into kernel space!\n");
printk("Simple module has been assigned to Major number %d\n", Major);
printk("Create device file with: mknod /dev/simple c %d 0\n", Major);
return 0;
}
static void simple_exit(void) {
int ret = unregister_chrdev(Major, "simple");
if (ret < 0)
printk("Error in unregister_chrdev: %d\n", ret);
printk("Simple module unloaded successfuly!\n");
}
static int simple_open(struct inode *inode, struct file *file)
{
static int counter = 0;
if (Device_Open)
return -EBUSY;
Device_Open ++;
sprintf(msg,"You have accessed this device %d times!\n", counter++);
msgPtr = msg;
return 0;
}
static int simple_release(struct inode *inode, struct file *file)
{
Device_Open --;
return 0;
}
static ssize_t simple_read(struct file *filp, char *buffer, size_t length, loff_t *offset)
{
int bytes_read = 0;
if (*msgPtr == 0)
return 0;
while(length && *msgPtr)
{
put_user(*(msgPtr++), buffer++);
length --;
bytes_read ++;
}
return bytes_read;
}
static ssize_t simple_write(struct file *filp, const char *buff, size_t len, loff_t *off)
{
printk( "You don`t wanna write to me :)");
return -EINVAL;
} |
След като заредим модула ще трябва да създадем файл за устройството в /dev с mknod като му зададем Major number според това, какъв Major number ни е определен от ядрото
Примерен код |
jorko tut
jorko tut
Create device file with: mknod /dev/simple c 253 0
jorko tut
jorko tut
You have accessed this device 0 times!
jorko tut
You have accessed this device 1 times!
jorko tut
You have accessed this device 2 times!
jorko tut
You have accessed this device 3 times!
jorko tut
jorko tut
Simple module unloaded successfuly
jorko tut |
Вече знаем как да създадем модул и да го асоцираме с user space устройство.
Остана единствено момента, в който реално ще комуникираме с хардуер...
.:: Операции с хардуер
Линукс предоставя няколко функций за операции с хардуера. Преди да се зареди драйвър, подобен на горният той трябва да /резервира/ даден IO порт за ползване.Това се осъществява чрез функцията request_region изискваща номер на порт, обхват ако са повече от 1 входно-изходен порт и име на драйвъра. Преди нея обаче, за да се увери, че някой друг модул не ползва порта драйвъра трябва да извика check_region, изискваща два параметъра: номер на порт и дължина (1 за конкретен порт). След приключване на работа с външното у-во Драйъвъра трябва да освободи порт-а за да е възможно други драйвъри да работят с него, за целта се ползва release_region изискваща отново същите параметри. За комуникация с устройствата се ползват функции от библиотеката io.h (asm/io.h). Двете по-съществени функций за писане и четене от порт са: outb и inb
.:: Хардуерни прекъсвания
За да работи с хардуерни прекъсвания (IRQ - Interrupt Request), драйвърът ползва request_irq и
free_irq. Също можем да ползваме две функций за временно прекратяване на прекъсванията и съответно възстановяването им: cli и sti.
.:: DMA
DMA (Direct Memory Access) каналите ползват директно RAM паметта, без да налагат прекъсване на процесорната работа. За работа с DMA се използва библиотеката asm/dma.h. Устройствата ползващи DMA са обикновено прикрепени към дънната платка.
.:: КонклузИонето
Най-добрият начин да разберете нещо е да разгледате готов пример и да тествате.
п.с.: Тази статия е провокирана от основното соло в Sweet child o' mine на Guns 'n' Roses
Референций:
http://www.faqs.org/docs/kernel/
http://kernel.org/
Благодарско на @Angel от http://forums.bgdev.org/ за съветите относно kernel/user space графиката.
Димитър Т. Димитров PxL 2007
http://insecurebg.org/
pxl at insecurebg
--EOF
Съжалявам ако имам неточности,помагайте да ги оправим.
<< | Програмиране графичен интерфейс (GUI) с Lazarus и freepascal >>
|
|
|
|
|
"ий" От: NT На: 22-02-2007@11:31 GMT+2 Оценка: 1/Неутрален Нямам нищо против статията и/или автора. Не съм програмист, затова и няма да коментирам материята - не я разбирам. Не знам обаче кои са "Функций" и "Референций" ... звучат някак латински, но нямат нищо общо с понятията "Функции" и "Референции".
Поздрави !
[Отговори на този коментар]
Към: "ий" От: баш юзъра На: 23-02-2007@10:16 GMT+2 Оценка: 1/Неутраленмда, и на мен ми направи впечатление че се споменава за някой си Функций, Референций, Конфуций, Клавдий и т.н. пичове.
но това като че ли е единствената забележка относно правописа.
иначе материалът си заслужва четенето и е много информативен и поучителен. истински ми 'отвори' очите по темата и ми даде нови познания за едни интересни за мен неща.
[Отговори на този коментар]
Браво :) От: gat3way На: 22-02-2007@13:47 GMT+2 Оценка: 1/НеутраленПоздравления за статията, много добра е :)
За неточности в кода не мисля, по-скоро бих предложил две малки "подобрения" : първото е там където прехвърляш данни userspace<->kernelspace, мисля че е по-добре вместо цикъл, който прехвърля по един байт, след като знаеш големината на буфера (параметър на callback функцията - size_t length), направо да прехвърлиш наведнъж всичкото чрез copy_to_user() / copy_from_user(), които приемат като параметър и броят байтове. Така нещата според мен ще се ускорят особено ако прехвърляш големи обеми данни (напр. драйвер за някаква камера, от която вземаш изображения често). Другото, което не ми допада много е ползването на printk() без макросите за logging level. Повечето хора ползват klogd/syslogd и примерно искат да логват само определени съобщения от ядрото, не всички. Не че това е голям проблем де..
Иначе в сферата на простотията, зачудих се за кода свързан с отварянето на device-a, който събира в един фиксиран буфер с големина 64 байта "you have accessed blabla %d times\n". Добре си го замислил, хехе, малко по-дълго да беше съобщението и модулът щеше да е податлив на DoS (един userspace shit цикли отваряне и затваряне на devnode-a, като в един прекрасен момент след неизвестен брой време, особено ако може да е на 64-битова архитектура, заради по-големият int, overflow-ваш msg[BUF_LEN] - нямам идея обаче това до какъв точно резултат би довело, вероятно ще има паники :) ).
Да се върнем пак на статията, отново поздравления, наистина бях приятно изненадан :)
[Отговори на този коментар]
Към: Браво :) От: offs3t На: 23-02-2007@13:44 GMT+2 Оценка: 1/НеутраленAко вземем примера на gat3way с камерата, този изключително _обобщен_ пример не би свършил никаква работа. Липсват обичайните практики за този вид устройства:
* при отварянето и затварянето на устройството не се изполват struct inode *inode, struct file *filp (какво става ако имам повече от едно устройство ?)
* четенето и записа не са асинхронни (.aio_read, .aio_write, .lseek)
* няма kmem_cache_t *cache;
* ioctl имплементация
* mmap и т.н ...
Може би авторът е искал да запознае читателя с основните моменти, но като цяло системното програмиране си остава *адски* трудна работа изискваща много време опит и познания.
[Отговори на този коментар]
й От: hellfire <hellfire7 (a) gmail__dot__com> На: 22-02-2007@13:51 GMT+2 Оценка: 1/НеутраленВсичко е много добре, само не мога да разбера защо е толкова модерно в думи, които са в множествено число вместо и да се пише й ("Референций"). И обратно на места, където по принцип трябва да има й се пише и.
PS
По принцип не се заяждам за правописа, обаче този процес на заменяне на и с й и обратно го наблюдавам от доста време и този път доста ме изнерви.
[Отговори на този коментар]
Към: й От: kpp На: 22-02-2007@17:06 GMT+2 Оценка: 1/НеутраленИ не само "ий". Вижте това: "в повечето случай"
То бива неграмотност...
[Отговори на този коментар] Към: й От: plamen На: 22-02-2007@17:09 GMT+2 Оценка: 1/НеутраленСъщо така и: "в някои случай"
Наистина е отвратително изнервящо на където и да се обърнеш да се сблъскваш с такава неграмотност.
[Отговори на този коментар]
Към: Към: й От: PxL <x00 (a) abv__dot__bg> На: 22-02-2007@18:18 GMT+2 Оценка: 1/Неутрален Напълно съм съгласен, ще поправя грешките. Честно казано много пъти съм се усещал, че правя подобни грешки, но по-скоро това е провокирано от концентрацията в насока на техническата част и желанието да получа мнение за нея.
Благодаря за коментарите до тук! Наистина са ценни и ще се радвам на други допълнения и мнения.
[Отговори на този коментар]
udev От: growchie <growchie __@__ yahoo[ точка ]com> На: 22-02-2007@15:01 GMT+2 Оценка: 1/НеутраленКак стои положението с udev и създаването на устройства. mknod до колкото си спомням се ползваше при статични /dev директории.
[Отговори на този коментар] Някои идеи От: integer <coroot (a) gmail[ точка ]com> На: 22-02-2007@15:02 GMT+2 Оценка: 1/НеутраленИзвършването на файлови операции във kernelspace е опасно и може да доведе нестабилност на системата. Може би е добра идея да се използва Virtual Filesystem layer. Има доста generic object types, които могат да се използват.
1. file - include/linux/fs.h
2. inode - include/linux/fs.h
3. dentry - include/linux/dcache.h
4. superblock - include/linux/fs.h
[Отговори на този коментар]
Към: Някои идеи От: gat3way На: 22-02-2007@15:29 GMT+2 Оценка: 1/НеутраленТова не са "файлови операции", човекът просто си регистрира character device и си дефинира callback функциите за комуникация с него. Самият "файл" си го създава потребителя с mknod. Ядрото не създава никакви файлове в случая. Дори не би трябвало да има проблеми свързани с конкурентност, тъй като няма вариант 2 или повече процеса едновременно да комуникират с char device-a. Кодът си е абсолютно безопасен според мен, а и това е начина, по който работят всички останали character devices - в това число драйвери за терминали, sound карти, мишки, dummy драйвери като /dev/random или /dev/zero и т.н.
В един по-специален случай, ядрото дефакто "създава" файлове в procfs или sysfs, което също е често използвано явление и няма нищо страшно и опасно :)
[Отговори на този коментар]
браво От: аделин На: 22-02-2007@16:49 GMT+2 Оценка: 1/Неутраленся мое ли напишеш кернел модул за автоматично индексиране на файлове в директория;)
та още с меткането им да се отразява разликата:)
[Отговори на този коментар]
rlocate От: BlackByte <bbyte __@__ mail[ точка ]bg> На: 23-02-2007@7:51 GMT+2 Оценка: 1/Неутраленето ти нещо което прави това ... ама не съм го тествал :)
http://sourceforge.net/projects/rlocate/
[Отговори на този коментар]
/off От: Vladsun <vsmin__at__mail< dot >bg> На: 22-02-2007@20:32 GMT+2 Оценка: 1/НеутраленХм, PxL не те бях виждал тук до сега - добре дошъл от мен! :)
Коментарът ми е малко оффтопик - мисля, че ще си полезен и на още едно място в този сайт:
http://openfmi.net/plugins/scmsvn/cgi-b...
Поздрави!
Редактиран на: 22-02-2007@20:32
[Отговори на този коментар]
Към: /off От: PxL <x00__at__abv[ точка ]bg> На: 5-03-2007@14:42 GMT+2 Оценка: 1/НеутраленЗдрасти,
ще се радвам да помогна, даже в момента виждам един малък XSS пропуск :)
[Отговори на този коментар]
Към: Към: /off От: Vladsun <vsmin (a) mail__dot__bg> На: 21-03-2007@13:11 GMT+2 Оценка: 1/Неутрален:) Знаех си, че ще намериш нещо! ;)
Е, все пак дай да видим какво е - качи пач или пусни едно писъмце до администратора.
[Отговори на този коментар]
Има нужда от такива статии От: иво На: 23-02-2007@0:45 GMT+2 Оценка: 1/НеутраленПохвално е /и доста смело/ да се пишат
статии за Линукс ядрото.Действително има
доста информация в Интернет, но в огромната
си част,тя не е актуална и е на практика
безполезна.За сьжаление ,хората които пишат
ядрото ,нямат време за документацията.А
това което е "четливо" за Линукс и
компания ,не е достатьчно за нас ...
"Заравял" сьм се на няколко пьти в кода , и
ако незнаех от кого е писано ,щях да кажа:
или луд или гений е писал това!
Абсолютно опасни и хазартни техники / и
красиви / в огромен проект , вградения
асемблер , макросите ... , явно всичко това
е цената на спечелената микросекунда.
Казвам всичко това ,за да подкрепя автора:
има нужда да се побликуват статии от този
род.
като препорька бих казал : да се захващаме
по ясно и подробно изложение на по малки
части от кернела.В случея смятам пьрвата
част за добра : структурата на модула
и т.н.А втората ми се вижда излишна.Защото
разширява темата .Би било полезно /според
мен ,нямам претенции да сьм прав / да се
обясни как и кьде се зареждат модулите
какви прдимцтва и недостатьци имат и т.н.
А в друга статия да се продьлжи.Но моля ,не
премайте това за критика.
Друго ,което смятам че засяга всеки от
нас , това са описание на модулите в
ядрото.Хелпа е крайно недостатьчен,всеки
който компилира ядра , има нужда да направи
по-подробна справка за някой модул: Може ли
без него ,как се отразява на цялото
ядро ,кое го забавя и т.н.
И всичките тези статий би било добреа да са
написани в кратьк ,ясен справочен контекст.
Изчерпателно по вьпроса ,най-важното и
необходимото.За да са полезни.
[Отговори на този коментар] Абе ние българи ли сме или лукови глави !! От: Ivan Kostov <ikostov< at >gmail< dot >com> На: 23-02-2007@9:00 GMT+2 Оценка: 1/НеутраленЗдравейте,
съжалявам, че така ни в клин ни в ръкав пиша, но за пореден път ми прави впечатление, това че тук упорито се пише на шлюкавица. Се се надявах, че видните умове на информатиката, които се събират тук, могат да пишат що годе грамотно. Ако има някакъв проблем с кирилицата, кажете да помагаме ... срамота такъв хубав сайт да си иде заради една кирилица ...
[Отговори на този коментар]
Към: Абе ние българи ли сме или лукови гла От: Мирчо Мирев <mircho__at__linux-bg __точка__ org> На: 23-02-2007@10:04 GMT+2 Оценка: 1/НеутраленСайта заради това няма да си отиде. Обаче мисля, че той дава ясен пример, за това, какво се опитваме да правим - някой знае как се прави нещо и полага усилия да го покаже на другите. Коментарите, от които обикновено има полза, са тези, които допълват и подкрепят. "Ние българите" не върши обикновено работа.
[Отговори на този коментар] Към: Абе ние българи ли сме или лукови гла От: PxL <x00 (a) abv[ точка ]bg> На: 23-02-2007@10:21 GMT+2 Оценка: 1/Неутрален Здравей,
до колкото виждам няма коментари на /шлюкавица/. Съжалявам ако не съм те разбрал, нямам за цел да те обидя, но може би проблема е от възможността на сайт-а да конвертира кирилиските символи. Горе има меню, от където можеш да видиш всичко нормално.
Поздрави
[Отговори на този коментар]
Към: Към: Абе ние българи ли сме или луков От: gat3way На: 23-02-2007@10:34 GMT+2 Оценка: 1/НеутраленТака като гледам от 17 коментара уж по темата, 8 коментират единствено проблеми с правописни грешки или субективните виждания за правилно форматирани постове.
Според мен сме лукови глави, точно защото сме българи :)
[Отговори на този коментар] Kum: Kum: Kum: Abe nie bulgari li sme ili От: Ivan Kostov <ikostov__at__gmail __точка__ com> На: 24-02-2007@12:51 GMT+2 Оценка: 1/НеутраленСъжалявам ако сте ме разбрали погрешно. Радвам се много на такива хора, които отделят време за да опишат ясно и една сравнително непозната материя, като например писанто на драйвери за ядрото. Искам изрично да благодаря на авторя и. Просто исках да кажа, че ми беше трудно да я прочета заради "шльокавицата".
[Отговори на този коментар]
Добре е но може и още по-добре! От: БатСержо На: 19-03-2007@8:04 GMT+2 Оценка: 2/Градивна критика
Няма да започвам веднага с "градивната критика" и станалото напоследък доста популярно "стимулиращо не" :-) Статийката е готина и бих се радвал да виждам повече такива. Всъщност от доста време си мисля да направя нещо подобно, като разкажа с какви перипетии и странични "екстри" с съм се сблъсквал докато съм ръчкал модулчета за ядрото.
Та така след краткия увод следва критиката. Стимулиращото НЕ отива за пича твърдящ, че кода бил мега сигурен и отказо-устойчив. Както и за пича който се хвана за статичния буфер който в случая си е бял кахър в сравнение с коварните и не толкова очевидни състезателни условийца присъстващи в примера.
Сега тук е мястото да уточня че за примерна статийка нивото е не просто задоволително а направо ЗАДОВОЛЯВАЩО! Проблема за който говоря (съзтезателни условия - race conditions) лежи ей в тез няколко реда.
if (Device_Open)
return -EBUSY;
Device_Open ++;
Ако кода се използва със стари ядра върху еднопроцесорна бавна система проблем никакъв! Всичко ще си върви ок. Но върху ядра > 2.6 и по-бързи машини тук определено може да стане МАЗАЛО. Искам да попитам пича писал тази статия: Защо бе, защо не си ползвал mutex а? Защо са напрайли хората mutex-и ако няма да се ползват. Тъй като > 2.6 ядрата са напълно преемтивни (каквото и да значи това за хората който не знаят) то е напълно възможно един процес да реши да си отвори това устойство, тогава влиза във функцийката simple_open и стига да речем до реда Device_Open ++; и нека точно тогава процесът бъде прекъснат като друг получи процесорното време. Сега нека втория процес се опита да отвори устройството. Тъй като първия е бил прекъснат преди да увеличи Device_Open то втория ще успее. При връщане на управлението на втория процес той също вече е успял и така КЕ си имаме 2 процеса които са отворили устройството. Фъссс а такаааа. За хората който не стоплят за какво става въпрос да прочетат що е то пълна преемтивност + какво е big kernel lock. Някой може да си помислят сега че се заяждам, но я се замислете за някой "даровит" програмист който за такива неща въобще не мисли и шефа му е казал да направи еди си кво модулче за линукс ядрото. Ефекта в един момент ще бъде плачевен и хората като мен дето са се парили сега кашичка духат, щото няма нищо по странно от "забила" линукс машина която обаче продължава да си рутира.
Та така това са ми препоръките за разширяване на статията. Да се вмъкнат mutex-и да се покаже работа с листчета в ядтото ( има поне 3 предефинирани апи-та за това ). Защото си е сериозен проблем и неопитните програмисти лесно биха се предали пред бъг от такава величина.
[Отговори на този коментар]
Към: Добре е но може и още по-добре! От: gat3way На: 26-03-2007@10:21 GMT+2 Оценка: 1/НеутраленМисля, че това е малко пресилено все пак. В общия случай вероятността това да се случи е ниска, освен при положение че нямаш много процеси, които се бият да отварят device-a. Ако "употребата" се свежда както горе в статията, в няколко cat-вания на device-а, то тогава, шансът да настъпиш такава мотика, клони към 0.
И после, поне в този случай не мисля, че ще се случи нещо фатално, дори първият процес да се преемпт-не и вторият да се schedule-не точно тогава, така че и двата процеса да "държат" устройството отворено. Така както го виждам:
1) нека единият процес "затвори" device-a,а вторият чете оттам - мисля, че ще му се върне EINVAL, EIO или нещо от сорта, не вярвам че това ще "паникьоса" ядрото.
2) Нека единият го изчете, преемпт-не се преди да го "затвори" и вторият реши да чете - msgptr не се нулира или променя, така че няма да има драми с достъпване на NULL pointer-и, съответно краш-ванки на ядрото.
3) два процеса са "отворили" устройството, единият го затваря и докато вторият се мота, трети процес се опита да го отвори - Device_Open ще има стойност 1, така че няма да успее. Не е много ясно обаче дали вторият процес, като се опита да го "затвори", дали ще стигне дотам че да се намали Device_Open отново на 0 или директно няма да му се върне някаква грешка без въобще да се изпълни release функцията, която е дефинирана. В такъв случай май най-добре е при release-ване на устройството, онзи брояч да се сет-ва на 0, а не да се намаля с единица.
В крайна сметка, мисля че в най-лошият случай устройството ще стане недостъпно за отваряне и четене, поне докато модулът не се rmmod/insmod-не наново. Не мисля, поне в конкретния случай, че е възможно по някакъв начин да се краш-не ядрото поради concurrency драми.
Поне така си го представям де, може и да греша. Ти как мислиш?
[Отговори на този коментар]
|
|
|
|
|
|
|
|