Dos fn 40h писать в файл через описатель


Операционная система MS-DOS

3.5. Чтение/запись файлов

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

На входе: AH = 40h
BX = файловый индекс открытого файла
CX = количество записываемых байтов
DS:DX = Адрес буфера, содержащего записываемые данные
На выходе: AX = Код ошибки, если был установлен в 1 флаг переноса CF;
Количество действительно записанных байтов, если флаг переноса CF сброшен в .

При записи данные попадают в то место внутри файла, которое определяется содержимым так называемого файлового указателя позиции. При создании нового файла этот указатель сбрасывается в 0, что соответствует началу файла. При открытии файла с помощью функции 3Dh указатель также устанавливается на начало файла. Операция записи в файл с помощью функции 40h продвигает указатель вперед к концу файла на количество записываемых байтов.

По мере увеличения размера файла ему будут распределяться все новые и новые кластеры из числа отмеченных как свободные.

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

Следует учитывать, что количество действительно записанных байтов может не совпадать с заданным в регистре CX при вызове функции 40h. Такая ситуация возможна, например, при записи в файл, открытый в текстовом режиме, байта Ctrl-Z (1Ah). Этот байт означает конец текстового файла. Другая возможная причина — отсутствие свободного места на диске.

Если функция вызывается с содержимым регистра CX, равным , файл будет обрезан или расширен до текущего положения файлового указателя.

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

Функция 40h может выполнять запись не только в файл, но и в устройство посимвольной обработки, предварительно открытое функцией 3Dh. Об этом мы говорили в разделах книги, посвященных драйверам.

Для чтения данных из файла (или устройства посимвольной обработки) предназначена функция 3Fh прерывания INT 21h:

На входе: AH = 3Fh
BX = файловый индекс открытого файла
CX = количество читаемых байтов
DS:DX = Адрес буфера для данных
На выходе: AX = Код ошибки, если был установлен в 1 флаг переноса CF;
Количество действительно прочитанных байтов, если флаг переноса CF сброшен в .

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

Если ваша программа составлена на языке программирования С, для записи и чтения данных она может воспользоваться функциями write() и read():

Эти функции работают аналогично функциям 40h и 3Fh прерывания INT 21h. Параметр handle определяет файл, для которого необходимо выполнить операцию записи или чтения. Параметр buffer — указатель на буфер, который содержит данные для записи или в который необходимо поместить прочитанные данные. Количество записываемых/читаемых байтов определяется третьим параметром — count.

После выполнения операции функция возвращает количество действительно записанных или прочитанных данных или -1 при ошибке. Будьте внимательны, если вы записываете или читаете больше 32К байтов — вы можете получить признак ошибки, хотя передача данных выполнилась правильно. Большие массивы данных можно записывать по частям.

В качестве примера мы приведем программу копирования файлов, которая пользуется описанными выше функциями ввода/вывода:

В приведенной программе для определения конца исходного файла использована функция eof():

Для файла с файловым индексом handle эта функция возвращает одно из трех значений:

1 достигнут конец файла;
конец файла не достигнут;
-1 ошибка, например, неправильно указан handle.

Программа, которая читает файл с помощью функции 3Fh прерывания INT 21h, может определить момент достижения конца файла, анализируя код ошибки в регистре AX.

Подпрограммы, работа с файлами через описатели.

Цель. Получить навыки организации подпрограмм и навыки работы с файлами через описатели.

Задание. Разработать программу в соответствии с вариантом задания, работающую с файлами, используя описатели.

Таблица. Варианты заданий

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

Теоретические сведения

Подпрограммы

Как уже было сказано выше, программный сегмент может разбиваться на части директивами определения подпрограмм (процедур). Для определения процедур используются две директивы – директива начала PROC и директива конца ENDP. Процедура должна иметь имя, которое включается в обе директивы. В сегменте кода процедуры могут располагаться последовательно, а могут быть вложенными одна в другую. Определение подпрограмм имеет следующий синтаксис:

имя_процедуры PROC параметр

Передавать фактические параметры процедуре можно несколькими способами. Простейший способ – передача параметров через регистры: основная программа записывает параметры в какие-либо регистры, а процедура по мере необходимости извлекает их из этих регистров и использует в своей работе. Такой способ имеет один основной недостаток: передавать параметры через регистры можно если их немного (если много, то просто не хватит регистров). Решить это проблему можно, передавая параметры через стек. В этом случае основная программа записывает параметры в стек и вызывает подпрограмму, подпрограмма работает с параметрами и, возвращая управление, очищает стек.

Существует несколько разновидностей команды вызова процедуры CALL. Все команды вызова безусловны и используются для передачи управления процедуре. Внутрисегментный вызов NEAR CALL используется для передачи управления процедуре, находящейся в том же сегменте. Межсегментный вызов FAR CALL используется для передачи управления процедуре, находящейся в другом сегменте или даже программном модуле.

Командам CALL соответствуют команды возврата RET. Все возвраты – косвенные переходы, поскольку извлекают адрес перехода из вершины стека. Внутрисегментный возврат извлекает из стека одно слово и помещает его в регистр IP, а межсегментный возврат извлекает из стека два слова, помещая слова из меньшего адреса в регистр IP, а слово из большего адреса – в регистр CS. Оператор RET может иметь операнд, который представляет собой значение, прибавляемое микропроцессором к содержимому указателя стека SP после извлечения адреса возврата.

Ниже приведен пример программы, в которой передача параметров в подпрограмму выполняется через стек.

MOV BP,SP ;загрузка в BP текущего адреса стека

MOV BX,[BP+4];выборка из стека 1 параметра (ca)

MOV BX,[BP+2];выборка из стека 2 параметра (ll)

RET 4 ;Возврат с удалением 4 слов из стека

MOV AX,’ca’ ;Загрузка в AX символов

MOV CX,’ll’ ;Загрузка в CX символов

PUSH AX ;Сохранение AX в стек

PUSH CX ;Сохранение CX в стек

Работа с файлами

Используется два метода доступа к файлам: метод управляющего блока файла (FCB) и метод дескриптора файла. С помощью метода FCB можно получить доступ только к файлам, находящимся в текущем каталоге. Метод дескриптора файла позволяет получить доступ к любому файлу, независимо от того, какой каталог является текущим.

Начиная с DOS версии 2.0, в набор функций прерывания 21h включены UNIX-подобные файловые функции. Идея их состоит в том, что, когда программа открывает файл, DOS возвращает 16-битовое значение «описателя файла» (дескриптора файла) (handle). После этого, когда программа читает, позиционирует, пишет или закрывает файл, она ссылаетесь на него через описатель. Одно из самых больших удобств – то, что можно обращаться к некоторым устройствам так, как будто это дисковые файлы, через зарезервированные описатели DOS:

Таблица.Предопределенные описатели DOS

Handle Наименование и описание
Стандартное устройство ввода (обычно клавиатура)
Стандартное устройство вывода (обычно экран)
Стандартное устройство ошибок (всегда CON — экран. Для сообщений)
Стандартное устройство AUX (асинхронный адаптер; 1-й послед. порт — COM1)
Стандартный принтер (1-й параллельный порт принтера- LPT1)

Ниже приведен перечень наиболее часто используемых функций прерывания 21h для работы с файлами через описатели.

Функция 3cH

DS:DX=адрес строки ASCIIZ с именем файла

Выход. AX=код ошибки, если CF установлен и описатель файла,

если ошибки нет.

Описание. Файл создается в указанном (или умалчиваемом) оглавлении и открывается в режиме доступа «чтение/запись». Если файл уже существует, то при открытии файл усекается до нулевой длины. Если атрибут файла – «только чтение», открытие отвергается (атрибут можно изменить функцией 43H).

Функция 5bH

Создать новый файл (не должен существовать).

DS:DX=адрес строки ASCIIZ с именем файла

Выход. AX=код ошибки, если CF установлен и описатель файла,

если ошибки нет

Описание. Этот вызов идентичен функции 3ch, с тем исключением, что он вернет ошибку, если файл с заданным именем уже существует.

Функция 5aH

Создать уникальный файл.

DS:DX=адрес строки ASCIIZ с путем (заканчивается \)

Выход. AX=код ошибки, если CF установлен и описатель файла,

если ошибки нет

DS:DX (не изменяется) становится полным

ASCIIZ-именем нового файла.

Описание. Открывает (создает) файл с уникальным именем в оглавлении, указанном строкой ASCIIZ, на которую указывает DS:DX. Описание пути должно быть готово к присоединению в его конец имени файла. Необходимо обеспечить минимум 12 байт в конце строки. После возврата строка DS:DX будет дополнена именем файла. DOS создает имя файла из шестнадцатеричных цифр, получаемых из текущих даты и времени. Если имя файла уже существует, DOS продолжает создавать новые имена, пока не получит уникальное имя.

Функция 3dH

DS:DX=адрес строки ASCIIZ с именем файла

Выход. AX=код ошибки, если CF установлен и описатель файла,

если ошибки нет

Описание. В момент открытия файл должен существовать. Файл открывается в выбранном режиме доступа (AL = 0 – для чтения; AL = 1 – для записи; AL = 2 – для чтения и записи) и указатель «чтения/записи» устанавливается в 0.

Функция 3eH

Выход. AX= код ошибки, если CF установлен

Описание. BX содержит описатель файла (handle), возвращенный при открытии. Файл, представленный этим описателем, закрывается, его буфер сбрасываются, а оглавление обновляется корректными размером, временем и датой.

Функция 41H

DS:DX=адрес строки ASCIIZ с именем файла

Выход. AX=код ошибки, если CF установлен

Описание. Имя файла не может содержать обобщенные символы («?» и «*»). Файл удаляется из заданного оглавления заданного диска. Если файл имеет атрибут только чтение, то перед удалением необходимо изменить этот атрибут через функцию 43H.

Функция 42H

Установить указатель чтения/записи (можно также узнать размер файла).

CX:DX=смещение указателя: (CX * 65536) + DX

AL=0 переместить к началу файла + CX:DX

AL=1 переместить к текущей позиции + CX:DX

AL=2 переместить к концу файла — CX:DX

Выход. AX=код ошибки, если CF установлен

