Линуксы — это хорошо

# apt-get build-dep nginx
Reading package lists... Done
Building dependency tree
Reading state information... Done
Note, selecting 'liblua5.1-0-dev' instead of 'liblua5.1-dev'
The following packages will be REMOVED:
libgd2-xpm php5-gd

(c) [info]alexkuklin

Правильные конфигурационные файлы

Несколько соображений относительно того, как должны быть устроены конфигурационные файлы.

Во-первых, обязательно надо использовать xml-подобный синтаксис. Желательно глотать не любой файл с расширением xml, а только в одной из юникодных кодировок с добавлением трех «нечитаемых» байт в начале (это называется BOM, Byte Order Mark) — чтобы его нельзя было редактировать простыми текстовыми редакторами.

Во-вторых, я не зря сказал «подобный». Просто необходимо сделать результат чтения файла зависимым от наличия в нем табуляции (конечно, она дублирует xml, но…). При этом надо различать символ табуляции и четыре подряд стоящих пробела.

В третьих, для окончательного запутывания тех, кто решит «разбирать» эти файлы — сделайте набор команд в них полным по Тьюрингу.

Я ничего не забыл?

Собираем toolchain для MIPS

Как конченый китаефоб, я попросту не доверяю тем toolchain-ам, которые можно скачать с сайта Ingenic. А как вымирающий миниксовод, просто-таки обязан использовать для разработки Minix.

На самом деле, правда, я решил использовать ноутбук с миниксом как машину для разработки по одной простой причине — Minix 3 сейчас представляет собой довольно странную систему с точки зрения набора средств разработки. Например, make в миниксе — это вариация на темы BSD make, а входящий в Cygwin (под которым я хотел работать сначала) GNU make не понимает «позаимствованные» из NetBSD makefile-ы ядра Minix. Собрать BSD make в Cygwin у меня не получилось, там есть какие-то нюансы, так что я решил портированием миникса заниматься на нем самом.

Естественно, что фирма Ingenic не подозревает о существовании такой ОС и «готовый» toolchain для него не выкладывает. Тем хуже для них, потому как компилятор и утилиты для MIPS ничего особо страшного не представляют. Я решил действовать по инструкции с linux-mips.org. Пока что я собрал только binutils и GCC, перспективы использования отладчика пока туманные (так что все дальнейшее превращается в некую авантюру). Успокаиваю себя тем, что в Minix 3 приличного отладчика тоже никогда не было, и ничего, живут люди.

Итак, для начала скачиваем рекомендованные на сайте версии binutils (2-16.1) и GCC (3.4.4). Думаю, что binutils можно и посвежее, а вот с выбором GCC лучше не торопиться. То, что другие называют «бета-версией», разработчики компилятора GNU считают самостоятельным релизом. Например, в магистерской работе Ингмара Альтинга про портирование Minix 3 на PowerPC специально обращается внимание, что GCC 3.4.6 нормально компилирует MinixPPC, а 4 версия (4.x.x) жестко косячит.

Перед сборкой определим некоторые переменные окружения.

export WDIR=/tmp
export TARGET=mipsel-unknown-linux-gnu
export PREFIX=/usr/local/mipseltools
export PATH="${PATH}":${PREFIX}/bin

Говорят, что в качестве $TARGET можно безболезненно использовать сокращенную запись того же самого — а именно, mipsel-linux, но я не проверял. Вообще, эта строка строится по правилу архитектура-компания-ядро-ОС или иногда — архитектура-компания-ОС, например, mips-sgi-irix6.

Для начала собираем binutils. Тут все без сюрпризов. Распаковываем, конфигурируем, собираем, устанавливаем.

tar xjf binutils-2.16.1.tar.bz2
mkdir build-binutils && cd build-binutils
../binutils-2.16.1/configure --target=$TARGET --prefix=$PREFIX
gmake
gmake install
cd ..

Обращу внимание, что вместо make надо вызывать gmake, в случае с binutils все прокатывает, а с gcc возможны ошибки из-за различия синтаксиса Makefile-ов.

C gcc придется немного повозиться. Во-первых, configure неверно определяет возможности системы, во-вторых, встроенные типы Minix конфликтуют с некоторыми локальными для gcc определениями. Итак, распаковываем и конфигурируем.

tar xjf gcc-3.4.4.tar.bz2
mkdir build-gcc-bootstrap && cd build-gcc-bootstrap
../gcc-3.4.4/configure --target=$TARGET --prefix=$PREFIX --enable-languages=c --without-headers --with-gnu-ld --with-gnu-as --disable-shared --disable-threads

В файле gcc/auto-host.h убираем определения HAVE_GETRLIMIT и HAVE_MMAP_FILE. Кроме того, надо временно закомментировать в файле /usr/include/minix/types.h определение типа block_t. В одном из модулей gcc определяется собственный тип с таким же названием, не имеющий никакого отношения к блокам файловой системы Minix. После этого —

gmake
gmake install
cd ..

Работоспособность всей получившейся хрени можно проверить, собрав u-boot. Все очень просто — скачиваем его с VoGeeky (а можно, кстати, поставить git). Правда, придется поправить Makefile. Вместо конструкции

ifeq ($(ARCH),mips)
CROSS_COMPILE = mipsel-linux-
endif

пишем

ifeq ($(ARCH),mips)
CROSS_COMPILE = mipsel-unknown-linux-gnu-
endif

Теперь можно собирать (естественно, не забыв, что путь к тулчейну был добавлен в $PATH):

gmake volans_nand_config
gmake

После этого компилятор поработает-поработает и выплюнет вполне работоспособный загрузчик. Правда, придется перекинуть его на другую машину, чтобы засунуть в плату — дело в том, что в Minix то ли нет поддержки USB, то ли она в зачаточном состоянии. Соответственно, jzboot запустить не удастся. Ну а как перекидывать файлы — прекрасно написано в руководстве по сетевым возможностям Minix 3.

Про хаброидов

На днях видел на хабре две записи, можно либо смеяться, либо плакать.

Первая — «как правильно писать на Javascript». Первый же совет звучал примерно так:

Никогда-никогда не используйте eval()! Даже если очень надо — все равно не используйте! eval() позволяет порождать код, а это очень-очень нехорошо! Так делать нельзя, потому что так делать нельзя никогда!

В общем, аргументация была примерно на таком уровне — я только чуть-чуть преувеличиваю, «дабы дурость каждого видна была». Вопрос: почему порождать код нехорошо? Ответ: это нарушает привычную тем, кто всю жизнь писал на Паскале, «логику работы программы».

Доводя это до абсурда, скажу, что самые true-программисты никогда не используют операторы цикла, а обходятся только goto и if в виде

if(condition) goto somewhere;

Всякие бесполезные циклы нарушают логику работы программы, привычную тем, кто всю жизнь писал на FORTRAN-77.

Вторая — чувак для понтов реализовал в C++ вычисления, как он сам сказал, «с плавающей точкой» на этапе компиляции (с использованием шаблонов, разумеется). Я не буду придираться к тому, что на постсоветском пространстве вместо точки «плавает» запятая, потому как заголовок неправильный с самого начала. Число с плавающей запятой — это представление числа в виде мантиссы с фиксированным количеством знаков и порядка. У автора реализовано представление в виде отношения двух целых чисел, это несколько другое «решение». Обычно в программировании это называют «рациональными числами», не заботясь, правда, о полной корректности этой формулировки. Подходы эти принципиально разные и путать одно с другим не надо.

