НОВОЕ: OS/2 GURU - Вопросы и ответы ru · en · de · es · it · pt · cz · pl · fr

OS/2.GURU Library

Reviews / articles about OS/2 eComStation ArcaOS

Operating systems
ArcaOS, eComStation, IBM OS/2 Warp
eComStation myths 

Latest  
 
 
Blonde Guy

Reformat Утилита для форматирования USB флешек, USB винчестеров (для совместимости с OS/2)

 

(promo)

Unsorted

 

 

AD: Upgrade ArcaOS to NeoWPS level

  • Install original PNG icons drawed by designer, specialized at OS/2 adornation.
  • Install eSchemes 2019 to change colors and buttons on desktop.

OS/2 Miniaturization Contest


TITLE: OS/2 Miniaturization Contest

DATE: 2001-10-02 16:44:08

AUTHOR: Андрей А. Породько
Please use online translator
go to http://translate.google.com
and request the translation of http://ru.ecomstation./projects/reviews/index.php?id=26
to your language

Перевод с английского: Андрей А. Породько. Оригинал: Секреты мастеров программирования раскрыты! (Michal Necasek, Сентябрь 2001)

В августе 2001 меня посетила дикая идея: я собрался и анонсировал в группе новостей comp.os.os2.programmer.misc "Конкурс миниатюризации для OS/2", конкурс для программистов OS/2, целью которого должно было быть создание минимально возможной программы удовлетворяющей следующим условиям. Наиболее важными из них были:

  • Программа должна быть в формате OS/2 LX исполняемого модуля (32-бит);
  • Программа должна выводить сообщение "I'm really small!" (Я действительно мала!) и перевод строки на консоль;
  • Программа должна запускаться в OS/2 Warp 4 GA или более новой версии без добавления любих файлов сверх стандартного набора.

Для того, чтобы сделать конкурс более интересным и привлекательным для программистов, имеющих различный опыт, сначала я объявил конкурс в двух категориях, которые потом были дополнены еще одной:

  • Stock - разрешено использование любого языка высокого уровня и широкораспространенных инструментов. Использование ассемблера запрещено.
  • High Octane Stock - тоже самое что и выше, но с применением ассемблера.
  • Custom - (я бы сказал free style, прим.пер.) разрешено все, только бы программа работала и инструмент с помощью которого она была изготовлена был бы доступен.

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

Перед тем как объявить о конкурсе у меня были несколько своих идей как получить очень маленький исполняемый модуль. Но даже я был удивлен программой-победителем. Победителями в трех категориях стали:

  • Stock - Knut St. Osmundsen, 276 байт, используя Watcom C, WLINK и LxLite.
  • High Octane Stock - Knut St. Osmundsen, 273 байт, используя ALP, ILINK и LxLite.
  • Custom - Martin Lafaix, 196 байт, используя в основом 16-тиричный редактор.

Возможно, наиболее интересным результатом конкурса явился тот факт что программы на C и ассемблере очень незначительно отличаются размером, в отличие от первоначальных ожиданий. Это в основном благодаря тому факту, что Watcom C предоставляет очень хорошие средства управления генерируемыми объектными файлами, почти такое же хорошее как в ассемблере.

Но, конечно же, наиболее замечательный результат получил победитель в категории Custom. Особенно если Вы понимаете что размер заголовка LX-модуля составляет 196 байт и загрузчик программ OS/2 откажется запустить что-либо меньшее по размеру 196 байт. Но тем не менее этот исполняемый модуль размером с заголовок печатает 17-ти байтное сообщение и корректно возвращает управление операционной системе. Я не буду здесь обсуждать эту категорю в деталях и отправлю читателя к автору Martin Lafaix'-у за разбором его программы-победителя (справедливо названному Fandango on Core).

Теперь, после того как я обрисовал условия и ход конкурса, давайте перейдем к рассмотрению исходных текстов. Исходные тексты, которые я представлю, это мои собственные тексты, но с использованием многих идей победителей. Я строил все программы с помощью Watcom 11.0c и их вполне возможно приспособить к другим ассемблерам/компиляторам, возможно за исключением наиболее продвинутого C-кода. Я буду объяснять все ключи компилятора и линкера которые не совсем очевидны.