DX:AX=новая позиция указателя файла (если нет

Описание. Перемещает логический указатель чтения/записи к нужному адресу, с которого начнется очередная операция чтения или записи. Вызов с AL=2, CX=0, DX=0 возвращает длину файла в DX:AX. DX здесь старшее значащее слово: действительная длина (DX * 65536) + AX.

Функция 3fH

Читать из файла/устройства.

DS:DX=адрес буфера для чтения данных

CX=число считываемых байт

Выход. AX=код ошибки, если CF установлен

AX=число действительно прочитанных байт

Описание. CX байт данных считываются из файла или устройства с описателем, указанным в BX. Данные читаются с текущей позиции указателя чтения/записи файла и помещаются в буфер вызывающей программы, адресуемый через DS:DX.

Всегда необходимо сравнивать возвращаемое значение AX (число прочитанных байт) с CX (запрошенное число байт):

— если AX=CX, (и CF сброшен) – чтение было корректным без ошибок;

Вывод на экран средствами dos

DOS предоставляет следующие возможности вывода текстовой информации на экран:

-обращение к экрану как к файлу, с помощью прерывания DOS Int 21h с

-использование группы функций DOS из диапазона l…Ch (прерывание Int

21h) , реализующих посимвольный вывод, а также вывод строк.

Вывод на экран средствами файловой системы (Int 21h, функция 40h) осуществляется точно так же, как и запись в файл. Используются предопределенные дескрипторы 1 или 2, закрепленные за стандартными устройствами вывода и ошибки, соответственно (по умолчанию — экраном). Число выводимых символов указывается в регистре СХ, а адрес выводимой строки — в регистре DX. Коды 08h (забой), 0Ah (перевод строки), 0Dh (возврат каретки) и некоторые другие рассматриваются, как управляющие и приводят к выполнению соответствующих им действий. Число выводимых символов передается через регистр СХ, однако, если в строке встречается /Z (код 26), вывод прекращается. Дескриптор 1, закрепленный за стандартным устройством вывода, обеспечивает перенаправление вывода. Пусть, например, программа FILTXT содержит строки вывода через дескриптор 1. При запуске программы командой

ее вывод появится на экране, однако команда

приведет к автоматическому образованию файла MYFILE.DOC и записи в него всего вывода программы (на экран ничего не поступит); команда

направит вывод программы FILTXT в тот же файл MYFILE.DOC, при этом новая запись добавится к уже существующим, увеличив тем самым длину файла MYFILE.TXT.

Предопределенный дескриптор 2 (стандартная ошибка) всегда связан с экраном и не может быть перенаправлен.

Если требуется исключить для каких-то операторов вывода или для всей программы в целом возможность перенаправления, то, помимо использования дескриптора стандартной ошибки, можно открыть экран, как файл (с именем CON) функцией 3Dh, получить выделенный системой дескриптор, а затем использовать его в операциях вывода 40h:

screen db ‘CON’,0 ;Имя устройства

handlscr dw 0 ;Новый дескриптор

;Откроем новый дескриптор

mov АН,3Dh ;Функция открытия

mov AL.1 ;Доступ для записи

mov DX,off set screen ;Адрес имени устройства

mov handlscr,AX ;Получили дескриптор

Второй способ вывода на экран текстовой информации реализуется с помощью трех функций прерывания Int 21h:

02h — вывод символа;

06h — прямой ввод-вывод;

09h — вывод строки.

Функция 09h широко используется в системных программах (например, в драйверах) для вывода на экран информационных и диагностических сообщений. Перед вызовом прерывания адрес сообщения засылается в регистр DX; заканчивается сообщение символом $. В сообщение могут быть включены управляющие коды (возврата каретки, перевода строки, забоя и др.), а также Esc-последовательности. Так же, как и при выводе через дескриптор 1 (функция 40h), вывод функцией 09h поступает на стандартное устройство вывода и при запуске программы может быть перенаправлен на другие внешние устройства с помощью операторов перенаправления.

Если в процессе вывода сообщения на экран с клавиатуры поступает код /C, срабатывает стандартная процедура обработки этого прерывания и вывод завершается (как и вся программа в целом). Для надежной обработки прерывания по /C следует включать режим BREAK (командой DOS BREAK ON).

Функция 02h вызывает передачу на экран (точнее — на стандартное устройство вывода) одного символа, помещаемого в регистр DL. Для вывода строки функцию следует использовать в цикле. В остальном она не отличается от функции 09h (перенаправление, обработка управляющих кодов, реакция на ввод с клавиатуры /C).

Функция 06h (прямой ввод-вывод через консоль) используется в тех случаях, когда надо исключить стандартную реакцию системы на ввод с клавиатуры /C. В остальном она действует так же, как функции 09h и 02h, однако обеспечивает не только вывод, но ввод. В случае вывода код ASCII передаваемого символа засылается в регистр DL; при вводе DL=FFh.

Средства DOS в чистом виде позволяют выводить на экран только черно-белый текст; возможности позиционирования текста на экране ограничиваются использованием символов возврата каретки (0Dh) и перевода строки (0Ah). Для вывода на экран средствами DOS цветных изображений следует использовать управляющие Esc-последовательности, реализуемые драйвером ANSI.SYS.

Статьи к прочтению:

How to use 2+ Monitors/Screens with Pro Tools — Pro Tools Quick Tip

Похожие статьи:

Рассмотрим теперь средства вывода на экран, реализуемые драйвером BIOS, программное обращение к которому осуществляется с помощью прерывания Int 10h. При…

Система ввода/вывода Си++ действует через так называемые потоки (streams). Поток ввода/вывода – это логическое устройство, которое выдает и принимает…

Задание

Разработать программу, осуществляющую запись команд графического экрана в файл формата BMP.

Описание работы программы

DOS, функция 09h int 21h Запись строки на стандартный вывод

DS:DX — адрес строки, заканчивающейся символом «$» (ASCII 24h)

Строка, исключая завершающий ее символ «$», посылается на стандартный вывод.

DOS, функция 3Ch Создать файл через описатель

DS:DX — адрес строки ASCIZ с именем файла

СХ — атрибут файла

CF=0, если функция выполнена успешно

АХ — описатель файла CF=1, если при выполнении функции возникли ошибки

DOS, функция 40h Писать в файл через описатель

ВХ — описатель файла

DS:DX — адрес буфера, содержащего записываемые данные

СХ — число записываемых байт

CF=0, если функция выполнена успешно

AX — число действительно записанных байт CF°1, если при выполнении функции возникли ошибки

СХ байт данных записываются в файл или на устройство с описателем, заданным в ВХ. Данные берутся из буфера, адресуемого через DS:DX, и записываются, начиная с текущей позиции указателя чтения/записи файла. Чтобы установить указатель файла, если необходимо, можно использовать функцию 42h. Обновляет указатель чтения/записи файла, чтобы подготовиться к последующим операциям чтения или записи.

DOS, функция 3Eh Закрыть описатель файла

АН-ЗЕh ВХ — описатель файла

CF=0, если функция выполнилась успешно

АХ не сохранен CF=1, если при выполнении функции возникли ошибки

ВХ содержит описатель файла (handle), возвращенный при открытии. Файл, представленный этим описателем, закрывается, его буферы сбрасываются и оглавление обновляется корректными размером, временем и датой. Из-за недостатка описателей файлов (максимум 20, по умолчанию установлено 8), возможно, придется закрыть часть текущих описателей, как, например, описатель 3 (стандартный AUX).

DOS, функция 35h int 21h Получить вектор прерывания

AH-35h AL — номер прерывания (OOh до FFh)

ES:BX — адрес обработчика прерывания

Возвращает значение вектора прерывания для INT (AL), то есть загру- жает в ВХ 0000:[AL*4], а в ES — 0000:[(AL*4)+2].

DOS, функция 25h Установить вектор прерывания

AL — номер прерывания

DS:DX — вектор прерывания — адрес программы обработки прерывания

Описание. Устанавливает значение элемента таблицы векторов прерываний для прерывания с номером AL, равным DS:DX. Это равносильно записи 4-байтового адреса в 0000:(AL*4), но, в отличие от прямой записи, DOS знает, что происходит, и гарантирует, что в момент записи прерывания будут заблокированы. DOS, функция 31h int 21h Завершиться и остаться резидентным

DX — объем памяти, оставляемой резидентной (в параграфах)

Выходит в родительский процесс, сохраняя код выхода в AL. Код выхода можно получить через функцию 4Dh. DOS устанавливает начальное распределение памяти, как специфицировано в DX, и возвращает управление родительскому процессу, оставляя указанную память резидентной (число байт равно DX*16). Эта функция перекрывает функцию INT 27h, которая не возвращает код выхода и не способна установить резидентную программу, размер которой превышает 64Кбайт. Int 10h Чтение нескольких регистров таблицы цветов (регистров ЦАП) — подфункция 17h

Функция производит чтение 18-битовых значений из нескольких последовательно расположенных регистров таблицы цветов:

Dos fn 40h: писать в файл через описатель

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

короче, тут не сборище придурков-альтруистов, которым нечего делать и они ждут-не дождутся когда вы их попросите
что-то сделать _за_ вас
если позарез надо и вы даже готовы заплатить, тогда вам сюда
удачи

Просьба все большие листинги оформлять тегом more.

если вам вдруг не отвечают или ответ вас не устраивает
и вообще полезно прочитать всем спрашивающим Всего записей: 3951 | Зарегистр. 29-07-2003 | Отправлено: 01:42 29-11-2006 | Исправлено: ShIvADeSt, 03:52 26-07-2020

HRyk

Junior Member

Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору Друзья,помогите доработать программу.
Имеется программа на Assembler которая ищет целое слово, введенное с клавиатуры в файле «wesna.dat». как сделать так, чтобы поиск осуществлялся не в одном файле, а в нескольких-например, в четырех.
Подробнее.

Читаем шапку!

Всего записей: 162 | Зарегистр. 04-11-2006 | Отправлено: 21:02 08-12-2006 | Исправлено: ShIvADeSt, 02:04 11-12-2006
rain87

Advanced Member

Редактировать | Профиль | Сообщение | ICQ | Цитировать | Сообщить модератору HRyk
с кодом — рабочий или нет — не разбирался
Подробнее.
Всего записей: 1744 | Зарегистр. 21-06-2006 | Отправлено: 22:54 08-12-2006 | Исправлено: ShIvADeSt, 02:05 11-12-2006
HRyk

Junior Member

Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору Товарищи, нужна помощь в решении. Написал две программки, 1-я заполняет файл «константными записями», заданными в самой программе,
2-я осуществляет буферезированный ввод строки.
Как теперь осуществить буферезированное заполнение файла?

код 1-ой программы:
; заполнение
; файла ‘wesna.dat’

sega segment
assume cs:sega,ds:sega
org 100h
beg: mov ah,3dh ;открытие файла
mov al,1 ;атрибут 0-чт 1-з 2-чт-з
lea dx,fname
int 21h
mov handle,ax
mov si,0
mov di,0
m1: mov ah,42h ;установка указ
mov al,0 ;код метода смещ 0-абс смещ
mov bx,handle ;1-от нач ф с текущ позиции
;2- от конца ф с текущ поз
mov cx,0 ;старш часть смещ
mov dx,t ;младш часть смещ
int 21h
mov ah,40h ;чтение ф
mov bx,handle
mov cx,5 ;сколько читать
lea dx,buf[di] ;куда читать
int 21H
add di,5
add t,5
inc si
cmp si,5
jne m1

int 20h
t dw 0
fname db ‘wesna.dat’,0
handle dw ?
buf db ‘a111k’
db ‘b222l’
db ‘c333k’
db ‘d444l’
db ‘e555k’

код 2-ой программы:
art segment
assume cs:art,ds:art
org 100h
m1: mov ah,0ah
lea dx,buf
int 21h
mov ah,09h
lea dx,t3
int 21h
mov bl,t1
mov di,0
m2: mov al,t2[di]
mov t4[di],al
inc di
cmp di,bx
jne m2
mov t4[di],’$’
mov ah,09h
lea dx,t4
int 21h
mov ah,08h
int 21h
int 20h
; fname db ‘leto.dat’,0
handle dw ?
buf db 10
t1 db ?
t2 db 9 dup (‘ ‘),’$’
t3 db 10,13,’$’
t4 db 9 dup (‘ ‘),’$’
art ends
end m1

Всего записей: 162 | Зарегистр. 04-11-2006 | Отправлено: 15:28 16-12-2006 | Исправлено: HRyk, 18:06 16-12-2006
rain87

Advanced Member

Редактировать | Профиль | Сообщение | ICQ | Цитировать | Сообщить модератору блин. и что понимать под
Цитата:

буферезированное заполнение файла

?

зы. оформь всё в more, ShIvADeSt’у скоро надоест исправлять

Всего записей: 1744 | Зарегистр. 21-06-2006 | Отправлено: 21:29 16-12-2006
HRyk

Junior Member

Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору Нет проблем. Специально для ShIvADeSt’а готов на любые трудности. Буферезированное заполнение файла означает заполнение его любыми словами, вводимыми с клавиатуры. (Сейчас я умею заполнять файл «пробитыми константами» вида: a111k,b222l,c333k, как в программке 1) (Во второй программке я осилил буферезированный ввод строки) теперь нужно «совместить» эти программы, тоесть заполнять файл словами произвольной длины, вводимыми с клавиатуры, до тех пор, пока в файле не окажется некоторое количество символов. Не могу осилить уже несколько дней. Rain87, на тебя вся надежда
Всего записей: 162 | Зарегистр. 04-11-2006 | Отправлено: 21:43 16-12-2006
rain87

Advanced Member

Редактировать | Профиль | Сообщение | ICQ | Цитировать | Сообщить модератору ShIvADeSt конечно будет безумно рад, узнав что такое буферизированное заполнение файла но я имел в виду то что написано в первом посте —
Цитата:

все большие листинги оформлять тегом more.

по поводу задач щас посмотрю

Добавлено:
HRyk
в общем, ни одна из прог работать не хочет, глючат и вылетают. разбираться почему — как бы влом, вылетают даже после фикса очевидных багов (вроде неинициализации DS и т.п.). может у меня компилер несовместим с твоим? у меня tasm5.0

по поводу задачи — ну а в чём проблема то? если умеешь вводить строку буферизовано вводи её (столько символов, сколько надо), а потом пиши в файл всё что ввёл

зы. по-моему разумно пользовать функции расширенного чтения и записи —
DOS Fn 3fH: Читать файл через описатель
DOS Fn 40H: Писать в файл через описатель

Всего записей: 1744 | Зарегистр. 21-06-2006 | Отправлено: 22:28 16-12-2006
HRyk

Junior Member

Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору ОК, Rain87, подскажи такую штукенцию: я прогу написал, она файл заполняет словами, вводимыми с клавы, но эти слова (в файле) если они меньше 9 символов, забиваются «значками» случайными, как это исправить?
Всего записей: 162 | Зарегистр. 04-11-2006 | Отправлено: 14:38 17-12-2006 | Исправлено: HRyk, 17:06 17-12-2006
rain87

Advanced Member

Редактировать | Профиль | Сообщение | ICQ | Цитировать | Сообщить модератору HRyk
когда ты читаешь 21 интом 10 функцией, то она тебе возвращает количество реально считанных символов. вот ты когда пишешь в файл, СХ ставь не 9, а вот это число, которое вернула 10 функция инт 21
Всего записей: 1744 | Зарегистр. 21-06-2006 | Отправлено: 20:04 17-12-2006
Morpy

Newbie

Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору У меня две нерешённых задачи по ассемблеру. Интересует вариант решения нижеприведённых заданий за деньги.

1)Шестнадцатеричное число представлено в виде строки ASCII. Преобразовать данную строку во внутреннее представление. Предусмотреть возможность многобайтного результата.