В общем, как кто-то заметил, «Русская Википедия — это деревенский кружок любителей истории». Хабр в таком случае — деревенский кружок любителей так называемых «информационных технологий».

Российского образования псто

От нечего делать занимаюсь изобретением велосипеда, то есть пишу программу, выполняющую триангуляцию Делоне некоторого множества точек. Да, я знаю о существовании Triangle, но изобретать велосипеды интереснее.

Как я уже говорил, описаний «делай раз, делай два» для триангуляции Делоне в Интернете практически нет. Есть книга А. В. Скворцова, есть несколько разной подробности описаний на сайтах американских университетов, например, из курса CS 294-5 Университета Беркли. Но я пишу этот пост не оттого, что хочу пожаловаться на жизнь — изобретателю велосипедов так делать нельзя, он сам себе злобный Буратино. Я хочу предложить уважаемой публике сравнить программу курса в Беркли с «Практикумом по компьютерной геометрии«, который с 2009 года включен в программу мехмата МГУ.

Согласитесь, что сравнение «практикума по Wolfram Mathematica» и очень серьезного курса в Беркли (предполагающего немалую самостоятельную работу) как-то не вяжется с представлением о «тупых американцах». Правда, компьютерной геометрии я не застал, а вот чему учат в «Практикуме на ЭВМ» на мехмате — рассказать могу. Предлагаю еще, для полного счастья, открыть пару хороших переводных учебников, типа SICP и Кормена и сравнить все происходящее.

Итак, на первом курсе сей предмет называется «Программирование и работа на ЭВМ» и предполагает сдачу двух зачетов — в осеннем и весеннем семестрах. Твердо установленной программы нет, все зависит от преподавателя, но подразумевается, что два сданных зачета доказывают умение студента писать программы на языке Си. Говорят, что иногда разрешают писать чуть ли не на Паскале. Занятия проходят попеременно в виде занятий за компьютерами и «обычных» семинаров, где препод рассказывает о том, что считает нужным. Например, А. Г. Кушниренко рассказывал про индуктивные функции, про некоторые нюансы языка Си типа арифметики указателей и правильного хранения двумерных матриц, а также — про своих (разумеется, тупых) американских студентов. Кроме того, он пугал нас тем, что на госэкзаменах мы забудем, что такое функция. К сожалению, мне на госах такой вопрос не задали, а я готовился :) Для получения зачета достаточно сдать три-четыре задачи из некоего списка (после этого преподаватель обычно убежден в том, что и остальные задачи студент сдаст, и больше не докапывается). Задачи простые (вот, например, список чего-то подобного), при минимальном знании Си решаются за полчаса каждая (и то время взято с большим запасом). У меня, например, от этого сложилось ложное впечатление, что и дальше все будет так просто.

На втором курсе было уже что-то похожее на вводный курс по Computer Science (с экзаменом в весеннем семестре и все тем же названием). Как всегда, было намешано все и сразу. В два семестра «у нас» запихнули четыре «американских» семестровых курса (вводный курс по C++, алгоритмы и структуры данных, формальные языки и грамматики, и понемногу, для общего знакомства — из курсов по операционным системам и компьютерным сетям). Все это прочитать за указанное время невозможно, поэтому реально программа несколько сокращается. Из лекций (их у нас читал В. В. Борисенко) я помню рассказ про машинную арифметику и стандарт IEEE 754, немного — про вызов функций с передачей параметров через стек в языке Си и ему подобных, было что-то про Register Transfer Language компилятора gcc, про «классические» алгоритмы и структуры данных, типа сортировок, разнообразных стеков, очередей и деревьев, кроме того, студентов развлекали байками о том, как горят в грозу сетевые карточки, и как отличить толстый Ethernet от тонкого. Формально для получения оценки нужно уметь решать задачи из опубликованного в Интернете списка и уметь отвечать на вопросы по программе, реально — надо решить по одной-две задачи из каждой темы или, допустим, найти ошибку в классе, реализующем арифметику произвольной точности (это приравнивается к решению соответствующей задачи). Этого достаточно для того, чтобы получить «пятерку» досрочно, а что происходит на основном экзамене — я не знаю. Говорят, что безбожно списывают, и положительные оценки получают даже полные «нули» по предмету.

В самом начале 2000-х на втором курсе пытались преподавать Java, но в итоге поимели кучу жалоб от студентов, от которых на третьем курсе внезапно потребовали знания Си или Фортрана. Борисенко говорил, что ничего «такого», что есть только в Си, использоваться не будет (а общее подмножество Java, Си и Фортрана осваивается в режиме «не приходя в сознание» за пять минут), но отечественный студент прекрасно умеет пользоваться фразой «а нас этому не учили!«.

Кстати, нельзя не упомянуть матчасть. Нет, это уже не старенький VAX, которому даже посвящен специальный сайт, сейчас мехмат полностью перешел на относительно современные персоналки. Ничего старше Pentium III я уже не застал. Гораздо интереснее софт. Стараниями К. Ю. Богачева на этих компьютерах стоит хитро настроенный Linux, запортить который студенты не могут. Чтобы жизнь малиной не казалась, задачи надо сдавать, используя хитро настроенный gcc (его еще называют «компилятор Богачева»). Там включена опция -Werror, приравнивающая предупреждения компилятора к ошибкам. В результате те, кто пишет дома в более лояльно настроенных компиляторах, сталкиваются с тем, что их программы просто не работают (в частности, компилятор матерно ругается на отсутствие пустой строки в конце файла, или на функцию main, возвращающую нецелое значение).

На третьем курсе начинается «Практикум на ЭВМ». Когда-то давно надо было писать на Фортране под СМ-4. Теперь, когда СМ сдана на цветмет, люди пишут на Си на тех же персоналках. Правда, говорят, что некоторые преподаватели принимают задачи и на Фортране. Не знаю, у нас этого никто не пробовал. Сей практикум растянут на четыре семестра, обо всем по порядку.

Третий курс в основном посвящен реализации алгоритмов из книжки «Методы решения линейных систем и нахождения собственных значений» все того же К. Ю. Богачева. В первом семестре надо написать две программы по 300-400 строк. В первой надо обратить матрицу размера около 1000×1000 (или решить аналогичных размеров линейную систему, это задачи равноценные), во второй — найти собственные значения той же матрицы. Пишутся они совершенно бездумно, простым переписыванием из книжки. Кстати, с точки зрения «справочника рецептов», написана она очень неплохо. Если преподаватель — не К. Ю. Богачев, то этого будет достаточно. Богачев требует несколько большего — а именно, ускорения этих методов в два-три раза. Правда, он еще и объясняет, как это сделать. После того, как сдана «практическая» часть — следует «теоретический» зачет. Всех студентов, сдавших программы, загоняют в аудиторию 14-08 и раздают письменное задание. Необходимо изложить три-четыре метода из книжки, если не полностью — то хотя бы в устраивающем Богачева объеме. Второй семестр — примерно то же самое, только книжка другая. На этот раз — «Методы приближения функций».