Я предполагаю что читатель имеет представление об использовании ассемблера, C, архитектуры x86, формата исполняемого модуля OS/2 и среды исполнения. Это определенно не для новичков даже если новичок сможет перенять прием или два не перегрузившись. Если все же перегрузился, не беда, возвращайтесь через год или два! Я тоже когда-то начинал и был новичком.

Категория High Octane Stock

Сначала я хочу представить категорию, в которой разрешалось использование ассемблера, так как текст здесь более "прямой" и не используется столько таинственных приемов (трюков) как в C версии. Надо сказать, что в ассемблерной версии все ясно видно, тогда как в C версии многие приемы скрыты за используемыми прагмами и ключами компилятора.

Хорошо, как вы выводите сообщение на консоль с использованием OS/2 АПИ? Первый очевидный выбор - через DosWrite. Но DosWrite имеет несколько серьезных недостатков. Он требует четырех параметров (т.е. четырех DWORD слов в стеке) и располагается в системной библиотеке DOSCALLS.DLL (на самом деле в ядре OS/2), которая имеет достаточно длинное имя, которое должно быть включено в исполняемый файл.

Давайте разберем внимательнее. В этом случае можно воспользоваться другим, редко используемым, вызовом OS/2 АПИ, это DosPutMessage. Он имеет только три параметра и, что еще лучше, располагается в MSG.DLL (более короткое имя библиотеки). Полностью исходный текст программы (названной asm1.asm) с использованием DosPutMessage выглядит примерно так:

.386p
            EXTRN   DosPutMessage:BYTE

_DATA       SEGMENT BYTE PUBLIC USE32 'STACK'
_msg:
    DB  "I'm really small!",0aH
_DATA       ENDS

_TEXT       SEGMENT BYTE PUBLIC USE32 'CODE'
            ASSUME CS:_TEXT, DS:_TEXT, SS:_TEXT

startup:
    push    offset flat:_msg
    push    12H
    push    1
    call    near ptr flat:DosPutMessage
    add     esp,0CH
    ret
_TEXT       ENDS

            END startup

Как Вы видите, эта программа очень короткая и не использует никаких трюков. Единственным "трюком", пожалуй, является использование вместо DosExit простого RETs. Это приводит к тому же эффекту, что и вызов DosExit с параметрами по-умолчанию. После компилирования WASM-ом и линковки WLINK-ом в конце концов Вы получите программу размером в 545 байт. Вот точный набор команд, который я использовал:

wasm asm1.asm 
wlink file asm1 lib os2386 option st=32k 

Единственным, не совсем очевидным ключом является st=32k который устанавливает размер стека программы в 32 килобайта. Без этого ключа, программа имеет только 18 байт стека (сообщение которое она должна вывести) и трапнется сразу после старта.

С точки зрения "жирности" программы 545 байт не так уж и плохо, однако легко уменьшить размер с помощью LxLite:

lxlite /T /ZS:512 asm1.exe 

Здесь трюком является удаление стаба MS-DOS (ключи /T и /ZS LxLite), размером в 128 байт, что значительно влияет на размер программы (в нашем случае. прим. пер.). OS/2 вполне нормально запустит и программу без этого стаба. MS-DOS не будет способен запустить это программу, но этого и не требовалось по условиям конкурса. LxLite проделывает некоторую дополнительную оптимизацию и результат - программа в 325 байт. Это значительно лучше чем 545, но все еще далеко от победителей. Мастера очевидно имеют какие-то козыри в рукавах. Хорошо, давайте посмотрим на них.

Для достижения минимального размера необходимо атаковать с нескольких направлений. Скорее всего одно из них позволит уменьшить размер программы. Одним, таким не очевидным ходом является уменьшение избыточности структуры LX модуля.