2)Заданы массивы A[N], B[N] из элементов типа word(целое 16-ти разрядное со знаком). Составить программу, формирующую массив C[N] из разности элементов массивов А и В.
(с[i]=a[i]-b[i]). Размерность элементов массива с[n] должна обеспечивать корректное вычитание(если результат не умещается в 16-ти разрядах)

Надеюсь на Вашу помощь.

Всего записей: 3 | Зарегистр. 18-12-2006 | Отправлено: 20:36 18-12-2006
akaGM

Platinum Member

Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору для
Цитата:

Интересует вариант решения нижеприведённых заданий за деньги

сюда

Всего записей: 19544 | Зарегистр. 06-12-2002 | Отправлено: 20:48 18-12-2006
Morpy

Newbie

Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору Перенёс. Извините, просмотрел не все разделы :о
Всего записей: 3 | Зарегистр. 18-12-2006 | Отправлено: 23:12 18-12-2006
AHuTA

Newbie

Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору Объясните пож. как запустить TASM.exe в windowsXP?
или дайте ссылку. Спасибо!
Всего записей: 4 | Зарегистр. 15-12-2006 | Отправлено: 22:33 25-12-2006 | Исправлено: AHuTA, 22:36 25-12-2006
TaHIOIIIkA

Newbie

Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору Ребят помогите пожалуйста решить пару задач?
времени обс. не хватает!
1 Ввести с клавиатуры строку. Посчитать в ней количество запятых.
Вывести результаты на экран.

2 Разработать две подпрограммы, одна из которых сравнивает
две строки по лексикографическому порядку,
а другая обменивает значения двух строк. Разработать программу,
которая вводит с клавиатуры несколь-ко строк (конец ввода пустая строка)
и сортирует их в лексикографическом порядке.(способ передачи параметров
в процедуру выбирать произвольно. Зациклить программу по вводу строки,
а признаком окончания работы считать ввод пустой строки.)

Всего записей: 6 | Зарегистр. 25-12-2006 | Отправлено: 22:58 25-12-2006
rain87

Advanced Member

Редактировать | Профиль | Сообщение | ICQ | Цитировать | Сообщить модератору TaHIOIIIkA
1. Подробнее.
Всего записей: 1744 | Зарегистр. 21-06-2006 | Отправлено: 18:06 26-12-2006
Qwezar

Member

Редактировать | Профиль | Сообщение | ICQ | Цитировать | Сообщить модератору Люди, помогите плиз, эта прога ищет все полиндромы до 5000 нужно их вывести на экран и в файл, с экраном все ОК, а вот с файлом проблема, создается пкстой txt и все.

Код:

MODEL TINY
.486
.CODE
assume cs:@code,ds:@code,es:@code,ss:@code
org 100h

mov ah,3ch ;Создание файла
mov cx,0 ;Для записи и чтения
mov dx,offset fn
mov bx,fnd
int 21h
mov fnd,ax

mov word ptr count,0
;до 10
mov eax,-1
p1:
inc eax
push eax
inc count
call print_n pascal,eax,word ptr 0,word ptr 1,word ptr 1,word ptr 0,word ptr 0
mov ah,2h
mov dl,32
int 21h

;двузначные
mov eax,-1
mov ebx,0
p2:

t1: inc eax
jmp t3

t2: inc ebx
jmp t3

t3:
cmp eax,ebx
jnz nm1

push eax
push ebx
inc count
call print_n pascal,eax,word ptr 0,word ptr 1,word ptr 1,word ptr 0,word ptr 0
call print_n pascal,ebx,word ptr 0,word ptr 1,word ptr 1,word ptr 0,word ptr 0
mov ah,2h
mov dl,32
int 21h

pop ebx
pop eax

nm1:
cmp ebx,9
jz nm2

nm2:
mov ebx,0
cmp eax,9
jz p3

jmp t1
;трехзначные

p3:
mov eax,0
mov ebx,0
mov ecx,0
jmp r4

r1: inc eax
jmp r4

r2: inc ebx
jmp r4

r3: inc ecx
jmp r4

r4:
cmp eax,ecx
jnz mn1

push eax
push ebx
push ecx
inc count
call print_n pascal,eax,word ptr 0,word ptr 1,word ptr 1,word ptr 0,word ptr 0
call print_n pascal,ebx,word ptr 0,word ptr 1,word ptr 1,word ptr 0,word ptr 0
call print_n pascal,ecx,word ptr 0,word ptr 1,word ptr 1,word ptr 0,word ptr 0
mov ah,2h
mov dl,32
int 21h

pop ecx
pop ebx
pop eax
mn1:
cmp ecx,9
jz mn2

mn2:
mov ecx,0
cmp ebx,9
jz mn3
jmp r2
mn3:
mov ebx,0
cmp eax,9
jz p4

p4:
;четырехзначные
mov eax,0
mov ebx,0
mov ecx,0
mov edx,0
jmp m5

m1: inc eax
jmp m5

m2: inc ebx
jmp m5

m3: inc ecx
jmp m5

m4: inc edx
jmp m5

m5:
cmp eax,edx
jnz n1
cmp ebx,ecx
jz m7
n1:
cmp edx,9
jz n2

n2:
mov edx,0
cmp ecx,9
jz n3

n3:
mov ecx,0
cmp ebx,9
jz n4

n4:
mov ebx,0
cmp eax,4
jz exit

push eax
push ebx
push ecx
push edx

call print_n pascal,eax,word ptr 0,word ptr 1,word ptr 1,word ptr 0,word ptr 0
call print_n pascal,ebx,word ptr 0,word ptr 1,word ptr 1,word ptr 0,word ptr 0
call print_n pascal,ecx,word ptr 0,word ptr 1,word ptr 1,word ptr 0,word ptr 0
call print_n pascal,edx,word ptr 0,word ptr 1,word ptr 1,word ptr 0,word ptr 0

mov ah,2h
mov dl,32
int 21h

pop edx
pop ecx
pop ebx
pop eax

exit:
call print_n pascal,word ptr count,word ptr 0,word ptr 1,word ptr 1,word ptr 0,word ptr 0
mov ah,4ch
int 21h

print_n proc near ;Процедура вывода десятичного числа на экран и в файл
locals @@
arg beg,zero,f_handle,pp0,num_off:word,numb:dword=arg_size

;Аргументы процедуры:
;numb: число(dword)
;num_off: смещение строки, где содержится число(word)
;pp0: 0-не печатать, 1-на экран, 2-в файл(word)
;f_handle: дескриптор файла(word)
;zero: 0-не печатать ведущие нули(word)
;beg: отступ в знаках от начала печатаемого числа(

Всего записей: 360 | Зарегистр. 31-12-2006 | Отправлено: 17:07 04-01-2007
rain87

Advanced Member

Редактировать | Профиль | Сообщение | ICQ | Цитировать | Сообщить модератору Qwezar
лень код смотреть, вопрос навскидку — файл не забываете закрывать?
Всего записей: 1744 | Зарегистр. 21-06-2006 | Отправлено: 19:05 04-01-2007
TaHIOIIIkA

Newbie

Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору rain87
большое спасибо!
Поможешь со второй, plzzzz?=)
Всего записей: 6 | Зарегистр. 25-12-2006 | Отправлено: 16:20 08-01-2007
rain87

Advanced Member

Редактировать | Профиль | Сообщение | ICQ | Цитировать | Сообщить модератору TaHIOIIIkA
щас помогу

Добавлено:
блин. слушай, а обязательно делать перестановку строк? имхо проще сделать массив индексов, т.е. 1й элемент обозначает, какая строка стоит на 1м месте, 2й — какая на 2м и т.д.

т.е. если надо обменять строки 3 и 5 местами, то я в массиве индексов обменяю 3 и 5 элемент, а сами строки останутся на месте. а при выводе я выведу сначала строку, номер которой записан в 3 ячейке индексного массива — т.е. 5ю строку, а потом — то что в 5й ячейке — т.е. 3ю строку

если же надо обязательно перемещать строки (хоть это и тупость), то надо делать строки фиксированной длины — т.е. не любой, а жёстко заданной. иначе перемещение строк будет сущим кошмаром

Dos fn 40h: писать в файл через описатель

Ассемблер? Это просто! Учимся программировать (FAQ)
______________________________________

Вопросы, которые поступили от подписчиков рассылки
«Ассемблер? Это просто! Учимся программировать»

Дата выхода: 2001-08-16

Здравствуйте!
Значит, история такая: мой приятель купил винт Quantum AS на 10 гигов. И принёс мне — временно, пока он не купит все остальные части компа. А у меня его не видно.
BIOS-то этот винт детектит, а вот Виндоуз — ни в какую. Я джамперы и так и сяк ставил — безуспешно.
Быть может, дело в моей матплате Acorp для Pentium MMX ?
Она у меня старая, 1997 года. Если так, то мне перед тем, как тоже винт покупать, и матплату сменить? (А заодно и процессор, и корпус с AT на ATX. )
Кстати, какую лучше брать матплату для Дюрона?
Спасибо за внимание.
Константин.


Привет!
А ты его отформатировал? Если нет, то вначале иди запускай fdisk, создавай логические диски, а потом форматируй их с помощью
format disk:
Если хочешь, чтобы весь диск можно было одним куском отформатировать в программе fdisk надо согласиться на поддержку больших дисков( >2ГБ).

Отправил эксперт: Александр
Эксперт отправил ответов (всего): 65

Экспертная группа: Модели, виды и типы компьютеров (hard)

Пишу программу работающую в реальном режиме времени
Использую прерывание по таймеру
Основная программа пишется на Паскале (Borland Pascal 7.0)
Иницилизация таймера и обработка прерывания на ассемблере.
В обработке прерывания работа с lpt-портом. И. проблема :
добавление команд out dx,al (время выполнения по некоторым
данным на 486 машине — 10 тактов, а у меня Celeron 433) приводит к изменению промежутка времени между прерываниями. Я в шоке! Помогите — горит проект и меня ждет жестокая расправа.
Ниже привожу исходник.

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

Отправил эксперт: Василий
Эксперт отправил ответов (всего): 20

Экспертная группа: Общие вопросы по программированию на Ассемблере под DOS

Здравствуйте, Broken Sword, уважаемые эксперты,

_программа_ должна трассировать Int 21h и сама принимять решение о местонахождении заданной функции.
Весь процесс проходит _автоматически_ без человеческого вмешательства.

Ко всему, возможно, добавляются защиты от трассировки, вроде уничтожения векторов 1 и 3, проверка и подмена флагов трассировки, запись «мусора» в регистры TR1, TR2 . и т.д.

С нетерпением жду ответа,
Андрей.

Не понял, ты хочешь, чтобы я тебе такую программу написал или у тебя есть идеи, но требуются консультации?

Отправил эксперт: Василий
Эксперт отправил ответов (всего): 21

Экспертная группа: Общие вопросы по программированию на Ассемблере под DOS

У меня проблема с завершением работы Windows98, мне приходится два раза повотрять операцию «Выключить компьютер». Каким образом мне изабивться от этого глюка?

Здравствуйте, Владимир!
Могу сходу предложить три варианта действий:
1.Проверить машину стандартным regclean-ом (можно взять на download.com.
2.Проверить машину с помощью приблуды типа Нортон Виндоктор.
Если ошибка была связана со сбоем в реестре — должно помочь.
3.Вариант, связанный с запуском приложения типа AVX ICQ checker.
(Мне также приходится по два раза выключаться, после первого раза он выкидывает aqmon, после второго — собственно выключается.Но если пользуешься ICQ и не хочешь заловить вируса луче перестраховаться, не так ли =|;о) ).

Отправил эксперт: RANDOM
Эксперт отправил ответов (всего): 10

Экспертная группа: Пользовательская работа с Windows

Как взять с дискетв серийный номр?
Как записать в файл текстовую информациь?

Описание: CX байт данных записывается в файл или на устройство с описателем,
заданным в BX. Данные берутся из буфера, адресуемого через DS:DX.
Данные записываются, начиная с текущей позиции указателя
чтения/записи файла.

Используйте функцию 42H LSEEK, чтобы установить указатель файла,
если необходимо (OPEN сбрасывает указатель в 0).

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

Вы должны всегда сравнивать возвращаемое значение AX (число запи-
санных байт) с CX (запрошенное число байт для записи).
если AX = CX, запись была успешной
если AX Эксперт отправил ответов (всего): 22

Экспертная группа: Общие вопросы по программированию на Ассемблере под DOS

Здравствуйте!
Значит, история такая: мой приятель купил винт Quantum AS на 10 гигов. И принёс мне — временно, пока он не купит все остальные части компа. А у меня его не видно.
BIOS-то этот винт детектит, а вот Виндоуз — ни в какую. Я джамперы и так и сяк ставил — безуспешно.
Быть может, дело в моей матплате Acorp для Pentium MMX ?
Она у меня старая, 1997 года. Если так, то мне перед тем, как тоже винт покупать, и матплату сменить? (А заодно и процессор, и корпус с AT на ATX. )
Кстати, какую лучше брать матплату для Дюрона?
Спасибо за внимание.
Константин.