Я не буду говорить ничего про Н. Н. Ченцову, которая вела у нас эти занятия, желающие могут составить представление о ее методах обучения, почитав один из околомехматовских форумов.

Четвертый курс несколько забавнее. Например, такого исчерпывающего руководства по всем вопросам, как методички Богачева, больше нет. Кроме того, надо написать не только программу, но и отчет — связный текст на 5-6 страниц, описывающий метод решения и убеждающий преподавателя в его правильности. Конкретные задачи зависят от «препода». Например, кто-то вычисляет интегралы всякими там «методами трапеций» или решает методом Ньютона нелинейные уравнения. У И. С. Григорьева задачи поинтереснее — решение дифференциальных уравнений методом Рунге-Кутта с автоматическим выбором шага и применение принципа максимума Понтрягина в задачах оптимизации (то есть сведение задачи оптимизации, которую человечество в целом решать не умеет, к «решабельной» системе дифференциальных уравнений). Сам Илья Сергеевич регулярно выигрывает некие международные соревнования по решению задач оптимизации и вообще, любит эту тему. Правда, с точки зрения программирования тут все довольно просто и «концептуально» не отличается от, скажем, метода Ньютона решения тех же дифференциальных уравнений. Если имеется «рыба» отчета, то программа вместе с отчетом пишутся за 5-6 часов (а их надо две штуки). Эта простота развращает, но благодаря ей я все-таки увидел Е. А. Лапшина, которого «надо бояться«.

Во втором семестре так же незамысловато решаются уравнения в частных производных. Все еще проще. Собственно, происходящее на третьем и четвертом курсах не имеет никакого отношения к тому, что на Западе называют Computer Science. Это просто практикум по численным методам, но, надо сказать, неплохой — если повезет с преподавателем. У вышеназванной Ченцовой, например, интересных задач не будет — зато будет много геморроя со сдачей отчета. Сегодня она не хочет брать его в руки, потому как «жалко, такой чистый», а завтра будет — «противно, такой грязный». На программу она даже не смотрит, кое-кто даже сомневается в том, что она знает Си.

Кстати, четвертый курс занимается в так называемом «классе Эйч-Пи» (я специально не пишу HP, чтобы не путали с русскими буквами). Это аудитория во «Втором гуманитарном корпусе» (хотя гуманитарность его ставится под сомнение наличием там ВМК и оккупацией мехматом нескольких этажей), где когда-то давно стоял миникомпьютер HP-810 (возможно, название неправильное, так его обозвали на страничке «Компьютеры, которые выбираем не мы«) с пятнадцатью терминалами. Сейчас там стоят обычные персоналки с Windows XP производства, кажется, того же Hewlett-Packard. Так как студенты таскают программы на дискетках, перед тем, как засунуть что-то в компьютер, надо это что-то дать на проверку специально обученной тетеньке, сидящей у входа в класс и умеющей пользоваться Dr. Web с безбожно устаревшими базами и пасьянсом «Косынка». Как нетрудно догадаться, вся зараза первым делом попадает на компьютер с антивирусом, откуда разносится по классу. Систему там переустанавливают по три-четыре раза за семестр. Впрочем, пора бы заканчивать с ностальгическими воспоминаниями.

Если принять «интенсивность работы» в американских ВУЗах из «первого десятка» за эталон — то первый курс мехматовского «Программирования и работы на ЭВМ» — это чистое жевание соплей. Второй курс более-менее похож на что-то нормальное (какие-нибудь «Алгоритмы и структуры данных»), «Практикум на ЭВМ» третьего и четвертого курсов из четырех семестров мог бы сжаться до двух (и идти параллельно с курсом лекций по численным методам). В общем, из восьми семестров получается четыре без особых потерь в содержании (а если так уж хочется оставить восемь — можно добавить столько полезных вещей, типа нормальной вычислительной геометрии…). Вот только с подобной интенсивностью попросту не справится половина студентов, а отчислить столько даже Лапшин не сможет.

Я уже упоминал, почему в американских ВУЗах программа курса строится не по принципу «меньше тройки не получишь», а исходя из того, что может/хочет рассказать преподаватель. Они могут позволить себе такую роскошь, как заваливающие экзамен 3/4 слушателей, у нас этого просто не поймут. Зато троечникам напрягаться не надо.

UPD http://shura.luberetsky.ru/2011/08/05/neobkhodimye-poyasneniya/ — я отвечаю на некоторые вопросы.

Про рельеф

В продолжение вот этой задачки:

http://shura.luberetsky.ru/2011/07/19/algoritmicheskaya-zadacha/

Я реализовал алгоритм выбора точек, описанный в статье Systematic selection of Very Important Points from Digital Terrain Model for constructing Triangular Irregular Network без каких-либо изменений. Особо не заморачиваясь с производительностью, я добился того, что из миллиарда точек выбиралось порядка миллиона «очень важных» где-то минут за 40. Такое время меня вполне устраивало. Думаю, при желании можно сократить его в два-три раза, но делать этого я не буду, и вот почему.

Во-первых, модель с миллиардом точек оказалась бесполезной. Выяснилось (после выбора «очень важных точек»), что расположены они как-то странно — вдоль сторон и диагоналей квадратиков регулярной сетки. Оказывается, что кто-то взял за основу матрицу высот с большим шагом, преобразовал в триангуляционную модель (тривиальным образом, без выбора «важных точек»), а затем снова преобразовал в растр с меньшим в 10 раз шагом.

Во-вторых, чудес не бывает. Более-менее адекватное количество «важных точек» можно выбрать тогда, когда их количество меньше общего количества точек где-то на два порядка. Итого миллиард точек исходника даст 10 миллионов точек TIN, или около 20 миллионов треугольников. Это много.

Если «сложить» эти пункты, то можно преобразовать исходный миллиард точек в 10 миллионов (с которых все и началось), а затем сделать из них 100 000 «важных точек» — около 200 тысяч треугольников, сущая ерунда по нынешним временам.

Кстати, хочу поплакаться на жизнь. Пол-пятницы искал нормальное описание того, как алгоритмически сделать триангуляцию Делоне. В интернетах встречается описание алгоритма со сложностью O(N2) — последовательно добавляем точки к существующей триангуляции, при необходимости перестраивая ее. Каждый шаг в худшем случае требует перестроения всех треугольников, и это мне не нравится. Иногда в довольно общих чертах описывают алгоритм типа «divide and conquer» — разбиваем множество точек надвое, строим для подмножеств триангуляции, а затем объединяем их. Сложность, как можно показать, асимптотически оптимальная, O(N ln N). Описание того, как именно надо объединять, обычно отсутствует. В книге Препараты и Шеймоса подробно разбирается задача построения диаграммы Вороного — графа, двойственного триангуляции Делоне, и вскользь делается упоминание о том, что построив диаграмму Вороного за асимптотически оптимальное время O(N ln N), можно преобразовать ее в триангуляцию. Этот подход тоже мне не нравится, так как для построения диаграммы Вороного требуется выполнять несколько более сложные операции.