Но мы начнем с чего-то совсем отличного. Существует более чем один путь уменьшения размера файла программы. Пока я думал что нет лучше способа печатать сообщение чем использование OS/2 АПИ, система Warp 4 (базовая ОС с конкурсе) предоставляет несколько менее известный путь, который подходит гораздо больше. Warp 4 поставляется с динамической библиотекой среды C, представляющей собой несколько видоизмененный вариант библиотеки от VisualAge C++ 3-й версии. На самом деле это несколько DLL-библиотек - LIBCS.DLL (для задач не использующих треды), LIBCM.DLL (для задач использующих треды) и LIBCN.DLL (подсистема). В них имеются vprintf() и puts() которые требуют только одного аргумента. Я выбрал puts() так как это позволяет еще оптимизировать код, опустив символ перевода строки в сообщении (puts() добавляет перевод строки автоматически). Тем не менее ординал (ссылка в DLL на функцию. прим. пер.) для vprintf() требует на один байт меньше в таблице импорта исполняемого модуля и, таким образом результат будет таким же. Как бы то ни было, вот asm2.asm:

.386p
            EXTRN   puts:BYTE

_DATA       SEGMENT BYTE PUBLIC USE32 'STACK'
_msg:
    DB  "I'm really small!"
_DATA       ENDS

_TEXT       SEGMENT BYTE PUBLIC USE32 'CODE'
            ASSUME CS:_TEXT, DS:_TEXT, SS:_TEXT

startup:
    mov     eax,offset flat:_msg
    jmp     near puts
_TEXT       ENDS

            END startup

Заметьте что puts() (и vprintf() тоже) требует в качестве параметра строку заканчивающуюся NULL, в отличие от DosPutMessage. Но так как мы знаем (ведь правда?) что сегмент данных гарантированно инициализируется нулями, мы этот факт опустим. Другой важный факт который следует запомнить - это то, что puts() использует соглашение _Optlink для вызова функций, при котором параметры передаются через регистры, а не через стек.

Эта программа использует другой интересный прием оптимизации. Вместо прямого вызова puts(), она делает на него переход. Когда puts() сделает возврат, управление вернется непосредственно в тот модуль который вызывал нашу программу, что избавляет нас от использования команды RET у себя. И что еще лучше, такой прием оставляет переход на puts() инициализированным нулями в файле исполняемого модуля, что позволяет линкеру и/или LxLite еще сэкономить 4 байта. Наша программа теперь имеет размер в 318 байт. Получите удовольствие от красоты программы, состоящей всего из двух инструкций и которая, тем не менее, что-то делает, безо всяких критических ошибок.

Теперь давайте пройдемся по действительно интересному. Чтобы достичь размеров программ-победителей, нам еобходимо избавится от 45 байт лишнего веса. Это можеть быть невозможно, потому что программа и так достаточно мала. Давайте попроказичаем и будем делать вещи, которые детям делать не разрешают. Это на самом деле просто, но совершенно не очевидно, это показано в листинге asm3.asm:

.386p
            EXTRN   puts:BYTE

_TEXT       SEGMENT BYTE PUBLIC USE32 'STACK'
            ASSUME CS:_TEXT, DS:_TEXT, SS:_TEXT
_msg:
    DB  "I'm really small!",0

startup:
    mov     eax,offset flat:_msg
    jmp     near puts
_TEXT       ENDS

            END startup

Да, у программы только один сегмент ! Этот сегмент не имеет флага исполняемого, но она запускается и работает и что важно... что более важно, программа имеет размер только 283 байт. Мы достигли размеров программ-победителей! А можно ли еще сэкономть ? Тут два пути. Один чрезвычайно прост: переименовать исполняемый модуль! Имя исполняемого файла сохраняется внутри его самого и для экономии драгоценных байтов, вы можете использовать пустое имя (.exe). Другой путь сэкономить место - это первая инструкция программы. Команда MOV занимает пять байт в исполняемом модуле тогда как JMP занимает только один байт (потому что адрес заполнен нулямя и будет подставлен на этапе загрузки программы). Так получилось, что мы знаем что команда MOV помещает в EAX. Это адрес 10000H (64K) потому что в линейной модели памяти исполняемый модуль всегда загружается по этому адресу. Knut St. Osmundsen нашел более элегантный метод загрузки значения 10000H в EAX длиной в 3 байта кода. Вот финальная версия исходного текста программы (asm4.asm):