Здравствуйте, Константин!
Просто напросто у тебя жёсткий диск новый, поэтому на нем нет никакой файловой системы(для Win — обычно Fat 32, для Пингвина — Ext2+Swap).
Тебе надо найти FDisk(стандартная для Win) или Partition Magic, затем создать основной раздел дос на нём(или ещё дополнительный), тогда всё будет в порядке.
А, вообще, обычно к жёсткому диску при покупке должна прилагаться дискета с утилитой установки жесткого диска!

Отправил эксперт: Orlando
Эксперт отправил ответов (всего): 10

Экспертная группа: Модели, виды и типы компьютеров (hard)

Как взять с дискетв серийный номр?
Как записать в файл текстовую информациь?

Здравствуйте, fagot!
По первому вопросу читай выпуск 056 этой рассылки, там есть как найти серийный номер винта(и дискеты соответственно)
По второму вопросу
Для открытия функция 3dh
al=Access mode(0-read, 1-write, 2-both)
DS:DX=ASCIIZ строка полного пути файла
Выход
если бит С=1 AX=Error code, иначе AX=Handle
Тогда для записи используем ф-цию 40h
AX=40h
BX=Handle
CX=кол-во байт для записи
DS:DX-адрес буфера
Выход
если бит С=1 AX=кол-во реально записанных байт

Отправил эксперт: Sensey
Эксперт отправил ответов (всего): 76

Экспертная группа: Общие вопросы по программированию на Ассемблере под DOS

Здравствуйте!
Значит, история такая: мой приятель купил винт Quantum AS на 10 гигов. И принёс мне — временно, пока он не купит все остальные части компа. А у меня его не видно.
BIOS-то этот винт детектит, а вот Виндоуз — ни в какую. Я джамперы и так и сяк ставил — безуспешно.
Быть может, дело в моей матплате Acorp для Pentium MMX ?
Она у меня старая, 1997 года. Если так, то мне перед тем, как тоже винт покупать, и матплату сменить? (А заодно и процессор, и корпус с AT на ATX. )
Кстати, какую лучше брать матплату для Дюрона?
Спасибо за внимание.
Константин.

Здравствуйте, Константин!
Кажись ты прав- твоя мать видит винты до 8 гиг
Хотя! Попробуй его на отдельный шлейф, причем строго Primary Master
А загружайся с другого, то есть типа с D
Для Дюрона лучше всего Soltek, хотя Canyon лучше, но дороже
И не пожалей лишних 3$ на корпус, возьми CodeGen- он пошире, охлаждение получше, и Дюрон с хорошим вентилятором точно влезет.

Отправил эксперт: Sensey
Эксперт отправил ответов (всего): 77

Экспертная группа: Модели, виды и типы компьютеров (hard)

с ЛЕМЪ РЮЙЮЪ ОПНАКЕЛЮ. лЮРЕПХМЯЙЮЪ ОКЮРЮ Asus CUEP2-M. мЮЯРПНИЙХ ОН СЛНКВЮМХЧ ГЮБНДЯЙХЕ. оНД DOCНЛ (МЕ ГЮЦПСФЮЪ Windows) Ъ МЕ БХФС ЛШЬЭ Х ХМНЦДЮ ОПНОЮДЮЕР ЙКЮБХЮРСПЮ. щРН ВРН МЕХЯОПЮБМНЯРЭ ЛЮРЕПХ ХКХ ЦДЕ-РН Ъ МЕСЯКЕДХК, МН МЮЯРПНИЙХ РН ГЮБНДЯЙХЕ. (Х ОНРНЛ МЮЯЙНКЭЙН Ъ ГМЮЧ ЛШЬЭ Х ЙКЮБХЮРСПЮ МХЙЮЙ МЕМЮЯРПЮХБЮЧРЯЪ Б BIOS). нОЕПЮЖХНММСЧ ЯХЯРЕЛС ОЕПЕЯРЮБХК, ПЮГЗЕЛШ ЛШЬХ Х ЙКЮБХЮРСПШ PS/2

гДПЮБЯРБСИРЕ, юКЕЙЯЕИ!
хГБХМХРЕ ГЮ БЯРПЕВМШИ БНОПНЯ, Ю бШ ДПЮИБЕП ДКЪ ЛШЬХ СЯРЮМЮБКХБЮКХ?
ю ЙКЮБХЮРСПЮ ХМНЦДЮ РЮЙ ЯЕАЪ БЕДёР.

Отправил эксперт: яРЮЯ
Эксперт отправил ответов (всего): 6

Экспертная группа: лНДЕКХ, БХДШ Х РХОШ ЙНЛОЭЧРЕПНБ (hard)

КАК
МОЖНО
ПОЛУЧИТЬ
ВСЮ
рассылку с начала.
Чтобы не повторять вопросы
не отнимать время
и неказаться совсем глупым

С уважением к Твоей просветительной деятельности
Олег

Олег Никуленков!
http://www.kalashnikoff.ru/, ссылка Ассемблер, там архив — ВСЕ номера рассылки «Ассемблер, это просто», и 33 (помоему) FAQ из 60 полных, все FAQ можно получить токо осенью

Отправил эксперт: Broken Sword
Эксперт отправил ответов (всего): 51

Экспертная группа: Общие вопросы по программированию на Ассемблере под Win32

гДПЮБЯРБСИРЕ, СБЮФЮЕЛШЕ ЩЙЯОЕПРШ.
мЕДЮБМН С МЮЯ КНЙЮКЭМСЧ ЯЕРЭ ОПНРЪМСКХ. яПЮГС ФЕ БНГМХЙКЮ ОПНАКЕЛЮ, Р.Й. Ъ ПЮАНРЮЧ ХГ ОНД ДБСУ НЯЕИ — WinXP RC2 Х Win98. рЮЙ БНР, ЯСРЭ ОПНАКЕЛШ Б РНЛ, ВРН ХГ ОНД БХМ98 БЯё МНПЛЮКЭМН ПЮЯЬЮПХБЮЕРЯЪ, БЯё ОНКЭГНБЮРЕКХ ОЮОЙХ БХДЪР, Б МЕЙНРНПШЕ ЛНЦСР ОХЯЮРЭ ЦДЕ ПЮГПЕЬЕМН, Ю ОНД WinXP ОНВЕЛС РПЕАСЕРЯЪ ОЮПНКЭ ОПХВЕЛ Й ОЮОЙЕ IPC$. лНФЕР ЩРН МЕДНДЕКЙХ ЙЮЙХЕ, БЕДЭ НТХЖХЮКЭМНЦН ПЕКХГЮ ЕЫЕ МЕ БШЬКН. ю ЛНФЕР БЯё-РЮЙХ ЛНФМН ЙЮЙ-МХАСДЭ МЮЯРПНХРЭ ?
P.s. вРН-РН ОНУНФЕЕ МЮАКЧДЮЕРЯЪ Х Я Win2000 Professional SP1.
я СБЮФЕМХЕЛ, дЛХРПХИ.

гДПЮБЯРБСИРЕ, дЛХРПХИ!
б НОЕПЮЖХНМЙЮУ НЯМНБЮММШУ МЮ NT (NT 4.0, w2k, winXP) ОПХЛЕМЪЕРЯЪ ДПСЦЮЪ ЯХЯРЕЛЮ ОПНБЕПЙХ ОНКЭГНБЮРЕКЪ. р.Е. ВРНАШ ОНКСВХРЭ ДНЯРСО Й ПЮЯЬЮПЕМНЛС ПЕЯСПЯС МЕНАУНДХЛН ББНДХРЭ МЕ РНКЭЙН ОЮПНКЭ МН Х КНЦХМ. ЩРН ЛНФМН ОПХ ОНЛНЫХ ЯОЕЖХЮКЭМНИ ОПНЦПЮЛЙХ (e.g. Essential NetTools ЙНЛОЮМХХ «TamosSoft» www.tamos.com), КХАН МЮДН КНЦХМХРЭЯЪ Б win’9x Я ХЛЕМЕЛ Х ОЮПНКЕЛ ОНКЭГНБЮРЕКЪ ЙНРНПНЛС МЮ WinXP ПЮГПЕЬёМ ДНЯРСО Й ПЮЯЬЮПЕМНЛС ПЕЯСПЯС.
ю КСВЬЕ ОНЯРЮБЭРЕ ЯЕПБЕП, ОСРЮМХЖШ АСДЕР ЛЕМЭЬЕ.

Отправил эксперт: яРЮЯ
Эксперт отправил ответов (всего): 8

Экспертная группа: оНКЭГНБЮРЕКЭЯЙЮЪ ПЮАНРЮ Я Windows

Как взять с дискетв серийный номр?
Как записать в файл текстовую информациь?

Уважаемый fagot! Вот пример проги, замени ‘Текст’
text_mes db ‘Текст’ на любой свой, и придумай имя файла
filename db ‘c:\text.txt’,0

Отправил эксперт: Broken Sword
Эксперт отправил ответов (всего): 52

Экспертная группа: Общие вопросы по программированию на Ассемблере под DOS

Здравствуйте!
У меня усть вопросики :)
1) Где можно найти tasm32.exe, tlink32.exe & import32.lib
2) Сейчас я пользуюсь Масмом, но у меня есть проблемка с созданием экзешника вот, то что я делаю.
файл Digger.asm:

includelib c:\masm\lib\kernel32.lib
extrn _imp_ExitProcess@4:dword
ExitProcess equ _imp_ExitProcess@4

.386
.model flat,stdcall
option casemap:none
.data
;Инициализированные данные
.data?
;Неинициализированные данные
.code
start: ;Точка входа

push 0
call ExitProcess ;Выход из программы
end start
—————-
А потом я запускаю.
masm /Ic:\masm digger.asm
link digger.obj,digger.exe
—————-
Выводиться сообщение об шибке.
Microsoft (R) Segmented Executable Linker Version 5.31.009 Jul 13 1992
Copyright (C) Microsoft Corp 1984-1992. All rights reserved.

List File [nul.map]:
Libraries [.lib]:
Definitions File [nul.def]:
LINK : warning L4021: no stack segment

digger.obj(digger.asm) : error L2029: ‘_IMP_EXITPROCESS@4’ : unresolved external

There was 1 error detected
———————-
Может я неправильно создал kernal32.lib.
НО kernal32.lib от Visual C++ не подошёл.
ии я сделал
implib.exe kernal32.lib kernal32.dll
(предварительно скопировав dll в ту директорию где запускал implib :)

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

Или (что вернее) в данном случае у тебя просто отсутствует глобальная переменная _imp_ExitProcess@4

Или же masm все имена приводит к нижнему регистру.

Или же все вместе. :) не знаю точно.

Отправил эксперт: Dron (http://spawnhole.narod.ru/asmos/asmos.html — Операционная система с нуля!)
Эксперт отправил ответов (всего): 53

Экспертная группа: Работа с MASM/TASM

А существуют ли разные всякие ассемблерные библиотеки? Где их можно поискать? (может есть сайты специальные?) Мне например нужен оконный интерфейс для ассемблерной проги. Не как Turbo Vision конечно, поскромнее, но желательно готовый.
PS прилиновать TV не предлогайте :)

Ну и обленился же ты!
Самому состряпать слабо?! Можно даже, используя объекты, благо TASM их поддерживает. Могу если что с этим помочь.

Отправил эксперт: Василий
Эксперт отправил ответов (всего): 23

Экспертная группа: Общие вопросы по программированию на Ассемблере под DOS

Здравствуйте,уважаемые эксперты. ) :)
Ответьте, пожалуйста вот на такой вопрос:)как создать файл, что-нить записать в него, удалить файл с диска, памяти, переместить файл, скопировать?

Здравствуйте, Веселый Эдик!

Как много ты сразу хочешь знать.

Обрати свое внимание на следующие функции DOS:

int 21h
fn 3ch Create file via handle
fn 3dh Open file via handle
fn 3eh Close file via handle
fn 3fh Read from file via handle
fn 40h Write to file via handle
fn 41h Delete file

Функции копирования файлов в досе не существует. реализуется через чтение/запись.

Помимо функций, работающих черех handle есть еще функции работающие через FCB, ноони гораздо неудобнее!.