Конечно, я нашел относительно адекватное описание всего этого безобразия, но как-то странно — вроде задача довольно классическая, а описаний решения «для тупых» нет.

Алгоритмическая задача

Дано: есть матрица высот, то есть (грубо говоря) равномерная сетка точек на поверхности Земли, для каждой из которых приведена ее высота. Точек много, порядка миллиарда. Нужно построить TIN-модель (triangular irregular network), то есть сетку из треугольников с «абы где» расположенными вершинами, «похожую» на исходную матрицу высот. Треугольников должно быть много меньше — несколько десятков тысяч это максимум.

Стандартные алгоритмы построения TIN построены по общему принципу — для каждой точки матрицы высот (всего их N) вычисляется какая-то численная характеристика, связанная с локальными свойствами этой точки, в качестве вершин треугольников выбираются точки с максимальным значением этой характеристики (а их число обозначим M), а затем для построенных вершин строится триангуляция Делоне. Так вот, поговорим про выбор вершин этих треугольников.

В руководстве, откуда я взял это описание, предлагается сначала вычислить характеристику, а уж потом выбрать точки, где она принимает наибольшие значения. Если прикинуть расход памяти для такого действия, то становится очень грустно (не забываем о том, что у нас уже есть загруженная матрица высот). Хочется сделать это несколько более эффективно.

Легко понять, что функция f, выбирающая из последовательности x0, …, xn M максимальных элементов, является индуктивной, то есть f(x0, …, xn) зависит только от f(x0, …, xn-1) и xn. Можно записать это следующим образом: f(x0, …, xn) = F(f(x0, …, xn-1), xn). Индуктивная функция от последовательности длины N вычисляется за N шагов очень простым циклом, надо знать лишь значение f на пустой или одноэлементной последовательности и функцию F.

Очень просто написать функцию, находящую M «наибольших» элементов в N-элементной последовательности за O(N*M) с расходом памяти O(M) — выбранные элементы хранятся в массиве, каждый новый элемент сравнивается со всеми выбранными до этого. Если включить мозг, то можно улучшить оценку до O(N*log M) с той же оценкой расхода памяти (храним выбранные M элементов в самобалансирующемся дереве). А можно ли сделать это еще быстрее, возможно, отказавшись от подхода с индуктивными функциями? Я ответа пока что не знаю.

Vogue, U-Boot и дисплей

Сумел добавить поддержку дисплея в U-Boot для вогоплеера. Подробности потом, а пока объясните, как пользоваться git :)

Кстати, оценил то, как написан U-Boot и китайские дополнения к нему. Разобраться с тем, что и как наворотили наши узкоглазые друзья — еще та задачка.

Нашел багу

Немного поковырял китайский U-Boot, адаптированный для процессоров Ingenic. Помните, я писал, что там некорректно отрабатывает утилита bmp_logo?

Так вот, дело — в типично китайском коде. Как вы думаете, что напечатает программа?

int main(void){
    int l;
    FILE *in;
/* skipped some code */
    fread(&l, sizeof(uint16_t), 1, in);
    printf("l = %d\n", l);
/* skipped some code */
    return 0;
}

Естественно, что на всех более-менее современных машинах sizeof(int) > sizeof(uint16_t), то есть переменная l окажется просто неопределена — проинициализируются только первые два байта. На little-endian системах программа будет «корректно» работать, если l «автоматически» инициализируется нулем.

Несмотря на то, что в стандарте C ничего не сказано про инициализацию нулем локальных переменных, иногда такое происходит. Когда ОС выделяет память для новой программы, назначенные для нее страницы обнуляются — чтобы никакие вирусы-трояны не искали там логины-пароли. Как бы не было больно знатокам стандарта языка C, main — далеко не та функция, с которой начинается выполнение программы. Сначала вызывается библиотечная функция _start или аналогичная ей по назначению, специфичная для каждого компилятора и инициализирующая необходимые для работы стандартной библиотеки вещи (подумайте, например, как будет работать без инициализации malloc()).

Так вот, в Linux-системах можно надеяться на то, что локальные для main переменные окажутся на еще нетронутой части стека. Происходящее в Windows — гораздо более неясно, но там локальные переменные main «инициализируются» чем-то непонятным.

Кто виноват — ясно. А что делать? Нет, не надо писать int l = 0;. Хочу лишний раз напомнить о существовании big-endian систем. В них после вызова fread() шестнадцатибитное значение запишется не в два младших, а в два старших байта — и нетрудно догадаться, что мы хотели несколько другого.

К сожалению, описанные у Кернигана и Ритчи типы int, short и long имеют нерегламентированную длину. Гарантируются какие-то минимальные значения, и ничего более. int может запросто оказаться двух-, четырех- и даже восьмибайтовым. И как тут жить? Керниган и Ритчи придумали один возможный подход. Например, во всех Unix-системах принято, что «время» — это 32-битовое целое без знака. В time.h с помощью typedef определяется тип time_t, который в реальности может быть int (на 32-битных машинах) или long (на 16-битных). На 64-битной экзотике он вполне может оказаться и short. Определено несколько таких «стандартных типов» — и все.

Естественно, если подходить таким образом, то очень скоро мы начнем определять типы наподобие bmp_file_data_length_t — целое, соответствующее тому типу, в который помещается «длина данных» из bmp-файла. Не очень весело, правда? В современных реализациях C, стандарте C99 и C++0x предусмотрен заголовочный файл stdint.h, в котором определяются целые типы фиксированного размера. Например, uint16_t — это 16-битное беззнаковое целое.

Хорошим тоном считается использовать именно тот тип, который нужен в конкретном случае, особенно, когда речь идет о полях структур или других данных, размер которых четко определен. При переносе на другую платформу не придется биться с тем, что int внезапно стал вдвое длиннее :)

В общем, если бы безвестный китаец знал про типы фиксированного размера, он не стал бы использовать четырехбайтный int для хранения 16-битного значения и все были бы счастливы.

PS А вот еще вопрос. Что произойдет, если l объявить не в теле функции main(), а вне нее, как глобальную для данного файла?

Айпадное

Кстати, по случаю недорого приобрел Apple iPad (первый — владелец решил поменять его на мало чем отличающуюся новую модель). И если честно — единственной моей реакцией было: «Как, за ЭТО люди готовы платить по 25 тысяч рублей?» Я так и не понял, чем же настолько замечателен этот девайс, и считаю 12 тысяч за слегка подержанный аппарат вполне нормальной ценой.

Начну по порядку. Читать книжки (для чего девайс покупался) на подобного рода девайсе (основное, для чего он покупался) не совсем удобно. iTunes навязывает свои алгоритмы работы, которые идут вразрез с выработанной годами привычкой «закинуть файлы на плеер дрыг-энд-дропом». Яблочники решили, что нормальная файловая система пользователям (тем самым пользователям, которые, если заставить, писали программы для ДВК или осваивали DOS по книжке Фигурнова) не нужна, а нужны «библиотеки», свои для каждой программы. iBooks не может читать файлы Stanza, Stanza не видит файлы iBooks’а. Разумеется, все должно быть честно куплено, а не скачано из торрентов под названием «Самая большая русскоязычная библиотека». Кстати, нормальной читалки djvu я не нашел — а это очень важно лично для меня.

Софту для iOS далеко до своих аналогов с «настольных» компьютеров, как до Китая пешком. В тутошнем Safari нет выбора кодировки, который иногда ну очень нужен на страничках десятилетней давности. Да, я понимаю, что «пользователю» до фонаря все эти КОИ-7 и прочие болгарские кодировки, но засуньте эту штуку в какую-нибудь задницу, а не убирайте совсем! Отсутствует поиск по страничке — ну кому он вообще нужен, когда можно прикольно масштабировать страничку двумя пальцами?

Вообще, я всегда считал тачскрин, особенно в его «пальцатом» исполнении, страшной эргономической ошибкой. В графических оболочках обычной «персоналки» сейчас активно задействованы три кнопки мыши и колесико. Фактически, мышкой можно изобразить огромное количество разных действий. Мышку можно перемещать, при этом нажатой может быть любая из кнопок. Есть double-click, есть прокрутка. Давайте просто посчитаем, какие действия можно выполнять мышью (стандартной трехкнопочной):

— простое перемещение
— клик любой кнопкой, double-click любой кнопкой
— drag-and-drop с любой из нажатых кнопок
— прокрутка колесом

Итого — 11 различных «жестов». Пользователь с минимальными навыками, умеющий играть в пасьянс «Косынка» и «Сапер», владеет минимум 5-6 из них. Тачскрин любого типа поддерживает только двойной и одиночный щелчки и drag-and-drop, мультитач добавляет «многопальцевые жесты» — жалкий заменитель колеса прокрутки. В общем, впечатление такое, что количество привычных действий сократили вдвое. Более того, оказывается, что тачскрин жутко неудобен в играх. На экране в более-менее сложных игрушках, типа стрелялок и слешеров, возникает целых два «виртуальных джойстика» (по поведению они больше похожи на трекбол), причем жутко неудобных — возить пальцем по гладкому экрану даже более гнусно, чем пользоваться ноутбучным тачпадом. IBM-овский clit mouse, несмотря на то, что является вещью компромиссной во всех отношениях, в разы лучше.

Впечатление от пересаживания на пальцатый интерфейс от мышки и клавиатуры описать сложно… Попробуйте представить, что вам привязали к спине левую руку и просят проехать немного на машине с механической коробкой передач. Конечно, это можно сделать, например, подруливая коленками, но все равно непросто. «Двурукий» пользователь превращается в однорукого инвалида, да еще и с ущербной одноконопочной «мышью».

Кстати, как-то обсудали с [info]golergka эргономику тачскринов. Я ругался на то, что точность позиционирования курсора мышью и пальцем просто несравнима. Макс отвечал на это «убойным» аргументом — мол, программа с правильным интерфейсом для тачкрина не будет использовать кнопочки размером 5×5 пикселей. Конечно, мерить размеры экранных элементов управления в пикселях неправильно, правильнее использовать либо миллиметры (чтобы сравнивать экраны с разным dpi) или даже градусы-минуты-секунды, чтобы измерять угловые размеры элемента при нормальном расстоянии до экрана. В любом случае, размер элемента управления не должен быть сильно меньше размера «пятна контакта» курсора — для мыши это, согласно разнообразным HIG, порядка 16×16 пикселей — т. е. около 3-4 мм на обычном экране. У пальца — не меньше, чем 10×10 мм. Так вот, программа с неправильным интерфейсом входит в стандартную поставку iPod и называется Safari. Достаточно зайти в профиль ЖЖ или на хорошо викифицированную википедическую страничку, чтобы убедиться в том, что комфортный при чтении размер шрифта делает из гиперссылок в тексте крайне неудобные «элементы управления».

Немного про клавиатуру. Она просто неудобна. Держа девайс двумя руками в вертикальном положении, я еще могу набирать что-то большими пальцами, но во-первых, у меня ладони большие, а во-вторых, это все равно неудобно. В горизонтальном положении никаких рук не хватит тянуться. Маленькая аппаратная клавиатура, как у HTC TyTN или Qtek 9100, в разы удобнее, даже несмотря на то, что набор большими пальцами — это извращение. Красивой экранной клавитауры айпада хватает разве что на написание адреса в браузере. Кроме того, абсолютно нелогичен набор кнопок со знаками препинания. Помнится, Лебедев ругался по поводу якобы неправильного расположения запятой. На экранной клавиатуре iPad неправильно расположено абсолютно все! Наверное, еще больше ненависти к айпадовской клавиатуре сгенерировал Каганов.

Вот что можно похвалить — это экран. Он очень приятен на вид, визуально имеет неплохую цветопередачу (из-за глянцевого покрытия) и очень и очень хорошую четкость, особенно в сравнении с обычным компьютерным дисплеем. На улице я на него не смотрел, но подозреваю, что тут глянцевое покрытие сыграет как раз негативную роль.

Вернусь к софту. В нашей Папуасии уже давным-давно считается, что самые навороченные программы стоят 70 рублей на лотке с дисками. Вызывает искреннее недоумение жадность, например, самой распоследней дурацкой игры. Хочешь пройти уровень? Гони 99 центов! Чисто гостиница «Экономическая» из сказки про Незнайку. Все-таки Джобс придумал гениальный способ «доить» пользователей. Якобы «бесплатный» софт оказывается демо-версией, просящей деньги или показывающей рекламу. Freeware как таковое на iOS, по-моему, отсутствует. Джобсу со всего этого, разумеется, капает процентик.

Непонятки возникают с «многозадачностью». Как я понимаю, для пользовательских приложений она отсутствует. Выполняющиеся «в фоне» программы — редкость, исключение — разве что iTunes. Яблофанаты говорят, что для обычного пользователя понятие «многозадачности» чуждо и непонятно. При этом у них одновременно висят аська, скайп, недоделанный документ в ворде и окошко браузера с башоргом. У самых продвинутых крутится винамп.

Кстати, в том же Safari возникли непонятки с табами. Например, «вконтактный» плеер нормально играет музыку, находясь в неактивной закладке. Youtube в неактивных закладках почему-то не работает. Еще убивает перезагрузка страничек при активном переключении среди двух-трех табов по непонятным причинам. Мало памяти?

Совершенно отвратителен редактор текста в Safari (а тексты в вебе не ограничиваются 140 символами для Твиттера). Набрать 140 букв в Твиттере или приличную запись в блог — это очень разные вещи, и второе требует возможности перемещения по тексту и вообще «нормального» редактирования. Тыканье пальцем в экран — это не заменитель курсорных клавиш. Кстати говоря, курсорные клавиши были, скажем, на клавиатурных коммуникаторах Qtek 9100, HTC TyTN и им подобных (а с поименованными я просто неплохо знаком), несмотря на наличие тачскрина. Просто пользоваться ими намного удобнее, чем пытаться попасть пальцем или стилусом в промежуток между теми самыми буквами.

В целом, впечатление не самое приятное. Те же коммуникаторы на Windows Mobile 6.0 «старались казаться» маленькими компьютерами, с маленькими Word и Excel. Конечно, трудно найти маньяка, который делал бы что-то осмысленное в этих программах, но факт остается фактом. iOS явно разработана исключительно для мобильных устройств, основное применение которых — игры, проигрывание музыки и видео, немножечко — серфинг по интернету или чтение честно купленных электронных книжек. Если от устройства на WM6 оставалось впечатление «компьютера-недомерка», то iPad — это «айфон-переросток», такая дорогая игрушка.

Кто-то утверждает, что iOS — это пример «операционной системы будущего», не требующей регулярных плясок с бубном, камланий и человеческих жертвоприношений. Не знаю, как вам, а мне такое будущее кажется малоинтересным.

Тьма невежества

В ЖЖ [info]nicka_startcev обсуждаем функциональное программирование и его «место под солнцем». Вспомнилась другая дискуссия, которую я однажды упоминал — там фигурировала несложная (для понимающих прикол) задачка, которую штатные форумные мегапрограммисты пытались решить известными им методами. Разумеется, никто ее не решил и даже толком не понял, что же там требуется. Хочу изложить тут несколько своих и не своих мыслей, которые в этом контексте оказываются довольно интересны.

Как обычно построено изучение языков программирования (в России)? В самом первом приближении оно выглядит так: в школе изучается КуМир или Pascal, в ВУЗе — сначала Си, затем Си++. Некоторые маньяки предлагают начинать с ассемблера, а Си — только после него. Кое-кто придерживается мнения, что Си надо забыть, как страшный сон, и начинать с Java.

Такой подход в педагогике называют «ретроспективным» — изучение предмета повторяет историческую последовательность его развития. Есть достаточно большое число возражений против этого подхода, но в обучении программированию он почему-то остается самым распространенным, несмотря на, допустим, слова Страуструпа «общее подмножество C и C++ не есть лучшее для изучения начальное подмножество».

Что получается в результате? Где-то я встречал идею упорядочить языки программирования по их «мощности». Язык B мощнее языка A, если позволяет легко записать нетривиальную для A операцию. Например, C мощнее Pascal, так как допускает запись произвольных операторов в конструкции for. Применение? Проход по связному списку «в одну строчку». С++ — мощнее C (классы и прочие извращения).

Так вот, что делает программист, знающий менее мощный язык (например, что-то типа C или Pascal), когда встречается с более мощным (Javascript, например)? Он легко находит аналогии между известным ему менее мощным языком (и некоторыми типичными «сложными» конструкциями) и некоторым подмножеством более мощного. Это подмножество и объявляется «более мощным» языком. Например, C++ превращается в «C на стероидах». От JavaScript остается небольшая его часть, отдаленно напоминающая Си с ампутированными типами. «Всякая функциональщина» вообще превращается в темный лес — подмножество, обеспечивающее императивное программирование, там очень неудобное, а все остальное — «это извращение».

Результат обычно печален — «мощный язык» используется так, как использовался бы «слабый». Новые возможности — это либо «приятный синтаксический сахар», либо «нафиг не надо». Если рассмотреть это с позиций теории о четырех стадиях обучения — неосознанного незнания, осознанного незнания, осознанного знания и неосознанного знания, то окажется, что, допустим, возможности JavaScript, делающие его мощнее C, для сишника являются «неосознаннным незнанием» — он не только не знает о них, он еще и не догадывается об их существовании (пока пальцем не ткнут).

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

PPS Я не ошибся, когда назвал Javascript «более мощным», чем C и Pascal. Вышеприведенное определение «мощности» не связано со скоростью работы программ, с близостью к «железу» и прочими параметрами, которыми сишники и ассемблерщики гнобят остальных.

Убивать программистов

I hope the guy that wrote the in-flight entertainment system had nothing to do with the flight control systems on the plane!

Отсюда: http://embeddedgurus.com/stack-overflow/2011/06/how-to-lockup-the-in-flight-entertainment-system-on-a-boeing-777/.

Эта система (с телевизорчиками в спинках кресел) действительно «развлекательная», даже для эмбеддеров.

volatile const

Увидел в Твиттере искреннее удивление по поводу того, что в C-подобных языках программирования вынесеная в заголовок поста конструкция является допустимой. В самом деле, как константа (const) может быть «изменчивой» (volatile)?

Дело в том, что в C нет понятия «константы». const — если верить Кернигану и Ритчи — не более, чем «квалификатор», то есть подсказка компилятору, что описываемый объект не может явным образом быть изменен в программе. Это повод ругаться нехорошими словами при попытке скомпилировать программу и не менее хороший повод применить какую-нибудь оптимизацию — Керниган и Ритчи говорят, например, о возможном размещении объекта в памяти, доступной только для чтения. Я даже не озвучиваю очевидного — можно вообще не создавать объект, а при его использовании просто подставлять заданное значение. Впрочем, согласно стандарту C, компилятор должен только ругаться при явных попытках присвоить значение переменной, объявленной как const. Во всех остальных случаях слово const можно игнорировать.

volatile обозначает несколько другое. Точное значение этого «квалификатора» зависит от реализации компилятора, он может быть даже проигнорирован. Обычно volatile — это указание на то, что описанный объект может изменяться не предусмотренным в программе способом, и следовательно, не должен «выбрасываться» при оптимизации. Тут обычно люди сильно удивляются, недоумевают и решают не засорять мозги таким непонятным словом. Как это — «изменяться не предусмотренным в программе способом»? Что же это такое творится, когда компьютер живет своей собственной независимой от программ жизнью и волшебным образом меняет значения переменных в программе?

Сейчас я буквально в двух строчках объясню, что же на самом деле означает volatile. Дело в том, что при программировании «на уровне железа» очень часто применяется memory-mapped IO — ввод-вывод с отображением на память. Допустим, в микроконтролере (наподобие PIC или AVR) байт памяти с номером 0xN содержит значение, которое намерил аналогово-цифровой преобразователь. А теперь давайте напишем цикл, который будет крутиться до тех пор, пока АЦП не намерит превышение параметра.

unsigned char *pADC = 0xN;
while(*pADC <128){
// fuck around for a while
}
// panic and die

Давайте подумаем, что же сделает с нашим циклом оптимизирующий компилятор? А он умный, он превратит все это в такую конструкцию, где значение АЦП будет считываться только один раз (тут я пишу на псевдокоде c GOTO — надо ли напоминать, что в «железе» циклов нет?):

IF memory[0xN] >= 128 GOTO panic;
loop: FUCK AROUND
GOTO loop
panic: ВСЕ ПРОПАЛО!

Видите? В нашей программе внутри цикла значение АЦП не изменялось в явной форме, поэтому компилятор и предпочел сэкономить в каждой итерации цикла одну инструкцию сравнения. Квалификатор volatile как раз придуман для предотвращения такого поведения. Следующий код будет работать правильно:

volatile unsigned char *pADC = 0xN;
while(*pADC < 128){ // fuck around for a while } // panic and die

К счастью, времена, когда программист должен был знать про MMIO, давно прошли. Алена C++ очень правильно заметила:

вообще можно всю жизнь программировать на C++, но с volatile так и не встретиться

Теперь про const. Прикрутим его к нашему примеру с АЦП. Нетрудно догадаться, что попытка что-то записать в область памяти, отведенную для АЦП, может закончиться весьма печально (а может, и нет). Допустим, что в этом случае процессор просто остановится (вполне разумное, кстати говоря, поведение). Хотелось бы иметь механизм, сигнализирующий программисту о недопустимости выполняемых им действий. Особенно "интересно" такое поведение будет выглядет в том случае, если случайная запись происходит редко при "трагическом стечении обстоятельств". Нет проблем! const как раз вынуждает компилятор ругаться в случае присваивания чего-то объекту с этим квалификатором. Например, программа (с очевидной ошибкой)

volatile unsigned char *pADC = 0xN;
while(*pADC = 128){
// fuck around for a while
}

успешно скомпилируется, но будет работать неправильно. Более того, разные компиляторы могут "наоптимизировать" совершенно по-разному. Впрочем, в случае зависающего при записи процессора это нам совершенно безразлично. Далеко не всегда компилятор будет выдавать warning на конструкцию типа while(*pADC = 128), особенно в случае какой-нибудь экзотики. А обнаружить такого рода ошибку позволит тривиальное исправление:

volatile const unsigned char *pADC = 0xN;
while(*pADC = 128){
// fuck around for a while
}

Все, что написано выше этого абзаца - знать полезно. А все, что написано ниже - будет плодом извращенного воображения, доказывающего, что в C и C++ константы таковыми вообще не являются. Например, в различных компиляторах для i386 данный код:

const int a = 0;
*((int*)&a) = 1;
printf("%i", a);

будет печатать либо 1, либо 0 в зависимости от настроек оптимизации. Замечу, что поведение данной конструкции может изменяться и при замене типа переменной a с int на, допустим, float.

Я специально сделал оговорку об i386. Есть архитектуры, где не вся память доступна для записи. Например, вышеупомянутые AVR имеют (семейство Mega) 8 кБ ОЗУ и целых 256 кБ флеш-ПЗУ. Практически все компиляторы для этой архитектуры стараются поместить константы в ПЗУ, иногда, правда, для этого им нужно "ткнуть пальцем", но... Подобная конструкция для засунутой во флеш-память константы должна приводиться не к указателю на целое, а к указателю на целое во флеш-памяти. При этом первый и второй имеют одинаковое представление, но показывают на совершенно разные вещи. Этот код выдаст "0", если затирание единицей какого-то байта памяти не приведет к фатальным последствиям.

В общем, если кто-то считает, что язык Си можно изучать в отрыве от "железа" - продолжайте считать так дальше. А я дам ссылочку на один интересный тест по C применительно ко встроенным системам.

Вечером в танце я передаю вам сведения

А вы пробовали станцевать хотя бы пузырьковую сортировку?

Самое мозгосносящее — когда десять танцующих мужиков изображают быструю сортировку (quicksort).

Впрочем, сортировка слиянием тоже ничего:

Также имеются сортировка Шелла:

Сортировка вставкой:

И сортировка выбором:

В общем, смотрите.

Про интерфейсы

Когда-то давно, еще, наверное, в IE5, при вводе в адресную строку чего-то, заведомо не являющегося URL, браузер искал это с помощью поисковика MSN или чего-то прописанного вместо него (хоть замена MSN на другой поисковик нигде не описывалась). Итого в панели браузера имелось лишь одно поле ввода, предназначенное как для поиска, так и для ввода URL. В современных браузерах существуют отдельные поле ввода URL и поле для «быстрого поиска», причем понять, что для чего предназначено, «юзеру» затруднительно (не говоря о том, что многие не отличают поле ввода поискового запроса на Яндексе от поля ввода URL в браузере). По-моему, получилось просто усложнение интерфейса.

И еще. Читал вчера у Joel’а по поводу картинок в FrontPage. Вспомнилась еще одна неприятная штука с картинками в HTML. В свое время считалось, что загрузка картинок в браузере может быть отключена, и тег img был снабжен атрибутом alt, в котором должен был прописываться некий текст, описывающий изображение. В «элементарных руководствах» по HTML всех призывали писать в нем описание картинки, что-нибудь типа «Белое облако, летящее с востока на запад». Надо ли говорить, что никто сегодня этим атрибутом толком не пользуется? К сожалению, человечество так и не придумало, как нормально снабдить графический файл альтернативным «текстовым описанием». Возможно, имело бы смысл отправлять эти данные в HTTP-заголовке, тогда вместо загрузки большого файла целиком можно было бы обойтись только запросом HEAD. Впрочем, все получилось так, как оно есть, с полной невозможностью найти картинку по ее описанию в «поиске по картинкам».

Ощущаю себя жестоко обманутым

Немножко полистал SICP. Вспоминаю то, чему учили студентов в курсе «Программирование и работа на ЭВМ» на первом курсе мехмата МГУ. Целый год изучался язык Си, точнее, жуткая смесь из Си (указатели, низкоуровневая работа с памятью и т. п.) и несложных задач (вычислить сумму последовательности). В SICP идет разговор о вещах, мехмату гораздо более близких: символьная арифметика, символьное дифференцирование, схемы из функциональных элементов, комплексные числа, определение функций с помощью ряда Тейлора и прочая математика, причем первому курсу вполне доступная.

Ощущаю себя жестоко обманутым.

Еще про недавний скандал с memmove

Вот представьте себе. Стоит в заповедных лесах дальнего Замкадья химкомбинат и делает что-то непонятное, используя в процессе этиловый спирт. Работяги спирт таскают, разбавляют и пьют. Нехорошо, конечно, но других в заповедных лесах дальнего Замкадья нет. И вот кому-то умному приходит в голову мысль заменить дорогой этиловый спирт на дешевую метилку. Работяги, разумеется, массово травятся, летальный исход, инвалидность, все дела.

metspirt

Вопрос: кто здесь козел? Несуны-алкашики или «рационализатор», не принявший во внимание местные алкогольные традиции?

PS А вот один из ответов на вопрос. В благословенные советские времена на химзаводах использование метилки сводилось к минимуму — именно по той причине, чтобы алкаши травились не сразу и насмерть, а постепенно до цирроза печени и белой горячки.

Про memcpy, memmove и Open Source

Кто не читал книжку Кернигана и Ритчи — сразу может скроллить до следующего поста. Остальные насладятся замечательной историей, почерпнутой у [info]avva.

В общем, так исторически сложилось, что в языке программирования Си есть две функции, которые копируют произвольные области памяти. Одна называется memmove — memory move, другая — memcpy, memory copy. Они имеют одинаковый синтаксис, но работают немного по-разному. Первая правильно копирует данные, когда области перекрываются, вторая — нет, но работает «чуть-чуть» быстрее. Естественно, что программисты эти функцию путают, и один из авторов языка Си, Брайан Керниган в своей книге «Практика программирования» даже высказался на этот счет:

В стандарте ANSI C определены две функции: memcpy, которая работает быстрее, но может затереть память, если источник и приемник данных пересекаются, и memmove, которая может работать медленнее, но всегда корректна. Бремя выбора между скоростью и корректностью не должно взваливаться на программиста; должна быть только одна функция. Считайте, что это так и есть, и всегда используйте memmove.

Как известно, большинство программ написано плохо. Несмотря на предупреждения, многие используют memcpy даже в случае перекрывающихся областей памяти — иногда она работает корректно и в этом случае. Керниган и Ритчи плачут кровавыми слезами, но ничего поделать не могут.

В общем, разработчики одной из наиболее распространенных реализаций этих функций — библиотеки glibc, использующейся практически во всех Linux-образных операционках, решили, что хорошо бы немного доработать memcpy, чтобы та работала еще быстрее, при этом сделав так, что она совсем перестала работать с перекрывающимися областями в памяти. Эта версия библиотеки прошлым летом была включена в дистрибутив Fedora — и вскоре обнаружилось, что некоторые программы перестали работать. Баг искали несколько месяцев, пока не поняли, что дело в том, что авторы этих программ понадеялись на то, что memcpy всегда будет правильно работать в тех самых «неправильных» случаях.

После этого началась веселуха. Авторы glibc, и «по совместительству» — разработчики Fedora — заявили, что никакого бага нет, а во всем виноваты криворукие программисты. Линус Торвальдс (живой бог для всех линуксоидов) пишет примерно следующее:

You can call it «crap software» all you like, but the thing is, if memcpy doesn’t warn about overlaps, there’s no test coverage, and in that case even well-designed software will have bugs.

Then the question becomes one of «Why break it?»

и

Quite frankly, I find your attitude to be annoying and downright stupid.

How hard can it be to understand the following simple sentence:

THE USER DOESN’T CARE.

Если у пользователя после обновления ОС не работает кривой Flash-плагин — то пользователь не будет винить в этом криворуких разработчиков плагина, а будет винить разработчиков ОС. Разработчики glibc этого не понимают и их главарь, Ульрих Дреппнер, подводит вот такой итог под всей этой дискуссией:

The existence of code written by people who should never have been allowed to touch a keyboard cannot be allowed to prevent a correct implementation.

Как же это не похоже на ту идеологию, которую проповедует Raymond Chen из Microsoft. Но вся эта история объясняет, почему на большинстве пользовательских ПК все еще стоит Windows.

PS В итоге до сих пор в glibc остается новая версия memcpy. Обнаружилось, что в огромном количестве программ программисты полагались на недокументированное поведение этой функции, где-то исправили, а где-то — нет.

Скачиваем книги с ResLib.com

Понадобилась мне сегодня одна книжка по математике. Но вот беда — ее не оказалось в той библиотеке научно-технической литературы, которую на 35 дисках предлагают скачать на РуТрекере. Лет пять назад я в таких случаях пользовался «Электронной библиотекой мехмата МГУ», благо в alma mater на 15 этаже висела бумажка «сайт lib.mexmat.ru, логин mexmat, пароль mexmat». Но что знают двое — знает и свинья, поэтому эти логин с паролем распространились по всему интернету, попали в лапы правообладателей и те потребовали прикрыть лавочку, написав чуть ли не Лупанову (тогдашний декан мехмата) с Садовничим (а это ректор МГУ). Библиотека, естественно, закрыла доступ извне полностью, оставив его только для сети мехмата.

Тем не менее, через несколько лет от ЭБММ отпочковалась новая электробиблиотека ResLib.com — те же книги, но с доступом через Web и ограничением — не более 25 страниц за раз. На мое счастье, нужная мне книжка в ней нашлась. Но вот предложение читать по странице в полчаса, да еще и через неудобный Web-интерфейс мне показалось оскорбительным.

Я решил попробовать скачать эту книжку — есть у меня еще нехорошая привычка не доверять веб-сервисам. Мало ли — через неделю все закроется? Так что, посмотрев на код картинок, которые показываются при просмотре книг в этой библиотеке, я решил запустить свой любимый ReGet и воспользоваться в нем прекрасной опцией «создать нумерованный список». URL картинок выглядел так:

http://reslib.com/img2.php?b=635101403&s=l&p=1

Параметры обозначают следующее: b — идентификатор книги, s — размер картинки, нам нужен l, то есть large, p — номер страницы (видимо, «выдираются» они из djvu-файла). Запускаем ReGet и создаем нумерованный список — это замечательная фича, разработчиками упоминаемая, как «уникальная».

reget-spisok

Запускаем закачки и… в директории, куда мы хотим скачать файлы, появляется файл img2.php (правда, Windows Explorer распознает в его содержимом png-картинку), а Регет бомбардирует нас несколькими десятками сообщений «файл уже существует, переименовать?». Непорядок. И оказывается, что в одном из лучших download-менеджеров нет никакого способа дать свои названия куче файлов, закачиваемых автоматически.

Правда, я все же обнаружил в ReGet так называемые «макросы», которые можно использовать для относительно массовых переименований закачек. Оказывается, что если объденить все файлы этой книжки в одну группу, то можно уже в группе задать правило, применяемое к тем файлам, для которых файл с тем же именем уже существует. Там есть выбор из нескольких настроек — «Спросить», «Переписать», «Переименовать» — и в случае переименования можно воспользоваться макросами. Обращу внимание почтенной публики на то, что в справке ReGet список макросов немного шире — в частности, имеется макрос Param, выдирающий из GET-запроса значение параметра с заданным именем. Как раз то, что надо — давайте назовем наши файлы page1.png, page2.png и так далее! Идем в свойства группы и ставим там такие настройки:

reget-makros

Теперь снова запускаем закачку, скачиваем пару десятков файлов уже с правильными названиями и на дальнейшие запросы получаем от сервера отлуп — «Ошибка 503, приходите позже». Ну я думаю, вы поняли? Сервер пытается «забанить» нас по IP-адресу. Меняем айпишник (кто пользуется proxy, кто «пересовывает» соединение с провайдером) и продолжаем. Итого — за 15 минут скачиваем книжку целиком.

Зачем я это рассказываю? Разумеется, не с целью ограбить онлайн-библиотеку — как вы видите, это может сделать каждый. Я хочу продемонстрировать, что даже в лучших программах «из мира Windows» какие-нибудь не очень тривиальные действия сопряжены с возникающими «из ниоткуда» трудностями. Windows-стиль не предполагает работу с командной строкой, а уж тем более — написание сложных скриптов на командном языке. А я более чем уверен, что на любом языке какого-нибудь Unix shell (или даже Windows Power Shell, или даже в обычном cmd.exe) написание скрипта, скачивающего ту же кучу файлов по маске, займет те же 15 минут, что я изучал документацию ReGet и придумывал способы обхода непонятно откуда возникших ограничений.

Да, пользоваться wget намного (или немного :) ) сложнее, чем ReGet или Download Master — но первый можно использовать в каких-то своих целях, а «качалки» из Windows — только в рамках того, что предусмотрели их авторы.

Все, что должен знать программист

По на водке ребе [info]metaclass нашел интересный списочек того, что должен знать Настоящий Программист (существо, видимо, мифическое):

http://sharpc.livejournal.com/67583.html

Конечно, как справедливо замечено в комментах,

достаточно 2-3 пунктов из описанного, чтобы уже быть лучше сотен индусов, которые НИЧЕГО из этого не знают, но тем не менее получают за программирование бабло

и

Чувак с таким теормаксимумом называется «технический консультант по вытаскиванию проектов из жопы», получает от $100-200 в час и свысока посматривает на «всех этих недальновидных индусов»

В общем, есть, на что ориентироваться.