.386p
            EXTRN   puts:BYTE

_TEXT       SEGMENT BYTE PUBLIC USE32 'STACK'
            ASSUME CS:_TEXT, DS:_TEXT, SS:_TEXT
_msg:
    DB  "I'm really small!",0

startup:
    dec     ax
    inc     eax
    jmp     near puts
_TEXT       ENDS

            END startup

А вот команды, которые я использовал для получения исполняемого модуля:

wasm asm4.asm 
wlink f asm4 n .exe imp puts LIBCS.362 op st=32k 
ren .exe asm4.exe 
lxlite /T /ZS:512 asm4.exe 

Необычная последовательность команд WLINK, imp puts LIBCS.362 показывает линкеру что символ (имя) должен быть экспортирован из библиотеки LIBCS как ординал 362. Мы могли бы использовать и LIBCM, но так как наша мини-программа имеет один тред, то и с LIBCS она работает нормально. Что касается номера ординала, то его легко найти с помощью EXEHDR. Конечно возможно испоьзование библиотеки импорта, но для получения доступа к одной функции совсем не обязательно ее иметь. Это способ для тех бедных (заблудших ? прим. пер. ;-) душ которые до сих пор не обзавелись Warp 4 Toolk для написания своих программ. Хитрым приемем является также директива n .exe (Имя). Она указывает WLINK использовать пустое имя для файла исполняемого модуля. И конечный размер теперь 274 байта! Так как я использовал инструменты Watcom (по крайней мере я так думаю), программа-победитель от Knut-а на один байт меньше вследствие разницы между испольняемыми модулями получаемыми с помощью WLINK и ILINK, и не существует способа проверить и проконтролировать это. Хей, однако и 274 байт совсем не плохо! Интересно также отметить что весь исходный код программы очень мал и даже команды для получения исполняемого модуля не очень сложные. Но это обманчивое представление, потому что программа явно использует некоторые особенности архитектуры x86 и среды исполнения OS/2.

Категория Stock

В предыдущем разделе я раскрыл все важные приемы достижения микроскопических размеров программы. Теперь мы попытаемся получить сходные результаты без использования ассемблера. Это требует достаточно хорошего знания специфики компилятора, такой как различные малоизвестные ключи и прагмы. Я сразу перейду к финальному коду и проанализирую его. Встречайте ! mini.c:

void puts(char *s);

#pragma data_seg("MYDATA", "STACK")
#pragma code_seg("MYDATA", "STACK")

char  msg[] = "I'm really small!";

void _System startup(void) {
    puts(msg);
}

Исполняемый модуль получается следующими командами:

wcc386 -s -g=DGROUP mini.c
wlink sys os2v2 name .exe f mini imp puts_ LIBCM.362 op start=startup,st=32k,nod
ren .exe minic.exe
lxLite.exe /T /ZS:512 minic.exe

Конечный исполняемый модуль, minic.exe, имеет размер всего 276 байт! Как это возможно? Близкое изучение раскрывает только одно отличие между C и ассемблерным вариантами машинного кода. C генерирует код эквивалентный:

    mov     eax,offset flat:_msg
    jmp     near puts

Да, компилятор чертовски умен и оптимизировал код настолько, насколько это возможно! Из-за того что компилятор не имеет информации как результирующий код будет загружаться, он не может заменить смещение текста сообщения константой. Во всем остальном, версия C использует все приемы описанные для ассемблерого варианта. Трюком здесь является то, как компилятор и линкер убедили постороить эту программу. Для достижения этого были использованы две редко применяемые прагмы компилятора. Прагмы #pragma data_seg и code_seg вполне стандартные прагмы (они поддерживаются в большинстве компиляторов и делают одно и тоже) и определяют сегмент - где должны быть размещены данные и код. В нашем случае данные и код конечно же размещаются в одном сегменте. Но почему они имеют класс STACK? Это потому что WLINK требует именованного сегмента который уже существует при сборке исполняемого модуля.

Теперь ключи компилятора. Ключ -s (отключить проверку стека) вполне очевиден. Если мы оставим проверку стека это увеличит размер кода и что гораздо хуже, потянет за собой исполняющую библиотеку C (runtime). Это без вопросов. Ключ -g управляет группой где будет размещен сегмент кода. Это должна быть DGROUP, в противном случае линкер не объединит сегменты данных и кода. К счастью Watcom крайне гибок в этих вопросах. Ключи линкера сходны с теми, что мы использовали для ассемблерной версии, за исключанием одного дополнительного. Опция NOD - это сокращение от NODefaultlibs (нет библиотеки по умолчанию) и отключает поиск библиотеки по умолчанию (для этого компилятор генерирует специальную запись в оъектном модуле). Такого же эффекта можно добится ключом -zl компилятора.

Обнако ключ к успеху заключается в опции start. В ассемблерном варианте мы использовали директиву END для указания точки старта программы. В C нет этому эквивалента, однако IBM C поддеживает прагму #pragma entry. Watcom C не имеет такой прагмы, но позволяет указать точку старта опцией start=symbol линкера WLINK.

Конечная версия исходного текста на C возможно выглядит более сложной чем ассемблерная, но все еще не слишком сложной для понимания. Результат гарантирован многочасовой интенсивной борьбой с компилятором, попытками найти правильное соотношение между ингредиентами и попытками заставить компилятор и линкер делать то, что они определенно делать не хотели. И как это не смешно, но результат борьбы не слишком велик (;-)))), прим. пер.).

Заключение

Я надеюсь, что это небольшое приключение по минимизации программы развлекло Вас или как минимум не шокировало. В лучшем случае Вы изучили некоторые приемы которые Вы сможете применить в своих программах, как это сделал я. Мораль все истории такова, если Вы потрудитесь достаточно усердно, то Вы сможете достичь результатов которые Вы и представить не могли (да-да-да, я знаю что это звучит глупо)! Да, между прочим, если Вы знаете еще приемы которые можно применить для уменьшения размеров исполняемых программ, дайте мне знать по адресу MichalN@prodigy.net.

Автор статьи: Michal Necasek

Переводчик: Андрей Породько

Test the program:

POP3 Mail Checker Widget - widget aimed to check new mail

Comments:

Reader
2001-10-03 00:39:27

I'm waiting for continuation...

....
2001-10-03 02:30:24

. ... ... ...? .... ........... ........!

Eugene Gorbunoff
2001-10-06 00:52:59

.. 5-.. ....... ...... .......... ..........
...-...... ........ ...... ..... ......... ......... ............ . ..., ... .. ........... ..... ........? :) .. .......... ...... ........... ... ...... .. ......, ..... .... ... ..... ...........

Rinat H. Sadretdinow
2001-10-11 14:32:55

. ... .. ........ ......... ..Custom''?

Eugene Gorbunoff
2001-10-25 13:53:49

........... ...... .......... .......... .. ....... ...... ....... .... .... - ....... ....... ... .......... . ....

Igor Vanin
2001-10-25 16:15:03

2EG:
.. ...... ......., ... . ......... custom .. .... ....... .......... . ....... ...........?

Eugene Gorbunoff
2001-10-29 00:23:51

. .... ......., ... .... .. ........ ........ . ...... . ....... ....... ... ..............
. ......... .....?

Timur Kazimirov
2001-10-30 04:47:58

..., ........... ;) ., ......, .. ... . ......... ....., .. ... ..... - ..........

Igor Vanin
2001-10-31 17:38:19

5 ....... ...... .... .......... ........., . 29 ....... ....... ... .............? ... ...? :-)

How to prevent data loss? a) don't use old version of JFS driver. b) Don't boot from old eCS CD1 (if you take eCS 2.0 CD1, then disable its disk checker else it can kill your partitions), Advice how to use JFS filesystem

 

Siberian OS/2

 


 

 

ArcaOS 5.1.1 whatsnew - PNG icons

PNG icons on Desktop

PNG icons on Desktop. (instead of ancient .ico designed in 1994)

Russian OS/2

eCo Software is making russian eComStation since 2001, all versions of ArcaOS since 2017.

// надо на ENG!!

Warpstock Europe 2016

Interview with Dmitry Kuminov

video

 

(C) OS2.GURU 2001 -- 2025