netlib.narod.ru | < Назад | Оглавление | Далее > |
В настоящем разделе рассматривается функция find_vma, которая будет применяться далее в этой главе, и вкратце описана тесно связанная с ней функция find_vma_prev. Это позволяет понять некоторые особенности обработки VMA и подготовиться к изучению описанного ниже кода.
33460: В общих чертах назначение функции find_vma состоит в поиске первой области VMA, содержащей заданный адрес. Вернее, ее назначение состоит в поиске первой области VMA, в которой значение vm_end превышает заданный адрес, но этот адрес все еще может находиться за пределами VMA, поскольку он может быть меньше значения vm_start этой области VMA. Функция возвращает указатель на VMA или NULL, если ни одна область VMA не соответствует запросу.
33468: Вначале проверяется та же область VMA, которая соответствовала последнему запросу к этому процессу, с использованием члена mmap_cache объекта mm, предназначенного специально для этой цели. Автор не проверял это сам, но в документации по этой функции указано, что коэффициент попадания в кэш составляет 35 процентов, а это весьма неплохой показатель, если учитывать то, что кэш состоит только из одной структуры VMA. Безусловно, этому способствует широко известное свойство, которое часто называют «локализацией ссылок»: согласно этому принципу программное обеспечение, как правило, обращается к данным (и командам), расположенным рядом с недавно использованными данными (и командами). Поскольку VMA содержит ряд смежных адресов, локализация ссылок способствует повышению вероятности того, что необходимые адреса будут находиться в той же области VMA, которая удовлетворяла предыдущему запросу.
При модификации списка VMA это значение кэша в нескольких других местах устанавливается в NULL для указания на то, что изменения в списке VMA могли сделать содержимое кэша недействительным. По крайней мере, в одном из этих случаев, в строке 33953, очистка кэша иногда может не потребоваться; сделав этот код немного интеллектуальнее, можно существенно повысить коэффициент попадания в кэш.
33471: Этот маленький кэш не применяется. Если дерево AVL не существует, функция find_vma просто проходит по всем структурам VMA в списке, возвращая первую из них, которая соответствует условию. Помните, что этот список структур VMA находится в отсортированном порядке, поэтому первая же область VMA, которая соответствует условию, представляет собой область VMA с наименьшим адресом, являющуюся таковой. Если функция выходит за конец списка, не найдя соответствия, переменная vma устанавливается в NULL и возвращается именно это значение.
33476: При наличии достаточно большого количества областей VMA прохождение по дереву становится быстрее, чем перебор связанного списка; поскольку деревья AVL сбалансированы, это операция с логарифмическими, а не линейными затратами времени. Итеративное прохождение по дереву встречается не так уж редко, но эта структура обладает некоторыми особенностями, которые не сразу бросаются в глаза. Прежде всего, обратите внимание на присваивание в строке 33484; в ней отслеживается наилучший узел, найденный до сих пор, поэтому будет возвращен именно он, если не нашлось ничего лучшего. Проверка if в следующей строке — это оптимизация для проверки того, лежит ли addr полностью в пределах VMA (мы уже знаем, что к этому моменту addr меньше значения vm_end для данной области VMA). Поскольку области VMA никогда не перекрываются, не существует другой, более подходящей области VMA, поэтому можно сразу же прекратить обход дерева.
33492: Если во время обхода дерева или просмотра списка была найдена область VMA, полученное значение сохраняется в кэше до следующего поиска.
33496: В любом случае возвращается значение vma; оно может быть равным NULL или может указывать на первую область VMA, которая удовлетворяет условию поиска.
Как было упомянуто в приведенном выше описании, эта функция (которая начинается со строки 33501) аналогична find_vma, но дополнительно возвращает указатель на область VMA, находящуюся перед той, где содержится искомое значение addr (если она существует). Эта функция интересует нас не сама по себе, а как источник дополнительной информации, необходимой для понимания особенностей программирования ядра в целом и программировании ядра Linux в частности.
Наиболее вероятно, что прикладной программист написал бы функцию find_vma как надстройку над более общей функцией find_vma_prev, просто отбросив указатель на предыдущую область VMA, следующим образом:
struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr) { struct vm_area_struct * notused; return find_vma_prev(mm, addr, ¬used); }
Прикладной программист сделал бы это потому, что быстродействие для приложений не имеет такого большого значения. В этой области программирования на передний план выходят иные соображения, которые продиктованы не стремлением к оригинальности, а неуклонным повышением быстродействия процессора: теперь можно позволить себе применять то здесь, то там вызов дополнительной функции в целях повышения удобства сопровождения.
В противоположность этому, программист ядра не может позволить себе роскошь выполнения дополнительного вызова функции; он скорее возьмет на себя ответственность за создание почти полного дубликата какой-то функции, чем потеряет несколько циклов процессора для ее вызова. Если даже исключить всё прочие соображения, именно такое отношение разработчиков ядра к своей работе является частью того, что позволяет прикладным программистам быть более расточительными.
Но есть более глубокая причина того, что в ядре Linux подобное дублирование менее важно по сравнению, скажем, с сопоставимой операционной системой с закрытым исходным кодом. Хотя ядро Linux должно ограничивать потребляемое им процессорное время, усилия разработчика ядра Linux не ограничены рабочим временем программиста, работающего по найму. Даже наиболее крупные коммерческие проекты разработки не могут сравниться по численности сотрудников с числом программистов, которые вносят свой вклад в создание Linux. (К тому же, автор не может удержаться от язвительного замечания: разработчики Linux не обязаны тратить свое время на бесконечные заседания, и они не связаны искусственными графиками.) Существование этой огромной команды, этого огромного коллективного мозга меняет правила разработки программного обеспечения.
Исходный код ядра Linux доступен каждому и сам Линус когда-то обронил знаменитую фразу: «... в мире столько внимательных глаз, что они обязательно найдут все ошибки». Если между реализациями функций find_vma и find_vma_prev возникнут какие-то важные расхождения, то разработчик ядра Linux, о существовании которого вы даже и не догадываетесь, найдет их и устранит быстрее, чем можно произнести слово «перетранслировать». На практике, разработка ядра системы Linux идет быстрее по сравнению с ее коммерческими аналогами, и результирующий код выполняется быстрее и содержит меньше ошибок, несмотря на наличие конструкций, которые при любых других обстоятельствах можно было бы считать нарушением принципов сопровождения кода.
Безусловно, автор может оказаться не прав, если просто никто не заметил, что можно объединить эти функции, и в следующем выпуске ядра одна из них исчезнет. Но это маловероятно, и даже если он окажется не прав в данном конкретном случае, автор настаивает, что в целом эта мысль справедлива. В этой операционной системе применяются несколько иные принципы, и именно благодаря таким отличиям Linux является столь оригинальной операционной системой.
netlib.narod.ru | < Назад | Оглавление | Далее > |