Отправил эксперт: Dron (http://spawnhole.narod.ru/asmos/asmos.html — Операционная система с нуля!)
Эксперт отправил ответов (всего): 54

Экспертная группа: Общие вопросы по программированию на Ассемблере под DOS

Подписаться на рассылки

Форма подачи вопроса

ВНИМАНИЕ. Пожалуйста, НЕ высылайте один вопрос сразу нескольким подгруппам! Дополнения к вопросам, на которые эксперт уже ответил НЕ следует направлять ведущему рассылки (я не успеваю!). Просто скомбинируйте первый вопрос и дополнение к нему, а затем заново отправьте его, используя приведенную ниже форму!

Нажимайте кнопку «Отправить» только ОДИН раз и дождитесь полной загрузки страницы, иначе вопрос будет продублирован!

Ведущий рассылки,

По вопросам сотрудничества, рекламы и спонсорства обращайтесь:

Файл дескриптор в Linux с примерами

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

Конечно же я ответил, что посмотрю, чем занято это место и если возможно, то почищу место.
Тогда интервьюер спросил, а что если на разделе нет свободного места, но и файлов, которые бы занимали все место, ты тоже не видишь?

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

Интервьюер прервал меня на последнем слове, дополнив свой вопрос: «Предположим, что данные нам не нужны, это просто дебаг лог, но приложение не работает из-за того, что не может записать дебаг»?

«окей», — ответил я, «мы можем выключить дебаг в конфиге приложения и перезапустить его».
Интервьюер возразил: «Нет, приложение мы перезапустить не можем, у нас в памяти все еще хранятся важные данные, а к самому сервису подключены важные клиенты, которых мы не можем заставлять переподключаться заново».

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

Интервьюер остался доволен, а я нет.

Тогда я подумал, почему человек, проверяющий мои знания, не копает глубже? А что, если данные все-таки важны? Что если мы не можем перезапускать процесс, и при этом этот процесс пишет на файловую систему в раздел, на котором нет свободного места? Что если мы не можем потерять не только уже записанные данные, но и те данные, что этот процесс пишет или пытается записать?

Тузик

В начале моей карьеры я пытался создать небольшое приложение, в котором нужно было хранить информацию о пользователях. И тогда я думал, а как мне сопоставить пользователя к его данным. Есть, например, у меня Иванов Иван Иваныч, и есть у него какие-то данные, но как их подружить? Я могу указать напрямую, что собака по имени «Тузик» принадлежит этому самому Ивану. Но что, если он сменит имя и вместо Ивана станет, например, Олей? Тогда получится, что наша Оля Ивановна Иванова больше не будет иметь собаки, а наш Тузик все еще будет принадлежать несуществующему Ивану. Решить эту проблему помогла база данных, которая каждому пользователю давала уникальный идентификатор (ID), и мой Тузик привязывался к этому ID, который, по сути, был просто порядковым номером. Таким образом хозяин у тузика был с ID под номером 2, и на какой-то момент времени под этим ID был Иван, а потом под этим же ID стала Оля. Проблема человечества и животноводства была практически решена.

Файл дескриптор

Проблема файла и программы, работающей с этим файлом, примерно такая же как нашей собаки и человека. Предположим я открыл файл под именем ivan.txt и начал в него записывать слово tuzik, но успел записать только первую букву «t» в файл, и этот файл был кем-то переименован, например в olya.txt. Но файл остался тем же самым, и я все еще хочу записать в него своего тузика. Каждый раз при открытии файла системным вызовом open в любом языке программирования я получаю уникальный ID, который указывает мне на файл, этот ID и есть файл дескриптор. И совершенно не важно, что и кто делает с этим файлом дальше, его могут удалить, его могут переименовать, ему могут поменять владельца или забрать права на чтение и запись, я все равно буду иметь к нему доступ, потому что на момент открытия файла у меня были права для его чтения и/или записи и я успел начать с ним работать, а значит должен продолжать это делать.

В Linux библиотека libc открывает для каждого запущенного приложения(процесса) 3 файл дескриптора, с номерами 0,1,2. Больше информации вы можете найти по ссылкам man stdio и man stdout

  • Файл дескриптор 0 называется STDIN и ассоциируется с вводом данных у приложения
  • Файл дескриптор 1 называется STDOUT и используется приложениями для вывода данных, например командами print
  • Файл дескриптор 2 называется STDERR и используется приложениями для вывода данных, сообщающих об ошибке

Если в вашей программе вы откроете какой-либо файл на чтение или запись, то скорее всего вы получите первый свободный ID и это будет номер 3.

Список файл дескрипторов можно посмотреть у любого процесса, если вы знаете его PID.

Например, откроем консоль с bash и посмотрим PID нашего процесса

Во второй консоли запустим

Файл дескриптор с номером 255 можете смело игнорировать в рамках данной статьи, он был открыт для своих нужд уже самим bash, а не прилинкованной библиотекой.

Сейчас все 3 файл дескриптора связаны с устройством псевдотерминала /dev/pts, но мы все равно можем ими манипулировать, например запустим во второй консоли

И в первой консоли мы увидим

Redirect и Pipe

Вы можете легко переопределить эти 3 файл дескриптора в любом процессе, в том числе и в bash, например через трубу(pipe), соединяющую два процесса, смотрим

Вы можете сами запустить эту команду с strace -f и увидеть, что происходит внутри, но я вкратце расскажу.

Наш родительский процесс bash с PID 15771 парсит нашу команду и понимает сколько именно команд мы хотим запустить, в нашем случае их две: cat и sleep. Bash знает что ему нужно создать два дочерних процесса, и объединить их одной трубой. Итого bash потребуется 2 дочерних процесса и один pipe.

Перед созданием дочерних процессов bash запускает системный вызов pipe и получает новые файл дескрипторы на временный буфер pipe, но этот буфер никак пока не связывает наши два дочерних процесса.

Для родительского процесса это выглядит так будто pipe уже есть, а дочерних процессов еще нет:

Затем с помощью системного вызова clone bash создает два дочерних процесса, и наши три процесса будут выглядеть так:

Не забываем, что clone клонирует процесс вместе со всеми файл дескрипторами, поэтому в родительском процессе и в дочерних они будут одинаковые. Задача родительского процесса с PID 15771 следить за дочерними процессами, поэтому он просто ждет ответ от дочерних.

Следовательно pipe ему не нужен, и он закрывает файл дескрипторы с номерами 3 и 4.

В первом дочернем процессе bash с PID 9004, системным вызовом dup2, меняет наш STDOUT файл дескриптор с номером 1 на файл дескриптор указывающий на pipe, в нашем случае это номер 3. Таким образом все, что первый дочерний процесс с PID 9004 будет писать в STDOUT, будет автоматически попадать в буфер pipe.

Во втором дочернем процессе с PID 9005 bash меняет с помощью dup2 файл дескриптор STDIN с номером 0. Теперь все, что будет читать наш второй bash с PID 9005, будет читать из pipe.

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

Файл дескриптор 255 я намеренно игнорирую, он использует для внутренних нужд самого bash и в дочерних процессах будет также закрыт.

Далее в первом дочернем процессе с PID 9004 bash запускает с помощью системного вызова exec исполняемый файл, который мы указали в командной строке, в нашем случае это /usr/bin/cat.

Во втором дочернем процессе с PID 9005 bash запускает второй исполняемый файл, который мы указали, в нашем случае это /usr/bin/sleep.

Системный вызов exec не закрывает файл дескрипторы, если они не были открыты с флагом O_CLOEXEC во время выполнения вызова open. В нашем случае после запуска исполняемых файлов все текущие файл дескрипторы сохранятся.

Проверяем в консоли:

Как видите уникальный номер нашего pipe у нас в обоих процессах совпадает. Таким образом у нас есть связь между двумя разными процессами с одним родителем.

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

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

Запустим программу и посмотрим на файл дескрипторы

Как видим у нас есть наши 3 стандартные файл дескрипторы и еще один, который мы открыли. Проверим размер файла:

данные пишутся, пробуем поменять права на файл:

Видим, что данные все еще пишутся, хотя наш пользователь не имеет права писать в файл. Попробуем его удалить:

Куда пишутся данные? И пишутся ли вообще? Проверяем:

Да, наш файл дескриптор все еще существует, и мы можем работать с этим файл дескриптором как с нашим старым файлом, мы можем его читать, очищать и копировать.

Смотрим на размер файла:

Размер файла 19923457. Пробуем очистить файл:

Как видим размер файла только увеличивается и наш транкейт не сработал. Обратимся к документации по системному вызову open. Если при открытии файла мы используем флаг O_APPEND, то при каждой записи операционная система проверяет размер файла и пишет данные в самый конец файла, причем делает это атомарно. Это позволяет нескольким тредам или процессам писать в один и тот же файл. Но в нашем коде мы не используем этот флаг. Мы можем увидеть другой размер файла в lsof после транкейт только если откроем файл для дозаписи, а значит в нашем коде вместо

мы должны поставить

Проверяем с «w» флагом

Программируем уже запущенный процесс

Часто программисты при создании и тестировании программы используют дебагеры (например GDB) или различные уровни логирования в приложении. Linux предоставляет возможность фактически писать и менять уже запущенную программу, например менять значения переменных, устанавливать breakpoint и тд и тп.

Возвращаясь к оригинальному вопросу с нехваткой места на диске для записи файла, попробуем сэмулировать проблему.

Создадим файл для нашего раздела, который мы подмонтируем как отдельный диск:

Создадим файловую систему:

Подмонтируем файловую систему:

Создаем директорию с нашим владельцем:

Откроем файл только на запись в нашей программе:

Ждем несколько секунд

Итак, мы получили проблему, описанную в начале этой статьи. Свободного места 0, занятого 100%.

Мы помним, что по условиям задачи мы пытаемся записать очень важные данные, которые нельзя потерять. И при этом нам нужно починить сервис без перезапуска процесса.

Допустим, у нас все же есть место на диске, но в другом разделе, например в /home.

Попробуем «перепрограммировать на лету» наш код.

Смотрим PID нашего процесса, который съел все место на диске:

Подключаемся к процессу через gdb

Смотрим открытые файл дескрипторы:

Смотрим информацию о файл дескрипторе с номером 3, который нас интересует

Помня о том, какой системный вызов делает Python (смотрите выше, где мы запускали strace и находили вызов open), обрабатывая наш код для открытия файла, мы делаем то же самое самостоятельно от имени нашего процесса, но биты O_WRONLY|O_CREAT|O_TRUNC нам нужно заменить на числовое значение. Для этого открываем исходники ядра, например тут и смотрим какие флаги за что отвечают

#define O_WRONLY 00000001
#define O_CREAT 00000100
#define O_TRUNC 00001000

Объединяем все значения в одно, получаем 00001101

Запускаем наш вызов из gdb

Итак мы получили новый файл дескриптор с номером 4 и новый открытый файл на другом разделе, проверяем:

Мы помним пример с pipe — как bash меняет файл дескрипторы, и уже выучили системный вызов dup2.

Пробуем подменить один файл дескриптор другим

Закрываем файл дескриптор 4, так как нам он не нужен:

И выходим из gdb

Проверяем новый файл:

Как видим, данные пишутся в новый файл, проверяем старый:

Данные не потеряны, приложение работает, логи пишутся в новое место.

Немного усложним задачу

Представим, что данные нам важны, но места на диске у нас нет ни в одном из разделов и подключить диск мы не можем.

Что мы можем сделать, так это перенаправить куда-то наши данные, например в pipe, а данные из pipe в свою очередь перенаправить в сеть через какую-либо программу, например netcat.
Мы можем создать именованный pipe командой mkfifo. Она создаст псевдофайл на файловой системе, даже если на ней нет свободного места.

Перезапускаем приложение, и проверяем:

Места на диске нет, но мы успешно создаем там именованный pipe:

Теперь нам надо как-то завернуть все данные, что попадают в этот pipe на другой сервер через сеть, для этого подойдет все тот же netcat.

На сервере remote-server.example.com запускаем

На нашем проблемном сервере запускаем в отдельном терминале

Теперь все данные, которые попадут в pipe автоматически попадут на stdin в netcat, который их отправит в сеть на порт 7777.

Все что нам осталось сделать это начать писать наши данные в этот именованный pipe.

У нас уже есть запущенное приложение:

Из всех флагов нам нужен только O_WRONLY так как файл уже существует и очищать нам его не нужно

Проверяем удаленный сервер remote-server.example.com

Данные идут, проверяем проблемный сервер

Данные сохранились, проблема решена.

Пользуясь случаем, передаю привет коллегам из компании Degiro.
Слушайте подкасты Радио-Т.

В качестве домашнего задания предлагаю подумать, что будет в файл дескрипторах процесса cat и sleep если запустить такую команду:

Приложение Б Функции DOS (INT 21h)

Приложение Б Функции DOS (INT 21h)

DOS, функция 00h

CS – сегмент PSP завершающегося процесса

Описание. Передает управление на вектор завершения в PSP (выходит в родительский процесс). Идентична функции INT 20h (Terminate). Регистр CS должен указывать на PSP. Восстанавливает векторы прерываний DOS 22h-24h (Завершение, Ctrl-Break и Критическая ошибка), устанавливая значения, сохраненные в родительском PSP. Выполняет сброс файловых буферов. Файлы должны быть предварительно закрыты, если их длина изменилась.

Данная функция не рекомендуется к использованию. Для выхода из программы лучше использовать функцию DOS 4Ch.

DOS, функция 01h Считать со стандартного устройства ввода

Выход: AL – символ, полученный из стандартного ввода

Описание. Считывает (ожидает) символ со стандартного входного устройства. Отображает этот символ на стандартное выходное устройство (эхо). При обнаружении Ctrl-Break выполняется INT 23h.

Ввод расширенных клавиш ASCII (F1-F12, PgUp, курсор и другие) требует двух обращений к этой функции. Первый вызов возвращает AL=0. Второй вызов возвращает в AL расширенный код ASCII.

DOS, функция 02h Записать в стандартное устройство вывода

DL – символ, выводимый в стандартный вывод

Посылает символ из DL в стандартное устройство вывода. Обрабатывает символ Backspace (ASCII 8), перемещая курсор влево на одну позицию и оставляя его в новой позиции. При обнаружении Ctrl-Break выполняется INT 23h.

DOS, функция 03h Считать символа со стандартного вспомогательного устройства

Выход: AL – символ, введенный со стандартного вспомогательного устройства

Описание. Считывает (ожидает) символ со стандартного вспомогательного устройства, COM1 или AUX и возвращает этот символ в AL.

Ввод не буферизуется и должен опрашиваться (не управляется прерываниями). При запуске DOS порт AUX (COM1) инициализируется так: 2400 бод, без проверки на четность, 1 стоп-бит, 8-битные слова. Команда DOS MODE используется для установки иных характеристик.

DOS, функция 04h Записать символ в стандартное вспомогательное устройство

DL – символ, выводимый в стандартное вспомогательное устройство

Посылает символ, находящийся в регистре DL, на стандартное вспомогательное устройство, COM1 или AUX.

DOS, функция 05h Вывести на принтер

DL – символ, записываемый на стандартный принтер

Посылает символ в DL на стандартное устройство печати, обычно LPT1.

DOS, функция 06h Консольный ввод-вывод

DL=00h-FEh – символ, посылаемый на стандартный вывод

DL=FFh – запрос ввода со стандартного ввода

ZF=0, если осуществлялся ввод символа и символ готов при запросе ввода

AL – считанный символ

ZF=1, если осуществлялся ввод символа и символа в консоли нет

При DL=0FFh выполняет ввод с консоли «Без ожидания», возвращая включенный флаг нуля ZF, если на консоли нет готового символа. Если символ готов, сбрасывает флаг ZF и возвращает считанный символ в AL. Если DL не равен 0FFh, то DL направляется на стандартный вывод.

DOS, функция 07h Нефильтрующий консольный ввод без эха

Выход: AL – символ, полученный через стандартный ввод

Описание. Считывает (ожидает) символ со стандартного входного устройства и возвращает этот символ в AL. Не проверяет на Ctrl-Break, BackSpace и другие.

Для ввода расширенного символа ASCII должна быть вызвана дважды. Для проверки статуса используется функция DOS 0Bh (чтобы не ожидать нажатия клавиши).

DOS, функция 08h Консольный ввод без эха

Выход: AL – символ, полученный через стандартный ввод

Описание. Считывает (ожидает) символ со стандартного входного устройства и возвращает этот символ в AL. При обнаружении Ctrl-Break выполняется прерывание INT 23h.

Для ввода расширенного символа ASCII должна быть вызвана дважды.

DOS, функция 09h Запись строки на стандартный вывод

DS:DX – адрес строки, заканчивающейся символом «$» (ASCII 24h)

Строка, исключая завершающий ее символ «$», посылается на стандартный вывод. Символы Backspace обрабатываются как в функции 02h (вывод на дисплей). Чтобы перейти на новую строку, обычно включают в текст пару CR/LF (ASCII 0Dh и ASCII 0Ah). Строки, содержащие «$», можно передать на стандартное устройство вывода с помощью функции 40h (BX=0).

DOS, функция 0Ah Ввод строки в буфер

DS:DX – адрес входного буфера (Таблица Б-1)

Таблица Б-1. Формат входного буфера

Буфер содержит введенные данные, в конце – символ CR (ASCII 0Dh)

DOS, функция 0Bh Проверка статуса ввода

Выход: AL=FFh, если символ доступен со стандартного ввода AL=00h, если нет доступного символа

Описание. Проверяет состояние стандартного ввода. При распознавании Ctrl-Break выполняется INT 23h.

Используется перед функциями 01h, 07h и 08h, чтобы избежать ожидания нажатия клавиши.

Эта функция дает простой неразрушающий способ проверки Ctrl-Break в процессе длинных вычислений или другой обработки, обычно не требующей ввода. Это позволяет снимать счет по нажатию Ctrl-Break.

DOS, функция 0Ch Ввод с очисткой

AL – номер функции ввода DOS:

AL=01h – ввод с клавиатуры

AL=06h – ввод с консоли

AL=07h – нефильтрующий без эха

AL=08h – ввод без эха

AL=0Ah – буферизованный ввод

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

DOS, функция 0Dh Сброс диска

Сбрасывает диск (записывает на диск все файловые буферы). Файл, размер которого изменился, должен быть предварительно закрыт (при помощи функций 10h или 3Eh).


DOS, функция 0Eh Установить текущий диск DOS

DL – номер диска (0 – A, 1 – B и так далее), который становится текущим

Выход: AL – общее число дисководов в системе

Описание. Диск, указанный в DL, становится текущим. Проверка: используется функция 19h (дать текущий диск). В регистре AL возвращается число дисководов всех типов, включая жесткие диски и «логические» диски (как диск B: системе с одним гибким диском).

AL имеет то же значение, что и LASTDRIVE, указанное в файле CONFIG.SYS, и по умолчанию равно 5.

DOS, функция 0Fh Открыть файл через FCB

DS:DX – адрес неоткрытого FCB (Таблица Б-2)

Таблица Б-2. Формат FCB

AL=00h, если функция выполнена успешно (FCB заполнен)

AL=FFh, если файл не найден или доступ к файлу не разрешен

Файл, описываемый неоткрытым FCB, должен существовать в текущем оглавлении на диске, специфицированном в FCB (0 – текущий, 1 – A, 2 – B и так далее). Если файл не существует, возвращается AL=0FFh. Файл открывается в режиме совместимости. Если поле «Номер диска» в FCB равно нулю в момент вызова, то оно заполняется номером текущего дисковода (1 – A, 2 – B и так далее). Поле FCB «Номер текущего блока» устанавливается в ноль. Поле FCB «Размер логической записи» устанавливается в 80h. Поля даты и размера файла в FCB устанавливаются из оглавления.

DOS, функция 10h Закрыть файл через FCB

DS:DX – адрес открытого FCB (Таблица Б-2)

AL=00h, если функция выполнена успешно

AL=FFh, если файл не найден там, где он находился при открытии с помощью функции 0Fh

Закрывает файл, открытый функцией 0Fh. Файл должен находиться на своем первоначальном месте в текущем оглавлении диска, на котором он был открыт. Если файл найден, оглавление обновляется, файловые буфера сбрасываются и возвращается AL=00h. Если файл не найден, оглавление не обновляется и возвращается AL=FFh.

DOS, функция 11h Найти первый совпадающий файл через FCB

DS:DX – адрес неоткрытого FCB (Таблица Б-2)

AL=00h, если подходящее имя найдено

AL=FFh, если подходящего имени нет

В текущем оглавлении DOS происходит поиск файлов с именем, соответствующим заданному шаблону. При неудаче возвращается AL=0FFh. Если имя найдено, AL очищается, в первый байт DTA помещается номер дисковода (A – 1, B – 2 и так далее), а в следующие 32 байта помещается элемент оглавления для найденного файла.

Можно использовать при вызове расширенный FCB, чтобы выбирать файлы с указанными атрибутами. В этом случае в DTA помещаются: байт FFh, 7 байт нулей, номер диска и элемент оглавления.

DOS, функция 12h Найти следующий совпадающий файл через FCB

DS:DX – адрес неоткрытого FCB (Таблица Б-2)

AL=00h, если подходящее имя найдено

DTA заполнен AL=FFh, если подходящего имени нет

Используется после вызова функции 11h (Найти первый совпадающий файл через FCB) с обобщенным именем файла. Каждый последующий вызов заполняет DTA очередным подходящим элементом оглавления и возвращает AL=00h. Если подходящих имен больше нет, возвращается AL=FFh.

Резервируемая область в FCB сохраняет информацию, необходимую для продолжения поиска. Поэтому не стоит открывать и изменять FCB между вызовами.

DOS, функция 13h Удалить файл через FCB

DS:DX – адрес неоткрытого FCB (Таблица Б-2)

AL=00h, если функция выполнена успешно

AL=FFh, если файл не найден или доступ к файлу не разрешен

Эта функция удаляет все подходящие файлы в текущем оглавлении указанного диска согласно спецификации в FCB. Если подходящие файлы не найдены или если доступ отвергнут (как при попытке удалить файл с атрибутом Read-Only), функция возвращает в регистре AL значение FFh.

DOS, функция 14h Последовательное чтение из файла через FCB

DS:DX – адрес открытого FCB (Таблица Б-2)

AL=00h, если чтение было успешным и DTA содержит данные

AL=01h, если достигнут конец файла (EOF) и данные не считаны

AL=02h, если произошел выход за сегмент (чтения не было)

AL=03h, если EOF и считана усеченная запись (дополнена нулями)

Функция читает файл, специфицированный в FCB. Затем соответственно увеличивает значения полей в FCB.

Перед началом последовательной обработки файла нужно сбрасывать CurRec в ноль, так как функция 0Fh не инициализирует это поле.

DOS, функция 15h Последовательная запись в файл через FCB

DS:DX – адрес открытого FCB (Таблица Б-2)

AL=00h, если запись была успешной

AL=01h, если ошибка переполнения диска (данные не записаны)

AL=02h, если произошел выход за сегмент (записи не было)

Функция записывает файл, специфицированный в FCB. Затем соответственно увеличивает значения полей в FCB.

Перед началом последовательной обработки файла нужно сбрасывать «Номер текщей записи» в ноль, так как функция 0Fh не инициализирует это поле.

DOS буферизует данные, записывая полный сектор за один раз.

DOS, функция 16h Создание файла через FCB

DS:DX – адрес неоткрытого FCB (Таблица Б-2)

AL=00h, если функция выполнена успешно FCB заполнен

AL=FFh, если при выполнении функции возникли ошибки

Описание. Файл, специфицированный неоткрытым FCB, создается на диске, указанном в FCB (0 – текущий, 1 – A и так далее). Он открывается в текущем оглавлении этого диска. FCB заполняется аналогично функции 0Fh. Если файл существует в момент вызова, его элемент оглавления перекрывается новым файлом, а длина файла сбрасывается в ноль.

Handle-ориентированные функции DOS 2.0+ гораздо удобнее в работе.

DOS, функция 17h Переименовать файл через FCB

DS:DX – адрес измененного FCB (Таблица Б-2)

AL=00h, если функция выполнена успешно

AL=FFh, если при выполнении функции возникли ошибки

Переименовывает файл в текущем оглавлении.

DOS, функция 19h Получить текущий диск DOS

Выход: AL – номер текущего диска (0 – A, 1 – B, и так далее)

Возвращает номер дисковода текущего диска DOS.

DOS, функция 1Ah Установить адрес DTA

DS:DX – адрес DTA

Устанавливает адрес DTA. Все FCB-ориентированные операции работают с DTA. DOS не позволяет операциям ввода/вывода пересекать границу сегмента. Функции поиска 11h, 12h, 4Eh и 4Fh помещают данные в DTA. DTA глобальна, поэтому надо проявлять осторожность при назначении ее в рекурсивной процедуре. При запуске программы ее DTA устанавливается по смещению 80h относительно PSP.

DOS, функция 1Bh Получить информацию FAT для текущего диска

DS:BX – адрес байта FAT ID, отражающего тип диска (Таблица Б-3)

DX – всего кластеров (единиц распределения) на диске

AL – секторов на кластер

CX – байт на сектор

Таблица Б-3. Значения >

Возвращает информацию о размере и типе текущего диска. Размер диска (в байтах) равен DX*AL*CX. Свободную память можно найти функциями 36h или 32h.

Версии: DOS 1.x держит FAT в памяти и возвращает DS:BX => FAT. DOS 2.0+ может держать в памяти лишь часть всей FAT.

Эта функция изменяет содержимое регистра DS.

DOS, функция 1Ch Получить информацию FAT для указанного диска

DL – номер диска (0 – текущий, 1 – A и так далее)

DS:BX – адрес байта FAT ID, отражающего тип диска (приведен в описании функции 1Bh)

DX – всего кластеров (единиц распределения)

AL – секторов на кластер

CX – байт на сектор

Аналогична функции 1Bh с той разницей, что регистр DL указывает диск, для которого нужно получить информацию.

DOS, функция 21h Считать произвольную запись файла

DS:DX – адрес открытого FCB (Таблица Б-2)

AL=00h, если чтение было успешным и DTA заполнена данными

AL=01h, если достигнут конец файла (EOF) и чтения не было

AL=02h, если произошел выход за сегмент (чтения нет)

AL=03h, если встречен EOF и усеченная запись дополнена нулями

Данная функция читает из файла с текущей позиции как с указанной в полях FCB «Запись с текущей позиции» и «Номер записи при непосредственном доступе к файлу».

DOS, функция 22h Писать произвольную запись файла

DS:DX – адрес открытого FCB (Таблица Б-2)

AL=00h, если запись была успешной

AL=01h, при переполнении диска

AL=02h, если DTA+FCB выходит за сегмент (нет записи)

Данная функция записывает в файл с текущей позиции как с указанной в полях FCB «Запись с текущей позиции» и «Номер записи при непосредственном доступе к файлу».

DOS, функция 23h Получить размер файла через FCB

DS:DX – адрес неоткрытого FCB (Таблица Б-2)

AL=00h, если функция выполнена успешно

AL=FFh, если при выполнении функции возникли ошибки

Проще определить размер файла при помощи функции 3Dh с последующим выполнением 42h (при AL=2).

DOS, функция 24h Установить адрес произвольной записи в файле

DS:DX – адрес открытого FCB (Таблица Б-2)

Устанавливает поле «Номер записи при непосредственном доступе к файлу» в FCB на файловый адрес, соответствующий значениям полей «Текущий блок» и «Запись с текущей позиции».

DOS, функция 25h Установить вектор прерывания

AL – номер прерывания

DS:DX – вектор прерывания – адрес программы обработки прерывания

Описание. Устанавливает значение элемента таблицы векторов прерываний для прерывания с номером AL, равным DS:DX. Это равносильно записи 4-байтового адреса в 0000:(AL*4), но, в отличие от прямой записи, DOS знает, что происходит, и гарантирует, что в момент записи прерывания будут заблокированы.

Восстановить DS (если необходимо) после этого вызова.

DOS, функция 26h Создать новый PSP

DX – адрес сегмента (параграфа) для нового PSP

CS – сегмент PSP, используемый как шаблон для нового PSP (Таблица Б-4)

Описание. Устанавливает PSP для порождаемого процесса по адресу DX:0000. Текущий PSP (100h байт, начиная с CS:0) копируется в DX:0000h, поле MemTop соответственно корректируется, векторы Terminate, Ctrl-Break и Critical Error копируются в PSP из векторов прерываний INT 22h, INT 23h и INT 24h. После этого можно загрузить программу с диска и передать ей управление посредством FAR JMP.

Если перехватывается INT 21h, нужно позаботиться о помещении в стек корректного CS: IP. Еще лучше использовать функцию 4Ch.

Таблица Б-4. Формат PSP

DOS, функция 27h Читать произвольный блок файла

DS:DX – адрес открытого FCB (Таблица Б-2)

CX – число считываемых записей

Выход: AL=00h, если чтение успешно и DTA заполнена данными AL=01h если достигнут конец файла (EOF) и данные не считаны AL=02h, если при чтении произошел выход за границу сегмента AL=03h, если EOF и считана усеченная порция (дополнена нулями) CX – действительное число считанных записей

Читает несколько записей из файла, начиная с файлового адреса, указанного полем «Номер записи при непосредственном доступе к файлу» в FCB. Помещает данные в память, начиная с адреса DTA. Соответствующие поля FCB корректируются, чтобы указывать на следующую запись (первую за прочитанными).

DOS, функция 28h Писать произвольный блок файла

DS:DX – адрес открытого FCB (Таблица Б-2)

CX – число записываемых блоков (если CX равен нулю, то размер файла усекается до указанного в поле FCB «Номер записи при непосредственном доступе к файлу»)

AL=00h, если запись успешна

AL=01h, при переполнении диска

AL=02h, если при записи произошел выход за границу сегмента

CX – действительное число сделанных записей

Описание. Записывает несколько блоков в файл, начиная с файлового адреса, указанного полем «Номер записи при непосредственном доступе к файлу» в FCB. Читает данные из памяти, начиная с адреса DTA. Соответствующие поля FCB корректируются, чтобы указывать на следующую запись (первую за прочитанными).

DOS, функция 29h Разобрать имя файла

DS:SI – адрес исходной текстовой строки для разбора

ES:DI – адрес буфера для результирующего неоткрытого FCB (Таблица Б-2)

AL – битовые флаги, указывающие опции разбора (Таблица Б-5).

AL=00h, если результирующий FCB не содержит обобщенных символов

AL=01h, если результирующий FCB содержит обобщенные символы

AL=FFh, если неверно обозначение диска в имени файла

DS:SI – изменен – указывает на символ сразу вслед за именем файла

ES:DI – не изменен – указывает на неоткрытый FCB

Создает неоткрытый FCB из строки текста или параметра команды. Текст, начиная с DS:SI, анализируется как имя файла в формате D: FILENAME.EXT, и буфер по адресу ES:DI заполняется как соответственно форматированный FCB.

Таблица Б-5. Битовые флаги

DOS, функция 2Ah Получить системную дату

AL – день недели (0 – воскресенье, 1 – понедельник, … 6 – суббота), DOS 3.0+

CX – год (от 1980 до 2099)

DH – месяц (1 до 12)

DL – день (1 до 31)

Описание. Возвращает текущую дату, которая известна системе.

DOS 2.x не гарантирует возврата в AL значения дня.

DOS 1.0+ возвращает правильный день недели.

Версии до 2.1 имеют проблемы с переходом через дату.

DOS, функция 2Bh Установить системную дату

CX – год (от 1980 до 2099)

DH – месяц (от 1 до 12)

DL – день (от 1 до 31)

AL=00h, если дата корректна

AL=FFh, если дата некорректна и не изменена

Устанавливает системную дату DOS.

DOS, функция 2Ch Получить время DOS

CH – часы (от 0 до 23)

CL – минуты (от 0 до 59)

DH – секунды (от 0 до 59)

DL – сотые доли секунды (от 0 до 99)

Описание. Возвращает текущее время, которое известно системе.

Поскольку системные часы имеют частоту 18.2 Гц (интервал 55мс), DL имеет точность примерно 0.04 сек.

DOS, функция 2Dh Установить время DOS

CH – часы (от 0 до 23)

CL – минуты (от 0 до 59)

DH – секунды (от 0 до 59)

DL – сотые доли секунды (от 0 до 99)

AL=00h, если время корректно

AL=FFh, если время некорректно и не изменено

Устанавливает системное время DOS.

DOS, функция 2Eh Установить/сбросить переключатель верификации

AL=00h – отключить верификацию

AL=01h – включить верификацию

Описание. Задает, должна ли DOS верифицировать (считывать обратно) каждый сектор, записываемый на диск. Это замедляет операции записи на диск, но гарантирует максимальную надежность записи.

DOS, функция 2Fh Получить адрес текущей DTA

Выход: ES:BX – адрес начала текущей DTA

Описание. Возвращает адрес начала области ввода-вывода (DTA). Поскольку DTA глобальна для всех процессов, в рекурсивной процедуре (например, при проходе по дереву оглавления) может потребоваться сохранить адрес DTA, а впоследствии восстановить его посредством функции 1Ah.

Примечание. Эта функция изменяет сегментный регистр ES.

Версии: DOS 2.00 и выше DOS, функция 30h Получить номер версии DOS

AL – старший номер версии

AH – младший номер версии

BL:CX – 24-битный серийный номер (большинство версий не поддерживают этот параметр)

Описание. Возвращает в AX значение текущего номера версии DOS. Например, для DOS 3.20 в AL возвращается 03h, в AH – 14h.

Примечание. Если в AL возвращается 00h, можно предполагать, что работает DOS более ранней версии, чем DOS 2.0.

Версии: DOS 2.00 и выше. DOS, функция 31h Завершиться и остаться резидентным

DX – объем памяти, оставляемой резидентной (в параграфах)

Описание. Выходит в родительский процесс, сохраняя код выхода в AL. Код выхода можно получить через функцию 4Dh. DOS устанавливает начальное распределение памяти, как специфицировано в DX, и возвращает управление родительскому процессу, оставляя указанную память резидентной (число байт равно DX*16). Эта функция перекрывает функцию INT 27h, которая не возвращает код выхода и не способна установить резидентную программу, размер которой превышает 64 Кбайт.

Assembler. Нужно вывести первую строку файла на экран.

Вот программа, но она почему-то не работает(((
model small
stack 256
.data
bufindb30 dup(‘ ‘)
handledw?
assmdb’Lab1.txt’

movAH, 3Dh
movAL, 0
movDX, offset assm
int21h
movhandle, AX

mov AH, 3Fh
movBX, handle
movCX, 30
movDX, offset bufin
int21h
movCX, AX

movAH, 40h
movBX, 1
movDX, offset bufin
int21h
end

Там же в папке лежит lab1.txt. Что я не так делаю. Очень прошу, помогите, уже целый день долбаюсь с ней.

mov AH, 3Dh ; открыть описатель файла
mov AL, 0 ; чтобы открыть для чтения
mov DX, seg assm
mov DS,DX
mov DX, offset assm
int 21h
mov handle, AX

mov AH, 3Fh ; читать файл через описатель
mov BX, handle
mov CX, 30
mov DX, seg bufin
mov DS,DX
mov DX, offset bufin
int 21h
mov CX, AX

Dos fn 40h: писать в файл через описатель

В языке C для осуществления файлового ввода-вывода используются механизмы стандартной библиотеки языка, объявленные в заголовочном файле stdio.h. Как вы вскоре узнаете консольный ввод-вывод — это не более чем частный случай файлового ввода-вывода. В C++ для ввода-вывода чаще всего используются потоковые типы данных. Однако все эти механизмы являются всего лишь надстройками над низкоуровневыми механизмами ввода-вывода ядра операционной системы.

С точки зрения модели КИС (Клиент-Интерфейс-Сервер), сервером стандартных механизмов ввода вывода языка C (printf, scanf, FILE*, fprintf, fputc и т. д.) является библиотека языка. А сервером низкоуровневого ввода-вывода в Linux, которому посвящена эта глава книги, является само ядро операционной системы.

Пользовательские программы взаимодействуют с ядром операционной системы посредством специальных механизмов, называемых системными вызовами (system calls, syscalls). Внешне системные вызовы реализованы в виде обычных функций языка C, однако каждый раз вызывая такую функцию, мы обращаемся непосредственно к ядру операционной системы. Список всех системных вызовов Linux можно найти в файле /usr/include/asm/unistd.h. В этой главе мы рассмотрим основные системные вызовы, осуществляющие ввод-вывод: open(), close(), read(), write(), lseek() и некоторые другие.

5.2. Файловые дескрипторы

В языке C при осуществлении ввода-вывода мы используем указатель FILE*. Даже функция printf() в итоге сводится к вызову vfprintf(stdout. ), разновидности функции fprintf(); константа stdout имеет тип struct _IO_FILE*, синонимом которого является тип FILE*. Это я к тому, что консольный ввод-вывод — это файловый ввод-вывод. Стандартный поток ввода, стандартный поток вывода и поток ошибок (как в C, так и в C++) — это файлы. В Linux все, куда можно что-то записать или откуда можно что-то прочитать представлено (или может быть представлено) в виде файла. Экран, клавиатура, аппаратные и виртуальные устройства, каналы, сокеты — все это файлы. Это очень удобно, поскольку ко всему можно применять одни и те же механизмы ввода-вывода, с которыми мы и познакомимся в этой главе. Владение механизмами низкоуровневого ввода-вывода дает свободу перемещения данных в Linux. Работа с локальными файловыми системами, межсетевое взаимодействие, работа с аппаратными устройствами, — все это осуществляется в Linux посредством низкоуровневого ввода-вывода.

Вы уже знаете из предыдущей главы, что при запуске программы в системе создается новый процесс (здесь есть свои особенности, о которых пока говорить не будем). У каждого процесса (кроме init) есть свой родительский процесс (parent process или просто parent), для которого новоиспеченный процесс является дочерним (child process, child). Каждый процесс получает копию окружения (environment) родительского процесса. Оказывается, кроме окружения дочерний процесс получает в качестве багажа еще и копию таблицы файловых дескрипторов.

Файловый дескриптор (file descriptor) — это целое число (int), соответствующее открытому файлу. Дескриптор, соответствующий реально открытому файлу всегда больше или равен нулю. Копия таблицы дескрипторов (читай: таблицы открытых файлов внутри процесса) скрыта в ядре. Мы не можем получить прямой доступ к этой таблице, как при работе с окружением через environ. Можно, конечно, кое-что «вытянуть» через дерево /proc, но нам это не надо. Программист должен лишь понимать, что каждый процесс имеет свою копию таблицы дескрипторов. В пределах одного процесса все дескрипторы уникальны (даже если они соответствуют одному и тому же файлу или устройству). В разных процессах дескрипторы могут совпадать или не совпадать — это не имеет никакого значения, поскольку у каждого процесса свой собственный набор открытых файлов.

Возникает вопрос: сколько файлов может открыть процесс? В каждой системе есть свой лимит, зависящий от конфигурации. Если вы используете bash или ksh (Korn Shell), то можете воспользоваться внутренней командой оболочки ulimit, чтобы узнать это значение. Если вы работаете с оболочкой C-shell (csh, tcsh), то в вашем распоряжении команда limit:

В командной оболочке, в которой вы работаете (bash, например), открыты три файла: стандартный ввод (дескриптор 0), стандартный вывод (дескриптор 1) и стандартный поток ошибок (дескриптор 2). Когда под оболочкой запускается программа, в системе создается новый процесс, который является для этой оболочки дочерним процессом, следовательно, получает копию таблицы дескрипторов своего родителя (то есть все открытые файлы родительского процесса). Таким образом программа может осуществлять консольный ввод-вывод через эти дескрипторы. На протяжении всей книги мы будем часто играть с этими дескрипторами.

Таблица дескрипторов, помимо всего прочего, содержит информацию о текущей позиции чтения-записи для каждого дескриптора. При открытии файла, позиция чтения-записи устанавливается в ноль. Каждый прочитанный или записанный байт увеличивает на единицу указатель текущей позиции. Мы вернемся к этой теме в разделе 5.7.

5.3. Открытие файла: системный вызов open()

Чтобы получить возможность прочитать что-то из файла или записать что-то в файл, его нужно открыть. Это делает системный вызов open(). Этот системный вызов не имеет постоянного списка аргументов (за счет использования механизма va_arg); в связи с этим существуют две «разновидности» open(). Не только в С++ есть перегрузка функций ;-) Если интересно, то о механизме va_arg можно прочитать на man-странице stdarg (man 3 stdarg) или в книге Б. Кернигана и Д. Ритчи «Язык программирования Си». Ниже приведены адаптированные прототипы системного вызова open().

Системный вызов open() объявлен в заголовочном файле fcntl.h. Ниже приведен общий адаптированный прототип open().

Начнем по порядку. Первый аргумент — имя файла в файловой системе в обычной форме: полный путь к файлу (если файл не находится в текущем каталоге) или сокращенное имя (если файл в текущем каталоге).

Второй аргумент — это режим открытия файла, представляющий собой один или несколько флагов открытия, объединенных оператором побитового ИЛИ. Список доступных флагов приведен в Таблице 4 Приложения 2.. Наиболее часто используют только первые семь флагов. Если вы хотите, например, открыть файл в режиме чтения и записи, и при этом автоматически создать файл, если такового не существует, то второй аргумент open() будет выглядеть примерно так: O_RDWR|O_CREAT. Константы-флаги открытия объявлены в заголовочном файле bits/fcntl.h, однако не стоит включать этот файл в свои программы, поскольку он уже включен в файл fcntl.h.

Третий аргумент используется в том случае, если open() создает новый файл. В этом случае файлу нужно задать права доступа (режим), с которыми он появится в файловой системе. Права доступа задаются перечислением флагов, объединенных побитовым ИЛИ. Вместо флагов можно использовать число (как правило восьмиричное), однако первый способ нагляднее и предпочтительнее. Список флагов приведен в Таблице 1 Приложения 2. Чтобы, например, созданный файл был доступен в режиме «чтение-запись» пользователем и группой и «только чтение» остальными пользователями, — в третьем аргументе open() надо указать примерно следующее: S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH или 0664. Флаги режима доступа реально объявлены в заголовочном файле bits/stat.h, но он не предназначен для включения в пользовательские программы, и вместо него мы должны включать файл sys/stat.h. Тип mode_t объявлен в заголовочном файле sys/types.h.

Если файл был успешно открыт, open() возвращает файловый дескриптор, по которому мы будем обращаться к файлу. Если произошла ошибка, то open() возвращает -1. Позже, в последующих главах книги мы научимся распознавать ошибки системных вызовов.

5.4. Закрытие файла: системный вызов close()

Системный вызов close() закрывает файл. Вообще говоря, по завершении процесса все открытые файлы (кроме файлов с дескрипторами 0, 1 и 2) автоматически закрываются. Тем не менее, это не освобождает нас от самостоятельного вызова close(), когда файл нужно закрыть. К тому же, если файлы не закрывать самостоятельно, то соответствующие дескрипторы не освобождаются, что может привести к превышению лимита открытых файлов. Простой пример: приложение может быть настроено так, чтобы каждую минуту открывать и перечитывать свой файл конфигурации для проверки обновлений. Если каждый раз файл не будет закрываться, то в моей системе, например, приложение может «накрыться медным тазом» примерно через 17 часов. Автоматически! Кроме того, файловая система Linux поддерживает механизм буферизации. Это означает, что данные, которые якобы записываются, реально записываются на носитель (синхронизируются) только через какое-то время, когда система сочтет это правильным и оптимальным. Это повышает производительность системы и даже продлевает ресурс жестких дисков. Системный вызов close() не форсирует запись данных на диск, однако дает больше гарантий того, что данные останутся в целости и сохранности.

Системный вызов close() объявлен в файле unistd.h. Ниже приведен его адаптированный прототип.

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

Теперь можно написать простенкую программу, использующую системные вызовы open() и close(). Мы еще не умеем читать из файлов и писать в файлы, поэтому напишем программу, которая создает файл с именем, переданным в качестве аргумента (argv[1]) и с правами доступа 0600 (чтение и запись для пользователя). Ниже приведен исходный код программы.

Обратите внимание, если запустить программу дважды с одним и тем же аргументом, то на второй раз open() выдаст ошибку. В этом виноват флаг O_EXCL (см. Таблицу 4 Приложения 2), который «дает добро» только на создание еще не существующих файлов. Наглядности ради, флаги открытия и флаги режима мы занесли в отдельные переменные, однако можно было бы сделать так: Или так:

5.5. Чтение файла: системный вызов read()

Системный вызов read(), объявленный в файле unistd.h, позволяет читать данные из файла. В отличие от библиотечных функций файлового ввода-вывода, которые предоставляют возможность интерпретации считываемых данных. Можно, например, записать в файл следующее содержимое:

Теперь, используя библиотечные механизмы, можно читать файл по-разному:

Системный вызов read() читает данные в «сыром» виде, то есть как последовательность байт, без какой-либо интерпретации. Ниже представлен адаптированный прототип read().

Первый аргумент — это файловый дескриптор. Здесь больше сказать нечего. Второй аргумент — это указатель на область памяти, куда будут помещаться данные. Третий аргумент — количество байт, которые функция read() будет пытаться прочитать из файла. Возвращаемое значение — количество прочитанных байт, если чтение состоялось и -1, если произошла ошибка. Хочу заметить, что если read() возвращает значение меньше count, то это не символизирует об ошибке.

Хочу сказать несколько слов о типах. Тип size_t в Linux используется для хранения размеров блоков памяти. Какой тип реально скрывается за size_t, зависит от архитектуры; как правило это unsigned long int или unsigned int. Тип ssize_t (Signed SIZE Type) — это тот же size_t, только знаковый. Используется, например, в тех случаях, когда нужно сообщить об ошибке, вернув отрицательный размер блока памяти. Системный вызов read() именно так и поступает.

Теперь напишем программу, которая просто читает файл и выводит его содержимое на экран. Имя файла будет передаваться в качестве аргумента (argv[1]). Ниже приведен исходный код этой программы.

В этом примере используется укороченная версия open(), так как файл открывается только для чтения. В качестве буфера (второй аргумент read()) мы передаем адрес переменной типа char. По этому адресу будут считываться данные из файла (по одному байту за раз) и передаваться на стандартный вывод. Цикл чтения файла заканчивается, когда read() возвращает нуль (нечего больше читать) или -1 (ошибка). Системный вызов close() закрывает файл.

Как можно заметить, в нашем примере системный вызов read() вызывается ровно столько раз, сколько байт содержится в файле. Иногда это действительно нужно; но не здесь. Чтение-запись посимвольным методом (как в нашем примере) значительно замедляет процесс ввода-вывода за счет многократных обращений к системным вызовам. По этой же причине возрастает вероятность возникновения ошибки. Если нет действительной необходимости, файлы нужно читать блоками. О том, какой размер блока предпочтительнее, будет рассказано в последующих главах книги. Ниже приведен исходный код программы, которая делает то же самое, что и предыдущий пример, но с использованием блочного чтения файла. Размер блока установлен в 64 байта.

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

5.6. Запись в файл: системный вызов write()

Для записи данных в файл используется системный вызов write(). Ниже представлен его прототип.

Как видите, прототип write() отличается от read() только спецификатором const во втором аргументе. В принципе write() выполняет процедуру, обратную read(): записывает count байтов из буфера buffer в файл с дескриптором fd, возвращая количество записанных байтов или -1 в случае ошибки. Так просто, что можно сразу переходить к примеру. За основу возьмем программу myread1 из предыдущего раздела.

В этом примере нам уже не надо изощеряться в попытках вставить нуль-терминатор в строку для записи, поскольку системный вызов write() не запишет большее количество байт, чем мы ему указали. В данном случае для демонстрации write() мы просто записывали данные в файл с дескриптором 1, то есть в стандартный вывод. Но прежде, чем переходить к чтению следующего раздела, попробуйте самостоятельно записать что-нибудь (при помощи write(), естественно) в обычный файл. Когда будете открывать файл для записи, обратите пожалуйста внимание на флаги O_TRUNC, O_CREAT и O_APPEND. Подумайте, все ли флаги сочетаются между собой по смыслу.

5.7. Произвольный доступ: системный вызов lseek()

Как уже говорилось, с каждым открытым файлом связано число, указывающее на текущую позицию чтения-записи. При открытии файла позиция равна нулю. Каждый вызов read() или write() увеличивает текущую позицию на значение, равное числу прочитанных или записанных байт. Благодаря этому механизму, каждый повторный вызов read() читает следующие данные, и каждый повторный write() записывает данные в продолжение предыдущих, а не затирает старые. Такой механизм последовательного доступа очень удобен, однако иногда требуется получить произвольный доступ к содержимому файла, чтобы, например, прочитать или записать файл заново.

Для изменения текущей позиции чтения-записи используется системный вызов lseek(). Ниже представлен его прототип.

Первый аргумент, как всегда, — файловый дескриптор. Второй аргумент — смещение, как положительное (вперед), так и отрицательное (назад). Третий аргумент обычно передается в виде одной из трех констант SEEK_SET, SEEK_CUR и SEEK_END, которые показывают, от какого места отсчитывается смещение. SEEK_SET — означает начало файла, SEEK_CUR — текущая позиция, SEEK_END — конец файла. Рассмотрим следующие вызовы:

Первый вызов устанавливает текущую позицию в начало файла. Второй вызов смещает позицию вперед на 20 байт. В третьем случае текущая позиция перемещается на 10 байт назад относительно конца файла.

В случае удачного завершения, lseek() возвращает значение установленной «новой» позиции относительно начала файла. В случае ошибки возвращается -1.

Я долго думал, какой бы пример придумать, чтобы продемонстрировать работу lseek() наглядным образом. Наиболее подходящим примером мне показалась идея создания программы рисования символами. Программа оказалась не слишком простой, однако если вы сможете разобраться в ней, то можете считать, что успешно овладели азами низкоуровневого ввода-вывода Linux. Ниже представлен исходный код этой программы.

Теперь разберемся, как работает эта программа. Изначально «полотно» заполняется пробелами. Функция init_draw() построчно записывает в файл пробелы, чтобы получился «холст», размером N_ROWS на N_COLS. Массив строк icode в функции main() — это набор команд рисования. Команда начинается с одной из трех литер: ‘v’ — нарисовать вертикальную линию, ‘h’ — нарисовать горизонтальную линию, ‘p’ — нарисовать точку. После каждой такой литеры следуют три числа. В случае вертикальной линии первое число — фиксированная координата X, а два других числа — это начальная и конечная координаты Y. В случае горизонтальной линии фиксируется координата Y (первое число). Два остальных числа — начальная координата X и конечная координата X. При рисовании точки используются только два первых числа: координата X и координата Y. Итак, функция draw_vline() рисует вертикальную линию, функция draw_hline() рисует горизонтальную линию, а draw_point() рисует точку.

Функция init_draw() пишет в файл N_ROWS строк, каждая из которых содержит N_COLS пробелов, заканчивающихся переводом строки. Это процедура подготовки «холста».

Функция draw_point() вычисляет позицию (исходя из значений координат), перемещает туда текущую позицию ввода-вывода файла, и записывает в эту позицию символ (FG_CHAR), которым мы рисуем «картину».

Функция draw_hline() заполняет часть строки символами FG_CHAR. Так получается горизонтальная линия. Функция draw_vline() работает иначе. Чтобы записать вертикальную линию, нужно записывать по одному символу и каждый раз «перескакивать» на следующую строку. Эта функция работает медленнее, чем draw_hline(), но иначе мы не можем.

Полученное изображение записывается в файл image. Будьте внимательны: чтобы разгрузить исходный код, из программы исключены многие проверки (read(), write(), close(), диапазон координат и проч.). Попробуйте включить эти проверки самостоятельно.

Илон Маск рекомендует:  Формат и синтаксис cookie
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL