closedir — Освободить дескриптор каталога


Содержание

Closedir

Php функции


Php скрипты


closedir

(PHP 3, PHP 4, PHP 5)

closedir — Освободить дескриптор каталога

Описание

void closedir ( resource dir_handle )

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

User Contributed Notes

foo at bar dot com
24-Nov-2000 09:35

About deleting a directory after doing a readdir /closedir on it. I’m not sure if this is the solution, but you could try to chdir («/»); before the rmdir to make absolutely sure you aren’t standing in the directory (i.e trying to pull out the rug from under yourself).

Каковы последствия не закрытия дескриптора каталога в Perl?

6 BrianH [2010-09-10 21:18:00]

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

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

Код был примерно таким:

(Это еще одна хорошая точка, которая была сделана в Perl Best Practices (страницы 208, 278) о проверке возврата функции close . Если в этом случае был проверен возврат close , это приведет к сбою с «Плохой номер файла».)

С тех пор я изменил это на closedir , но это заставило меня начать задаваться вопросом: поскольку дескриптор каталога никогда не был закрыт, каковы отрицательные последствия для сохранения дескриптора каталога в течение длительного времени?

Эта программа больше (3500 строк кода), выполняется некоторое время (5-10 минут), и несколько экземпляров этой программы работают одновременно. В случае с этим каталогом в приведенном выше примере $dir является одинаковым значением для всех экземпляров. Если 10 экземпляров этой программы были запущены одновременно, все они держали дескриптор открытого каталога в том же каталоге в течение 5 минут или дольше. Я уверен, что Perl автоматически закрывает дескриптор каталога, когда заканчивается программа, но лучше всего подходит как можно скорее закрыть его.

Для меня более очевидно, что оставить открытые дескрипторы файлов могут вызвать проблемы (особенно для дескрипторов файлов, которые открыты для записи), но что может произойти, если не закрыть дескриптор каталога?

Причина, по которой я спрашиваю, заключается в том, что было странное обстоятельство, когда эта программа пыталась создать файл (в каталоге, определяемом параметром $dir выше). В имени файла был встроен PID, поэтому вероятность того, что файл уже существует, меньше, но Perl не смог открыть файл для записи, потому что он сказал, что он уже существует. Когда мы смотрели в каталоге, этого файла не было. Мне интересно, может ли возникнуть такая проблема при работе с открытым каталогом каталога в этом каталоге?

Я не уверен, что ОС имеет значение, но эта программа работает в AIX.

Спасибо заранее, и счастливая пятница!

3 ответа

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

Если дескриптор каталога был локальной переменной, а не простым именем типа DIR, вы могли бы очистить Perl за вами. См. opendir, в котором говорится:

Открывает каталог с именем EXPR для обработки с помощью readdir, telldir, seekdir, rewinddir и closedir. Возвращает true в случае успеха. DIRHANDLE может быть выражением, значение которого может использоваться как косвенный dirhandle, обычно это имя реального dirhandle. Если DIRHANDLE является скалярной переменной undefined (или массивом или хэш-элементом), переменной присваивается ссылка на новый анонимный dirhandle. DIRHANDLE имеют собственное пространство имен, отдельно от FILEHANDLE.

7 rafl [2010-09-10 21:32:00]

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

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

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

6 Ether [2010-09-10 21:33:00]

Это урок, который всегда используется для обработки лексических файлов (и directory-) — лексические дескрипторы автоматически закрываются, когда они выходят из области видимости.

Таким образом, вы будете тратить только дескрипторы (как описывает Джонатан), если 1. вы использовали ручку glob старого стиля или 2. весь код находится в плоском script без каких-либо подпрограмм или других областей. Используйте хорошие методы программирования, а непреднамеренных ошибок будет меньше:)

а существует ли способ закрыть файловый дескриптор альтернативный close().

shutdown() для сокета, хотя несколько иная функциональность.

exit() еще покатит )

Re: а существует ли способ закрыть файловый дескриптор альтернативный close().

>shutdown() для сокета, хотя несколько иная функциональность.

хм. насколько я знаю shutdown закрывает соединение не трогая(не освобождая) связанный с ним файловый дескриптор. если я неправ то можно ссылку.

>exit() еще покатит )

Re: а существует ли способ закрыть файловый дескриптор альтернативный close().

shutdown() закрывает сокет, независимо от того, сколько ссылок имеется на дескриптор — вот его главное отличие от close().

Re: а существует ли способ закрыть файловый дескриптор альтернативный close().


Вот, из Unix-Socket-FAQ:

Generally the difference between close() and shutdown() is: close() closes the socket id for the process but the connection is still opened if another process shares this socket id. The connection stays opened both for read and write, and sometimes this is very important. shutdown() breaks the connection for all processes sharing the socket id. Those who try to read will detect EOF, and those who try to write will reseive SIGPIPE, possibly delayed while the kernel socket buffer will be filled. Additionally, shutdown() has a second argument which denotes how to close the connection: 0 means to disable further reading, 1 to disable writing and 2 disables both.

Re: а существует ли способ закрыть файловый дескриптор альтернативный close().

спасибо за наводку но вы всёже перепутали закрытие сокета и завершение соединения.

внимательно перечитайте вот это:

>Those who try to read will detect EOF, and those who try to write will reseive SIGPIPE, possibly delayed while the kernel socket buffer will be filled.

на закрытых файловых дескрипторах в обеих случаях будет возвращатся EBADF

Re: а существует ли способ закрыть файловый дескриптор альтернативный close().

—cut— The shutdown() function shall cause all or part of a full-duplex connection on the socket associated with the file descriptor socket to be shut down. —cut—

-> shutdown(2) закрывает логическое соединение, но не освобождает файловый дескриптор процесса. как пример: на сокет после shutdown(2) пожно вызвать recv(2) и получить 0 или SIGPIPE, но не -1 и EBADF.

Re: а существует ли способ закрыть файловый дескриптор альтернативный close().

Делитанты, специально для Вас написал:

Если вы взглянете на

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

Re: а существует ли способ закрыть файловый дескриптор альтернативный close().

впрочем, с точки зрения практики отчасти соглашусь: аналогично в *BSD после soshutdown() вызывается FILE_UNUSE(). однако, это лишь декремент счетчика использования fd -> это != close(2).

ps: я бы не стал кидаться громкими словами «дилетанты». ссылку на стандарт вам привели и в нём не указано явным образом, должен ли освобождаться файловый дескриптор или нет -> AFAIU в принципе тот или иной сценарий на совести разработчиков.

Re: а существует ли способ закрыть файловый дескриптор альтернативный close().

так если покапаться в ядре (времени нет на это), уверен, что можно найти место где возвращается эта самая ошибка согласно стандарту. Тоесть дискритоптор уже освобожден и, скажем, recv получит не «плохой дискритор», а именно Эошибку канала». Тоесть реализация соответсвует спецификации. В чем проблема? Пока не глянешь в ядро, ответа точного дать нельзя освобождается дескриптор или нет. А вы сказали что не освобождается. чем были не правы!

Closedir — Освободить дескриптор каталога

Файлы и каталоги в php

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

Итак перед нами стоит задача:

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

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

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

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

Вот как я это сделал:

Давайте пошагово разберем код скрипта.
Сначала я описывю функцию read2Dir(), которая собственно и будет выполнять всю работу.

Она принимает следующие параметры:

$dir Директория, которую мы будем сканировать $offs Параметр который определяет вид отсупа при выводе данных в браузер

Затем с помощью условного оператора if (он просто записан немного по другому) проверяем содержится ли слэш (/) в конце директории.

И если он там есть, то оставляем все как есть, а в противном случае добавляем его в конец.

Сначала проверяем является ли директорией на самом деле, адрес по которму нужно произвести сканирование — функция is_dir().

И если это не так выходим из функции возвращая FALSE. Ну а если это все-таки каталог, то с помощью функции opendir() получаем дескриптор каталога для последующего использования, который в свою очередь помогает функции readdir() получить элемент каталога по этому самому дескриптору.

А получать элементы каталога мы будем в цикле whille до тех пор пока элемент каталога существуют — т.е. не тождественнен FALSE. Или говоря проще в цикле будем получать элементы пока они не закончаться.

Обратите внимание, что проверять нужно именно на тождественность.

В итоге должна получится примерно такая строка — (documents/file.txt), конечно если полученный элемент является файлом, ну а в противном случае полученный элемент это каталог и соответственно нам нужно снова выполнить его сканирование.

Для этого сначала функцией basename(), которая возвращает имя файла из указанного пути, проверяем не является ли полученный элемент » . » или » .. »
Если это так то пропускаем итерацию цикла whille используя оператор continue.

Далее определяем с помощью функции filetype() определяем тип файла.
Эта функция возвращает одно из возможных значений: fifo, char, dir, block, link, file или unknown.

Но в нашем конкретном случае нас интересует тольлько dir, и если это так то тут то и запускаем рекурсию.

То есть снова вызываем нашу функцию read2Dir(), только на этот раз передаем ей текущий элемент и пристыковываем (конкатенируем) к переменной $offset дополнительный отступ.

Ну а если полученный при чтении каталога элемент, то проверяем расширение файла. Сделать это проще всего проверив на соответствие искомому расширению три последние символа.

Получаем их используя функцию substr(), которая возвращает подстроку. Причем я указал, что меня интересуют именно последние 3 символа.

Ну а дальше совсем просто.
Если расширение соответствует назначенному нами, тогда с помощью функции filesize(), которая возвращает размер указанного файла в байтах или FALSE в случае возникновения ошибки, получаем размер файла и оператором echo выводим его в браузер.


И так снова, снова и снова, при необходимости опять рекурсивно вызывая нашу функцию read2Dir() работает цикл whille, пока не будет прочитан последний файл в заданной директории.

Поработали? Тогда не забываем освободить дескриптор каталога функцией closedir (), которая закрывает поток связанный с каталогом.

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

Экспериментируйте на здоровье!

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

На этом сегодня все. С удовольствием отвечу на ваши вопросы, если они у вас есть. До свидания.

Автор: Сергей Зарубин

Дата: 2012-03-03

Колличество просмотров: 6146

Комментарии к заметке:

Комментарий добавил(а): ivanscm
Дата: 2012-10-18

Комментарий добавил(а): mara
Дата: 2012-11-24

Closedir — Освободить дескриптор каталога

27 просмотра

1 ответ

358 Репутация автора

Фон

В Linux или BSD можно отправлять дескрипторы для открытия файлов или сокетов между несвязанными процессами с SCM_RIGHTS , и у меня это работает, так что в процессе прослушивается соединение, а затем пересылает дескрипторы процессу, который выполняет связь.

проблема

Я не могу понять, как освободить дескриптор сокета из процесса прослушивания без отключения сокета.

Существует два противоречивых описания man close(3) :

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

Если fildes относится к сокету, close () приведет к уничтожению сокета.

Первоначально я думал, что это означает, что вызов close() просто уменьшит счетчик ссылок для объекта ядра, у которого есть сокет, так что последнее описание man close(3) означает «уничтожить, когда последний дескриптор закрыт».

Но, когда я запускаю тесты, кажется, что как только я вызову close() дескриптор сокета в процессе прослушивания, он начнет закрывать сокет, отправляя либо RST или FIN , в зависимости от того, что другой процесс делает с сокетом в то время.

Одним из решений было бы иметь обратный вызов из процесса передачи с «теперь вы можете закрыть сокет nnn», но это позволит сохранить в дескрипторе несколько дескрипторов сокета и добавить некоторые накладные расходы.

Илон Маск рекомендует:  frameset в HTML

Я знаю, что могу заставить сокет начать процесс выключения, вызывая shutdown() прямо из любого процесса, но я хочу его предотвратить .

Я предполагаю, что существует простое решение, но я не могу его найти.

Вопрос

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

Исходный код

SCM_RIGHTS Реализация используется для отправки сокета здесь ( send_fds а native_close ):

Код, который отправляет сокет, а затем закрывает его, находится здесь:

Если я прокомментирую строку 497, все будет работать, но, очевидно, вы получите утечку больших файлов.

Получающий конец SCM_RIGHTS здесь:

Ответы (1)

плюса

82180 Репутация автора

man unix(7) не указывает, когда будут отправлены дескрипторы файлов, отправленные в другой процесс dup .

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

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

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

Основными операциями, предоставляемыми ядром операционной системы программам (а точнее — процессам) для работы с файлами, являются системные вызовы open read, write и close. В соответствии со своими именами, эти системные вызовы предназначены для открытия и закрытия файла, для чтения из файла и записи в файл. Дополнительный системный вызов ioctl (input output control) используется для управления драйверами устройств и, как следствие, применяется в основном для специальных файлов устройств.

При запросе процесса на открытие файла системным вызовом оpen производится его однократный (относительно медленный) поиск имени файла в дереве каталогов и для запросившего процесса создается так называемый файловый дескриптор (описатель, от англ, descriptor).


Файловый дескриптор «содержит» информацию, описывающую файл, например индексный дескриптор inode файла на файловой системе, номера major и minor устройства, на котором располагается файловая система файла, режим открытия файла, и прочую служебную информацию.

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

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

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

Таблица файловых дескрипторов

$ lsof -р $$

COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME

. . . . . . . . .
bash 17975 john 1u CHR 136,2 0t0 5 /dev/pts/2

# lsof /dev/log

COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME

rsyslogd 543 syslog 0u unix 0xefef5680 0t0 1338 /dev/log

# fuser /dev/log

/dev/log: 543
root@ubuntu:

# ps p 543

PID TTY STAT TIME COMMAND
543 ? Sl 0:43 rsyslogd -c5

# lsof /var/log/syslog

COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME

rsyslogd 543 syslog lw REG 252,0 29039 26214496 /var/log/syslog

В первом примере из листинга выше показано получение списка файловых дескрипторов (столбец FD) процесса командного интерпретатора bash пользователя john, на котором файловый дескриптор номер 1 описывает открытый на чтение и запись и специальный символьный CHR файл устройства /dev/pts/2.

Во втором примере показано получение информации о процессе, открывшем файловый сокет unix с именем /dev/log (файловый дескриптор номер 0 на чтение и запись u) и обычный файл REG с именем /var/log/sysog (файловый дескриптор номер 1 на запись w).

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

Трассировка файлового программного интерфейса

$ date

Вт. окт. 15 18:17:42 MSK % 2020

$ strace -fe open, close, read, write, ioctl date

open(«/etc/localtime», 0_RDONLY|0_CLOEXEC) = 3

$ file /etc/localtime
/etc/localtime: timezone data, version 2, 13 gmt time flags, 13 std time flags, no leap

seconds, 77 transition tines, 13 abbreviation char
john@ubuntu:

$ ls -la /dev/dvd
lrwxrwxrwx 1 root root 3 окт. 16 18:09 /dev/dvd -> sr0

$ strace -fe open,close, read,write,ioctl eject

. . . . . . . . .
open(«/dev/sr0», O_RDWR|O_N0NBLOCK) = 3
ioctl(3, CDROMEJECT, 0x804cb4e) = 0
closed(3) = 0
john@ubuntu:

$ strace -fe open, read,write,close,ioctl setleds -L +num +scroll
ioctl(0, KDGKBLED, 0xbfe4f4ff) = 0
ioctl(0, KDGETLED, 0xbfe4f4fe) = 0
ioctl(0, KDSETLED, 0x3) = 0

Предположив, что программа date показывает правильное московское время, потому что узнаёт заданную временную зону MSK из некоего конфигурационного файла операционной системы, при трассировке ее «работы можно установить его точное имя — /etc/localtime.

Аналогично предположив, что программа eject открывает лоток привода CD/DVD при помощи специального файла устройства, при трассировке можно узнать имя файла /dev/sr0, номер файлового дескриптора при работе с файлом 3 и команду CDROMEJECT соответствующего устройству
драйвера ioctl_List.

Трассировка команды setleds показывает, что она вообще не открывает никаких файлов, но пользуется файловым дескриптором о так называемого стандартного потока ввода (прикрепленного, к текущему терминалу) и командами kdgetled и kdsetled драйвера консоли console_ioctl.

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

Фон

В Linux или BSD можно отправлять дескрипторы для открытия файлов или сокетов между несвязанными процессами с помощью SCM_RIGHTS , и у меня это работает, так что в процессе прослушивание соединений, а затем перенаправляет дескрипторы процессу, который осуществляет связь.

проблема

Я не могу понять, как освободить дескриптор сокета от процесса прослушивания, не закрывая сокет.

Есть два противоречивых описания в man close(3) :

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

Если fildes ссылается на сокет, close() вызовет его уничтожение.

Первоначально я думал, что это означает, что вызов close() просто уменьшит счетчик ссылок для объекта ядра, у которого есть сокет, так что последнее описание из man close(3) означало «уничтожить, когда последний дескриптор закрыт».

РЕДАКТИРОВАТЬ: Вот как это должно работать, а также как это работает.


Но когда я запускаю тесты, оказывается, что как только я вызываю close() для дескриптора сокета в процессе прослушивания, он начинает закрывать сокет, отправляя либо RST либо FIN , в зависимости от того, что другой процесс делает с розетка в самый раз.

Одним из решений было бы иметь обратный вызов от процесса передачи с «теперь вы можете закрыть socket nnn», но это будет держать ряд дескрипторов сокетов открытыми в процессе прослушивания и также добавит некоторые издержки.

Я знаю, что могу заставить сокет запустить процесс завершения работы, вызывая shutdown() напрямую из любого процесса, но я хочу предотвратить это.

Я предполагаю, что существует простое решение, но я не могу его найти.

Вопрос

Есть ли способ отменить регистрацию дескриптора сокета в процессе прослушивания, чтобы его больше не было в таблице дескрипторов файла процесса, но без активации завершения работы сокета?

Исходный код

Реализация SCM_RIGHTS используемая для отправки сокета, находится здесь ( send_fds и native_close ):

Код, который отправляет сокет, а затем закрывает его, находится здесь:

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

Closedir — Освободить дескриптор каталога

Свои услуги операционная система UNIX предлагает в виде набора системных вызовов, которые фактически являются ее внутренними функциями и к которым можно обращаться из программ пользователя. В настоящей главе описано, как в Си-программах можно применять некоторые наиболее важные вызовы. Если вы работаете в системе UNIX, то эти сведения будут вам полезны непосредственно и позволят повысить эффективность работы или получить доступ к тем возможностям, которых нет в библиотеке. Даже если вы используете Си в другой операционной системе, изучение рассмотренных здесь примеров все равно приблизит вас к пониманию программирования на Си; аналогичные программы (отличающиеся лишь деталями) вы встретите практически в любой операционной системе. Так как библиотека Си-программ, утвержденная в качестве стандарта ANSI, в основном отражает возможности системы UNIX, предлагаемые программы помогут вам лучше понять и библиотеку.

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

8.1 Дескрипторы файлов

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

В самом общем случае, прежде чем читать или писать, вы должны проинформировать систему о действиях, которые вы намереваетесь выполнять в отношении файла; эта процедура называется открытием файла. Если вы собираетесь писать в файл, то, возможно, его потребуется создать заново или очистить от хранимой информации. Система проверяет ваши права на эти действия (файл существует? вы имеете к нему доступ?) и, если все в порядке, возвращает программе небольшое неотрицательное целое, называемое дескриптором файла. Всякий раз, когда осуществляется ввод-вывод, идентификация файла выполняется по его дескриптору, а не по имени. (Дескриптор файла аналогичен файловому указателю, используемому в стандартной библиотеке, или хэндлу (handle) в MS- DOS.) Вся информация об открытом файле хранится и обрабатывается операционной системой; программа пользователя обращается к файлу только через его дескриптор.

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

Пользователь программы имеет возможность перенаправить ввод-вывод в файл или из файла с помощью значков , как, например, в

В этом случае командный интерпретатор заменит стандартные установки дескрипторов 0 и 1 на именованные файлы. Обычно дескриптор файла 2 остается подсоединенным к экрану, чтобы на него шли сообщения об ошибках. Сказанное верно и для ввода-вывода, связанного в конвейер. Во всех случаях замену файла осуществляет командный интерпретатор, а не программа. Программа, если она ссылается на файл 0 (в случае ввода) и файлы 1 и 2 (в случае вывода), не знает, ни откуда приходит ее ввод, ни куда отправляется ее вывод.

8.2 Нижний уровень ввода-вывода (read и write)

Ввод-вывод основан на системных вызовах read и write, к которым Си-программа обращается с помощью функций с именами read и write.

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

Обе функции возвращают число переданных байтов. При чтении количество прочитанных байтов может оказаться меньше числа, указанного в третьем аргументе. Нуль означает конец файла, а -1 сигнализирует о какой-то ошибке. При записи функция возвращает количество записанных байтов, и если это число не совпадает с требуемым, следует считать, что запись не произошла. За один вызов можно прочитать или записать любое число байтов. Обычно это число равно или 1, что означает посимвольную передачу «без буферизации», или чему-нибудь вроде 1024 или 4096, соответствующих размеру физического блока внешнего устройства. Эффективнее обмениваться большим числом байтов, поскольку при этом требуется меньше системных вызовов. Используя полученные сведения, мы можем написать простую программу, копирующую свой ввод на свой вывод и эквивалентную программе копирования файла, описанной в главе 1. С помощью этой программы можно копировать откуда угодно и куда угодно, поскольку всегда существует возможность перенаправить ввод-вывод на любой файл или устройство.

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

Параметр BUFSIZ также определен в : в каждой конкретной системе он имеет свое значение. Если размер файла не кратен BUFSIZ, то какая-то операция чтения вернет значение меньшее, чем BUFSIZ, а следующее обращение к read даст в качестве результата нуль.

Полезно рассмотреть, как используются read и write при написании программ более высокого уровня — таких как getchar, putchar и т. д. Вот, к примеру, версия программы getchar, которая осуществляет небуферизованный ввод, читая по одному символу из стандартного входного потока.

Переменная c должна быть типа char, поскольку read требует указателя на char. Приведение c к unsigned char перед тем, как вернуть ее в качестве результата, исключает какие-либо проблемы, связанные с распространением знака.

Вторая версия getchar осуществляет ввод большими кусками, но при каждом обращении выдает только один символ.

Если приведенные здесь версии функции getchar компилируются с включением заголовочного файла и в этом заголовочном файле getchar определена как макрос, то нужно задать строку #undef с именем getchar.

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

Функция open почти совпадает с fopen, рассмотренной в главе 7. Разница между ними в том, что первая возвращает не файловый указатель, а дескриптор файла типа int. При любой ошибке open возвращает -1.

Как и в fopen, аргумент name — это строка, содержащая имя файла. Второй аргумент, flags, имеет тип int и специфицирует, каким образом должен быть открыт файл. Его основными значениями являются:

В System V UNIX эти константы определены в , а в версиях Berkley (BSD) — в .

Чтобы открыть существующий файл на чтение, можно написать

Далее везде, где мы пользуемся функцией open, ее аргумент perms равен нулю.

Попытка открыть несуществующий файл является ошибкой. Создание нового файла или перезапись старого обеспечивается системным вызовом creat. Например

Функция creat возвращает дескриптор файла, если файл создан, и -1, если по каким-либо причинам файл создать не удалось. Если файл уже существует, creat «обрежет» его до нулевой длины, что равносильно выбрасыванию предыдущего содержимого данного файла; создание уже существующего файла не является ошибкой.

Если строится действительно новый файл, то creat его создаст с правами доступа, специфицированными в аргументе perms. В системе UNIX с каждым файлом ассоциированы девять битов, содержащие информацию о правах пользоваться этим файлом для чтения, записи и исполнения лицам трех категорий: собственнику файла, определенной им группе лиц и всем остальным. Таким образом, права доступа удобно специфицировать с помощью трех восьмеричных цифр. Например, 0755 специфицирует чтение, запись и право исполнения собственнику файла, а также чтение и право исполнения группе и всем остальным.

Для иллюстрации приведем упрощенную версию программы cp системы UNIX, которая копирует один файл в другой. В нашей версии копируется только один файл, не позволяется во втором аргументе указывать директорий (каталог), и права доступа не копируются, а задаются константой.

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

Заметим, что функция error, вызываемая с различным числом аргументов, во многом похожа на printf. Реализация error иллюстрирует, как пользоваться другими программами семейства printf. Библиотечная функция vprintf аналогична printf, с той лишь оговоркой, что переменная часть списка аргументов заменена в ней одним аргументом, который инициализируется макросом va_start. Подобным же образом соотносятся функции vfprinf с fprintf и vsprintf с sprintf.

Илон Маск рекомендует:  Что такое код asp cgitimeout

На количество одновременно открытых в программе файлов имеется ограничение (обычно их число колеблется около 20). Поэтому любая программа, которая намеревается работать с большим количеством файлов, должна быть готова повторно использовать их дескрипторы. Функция close(int fd) разрывает связь между файловым дескриптором и открытым файлом и освобождает дескриптор для его применения с другим файлом. Она аналогична библиотечной функции fclose с тем лишь различием, что никакой очистки буфера не делает. Завершение программы с помощью exit или return в главной программе закрывает все открытые файлы.

Функция unlink(char *name) удаляет имя файла из файловой системы. Она соответствует функции remove стандартной библиотеки.


Упражнение 8.1. Перепишите программу cat из главы 7, используя функции read, write, open и close. Замените ими соответствующие функции стандартной библиотеки. Поэкспериментируйте, чтобы сравнить быстродействие двух версий.

8.4 Произвольный доступ (lseek)

Ввод-вывод обычно бывает последовательным, т. е. каждая новая операция чтения-записи имеет дело с позицией файла, следующей за той, что была в предыдущей операции (чтения-записи). При желании, однако, файл можно читать или производить запись в него в произвольном порядке. Системный вызов lseek предоставляет способ передвигаться по файлу, не читая и не записывая данные. Так, функция

в файле с дескриптором fd устанавливает текущую позицию, смещая ее на величину offset относительно места, задаваемого значением origin. Значения параметра origin 0, 1 или 2 означают, что на величину offset отступают соответственно от начала, от текущей позиции или от конца файла. Например, если требуется добавить что-либо в файл (когда в командном интерпретаторе shell системы UNIX ввод перенаправлен оператором >> в файл или когда в fopen задан аргумент «a«), то прежде чем что-либо записывать, необходимо найти конец файла с помощью вызова функции

Чтобы вернуться назад, в начало файла, надо выполнить

Следует обратить внимание на аргумент 0L: вместо 0L можно было бы написать (long)0 или, если функция lseek должным образом объявлена, просто 0. Благодаря lseek с файлами можно работать так, как будто это большие массивы, правда, с замедленным доступом. Например, следующая функция читает любое число байтов из любого места файла. Она возвращает число прочитанных байтов или -1 в случае ошибки.

Возвращаемое функцией lseek значение имеет тип long и является новой позицией в файле или, в случае ошибки, равно -1. Функция fseek из стандартной библиотеки аналогична lseek: от последней она отличается тем, что в случае ошибки возвращает некоторое ненулевое значение, а ее первый аргумент имеет тип FILE*.

8.5 Пример. Реализация функций fopen и getc

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

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

Структура данных, описывающая файл, содержится в , который необходимо включать (с помощью #include) в любой исходный файл, если в том осуществляется стандартный ввод-вывод. Этот же заголовочный файл включен и в исходные тексты библиотеки ввода-вывода.

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

Макрос getc обычно уменьшает счетчик числа символов, находящихся в буфере, и возвращает символ, после чего приращивает указатель на единицу. (Напомним, что длинные #define с помощью обратной наклонной черты можно продолжить на следующих строках.) Когда значение счетчика становится отрицательным, getc вызывает _fillbuf, чтобы снова заполнить буфер, инициализировать содержимое структуры и выдать символ. Типы возвращаемых символов приводятся к unsigned; это гарантирует, что все они будут положительными.

Хотя в деталях ввод-вывод здесь не рассматривается, мы все же привели полное определение putc. Сделано это, чтобы показать, что она действует во многом так же, как и getc, вызывая функцию _flushbuf, когда буфер полон. В тексте имеются макросы, позволяющие получать доступ к флажкам ошибки и конца файла, а также к его дескриптору.

Теперь можно написать функцию fopen. Большая часть инструкций fopen относится к открытию файла, к соответствующему его позиционированию и к установке флажковых битов, предназначенных для индикации текущего состояния. Сама fopen не отводит места для буфера; это делает _fillbuf при первом чтении файла.

Приведенная здесь версия fopen реализует не все режимы доступа, оговоренные стандартом; но, мы думаем, их реализация в полном объеме не намного увеличит длину программы. Наша fopen не распознает буквы b, сигнализирующей о бинарном вводе-выводе (поскольку в системах UNIX это не имеет смысла), и знака +, указывающего на возможность одновременно читать и писать.

Для любого файла в момент первого обращения к нему с помощью макровызова getc счетчик cnt равен нулю. Следствием этого будет вызов _fillbuf. Коли выяснится, что файл на чтение не открыт, то функция _fillbuf немедленно возвратит EOF. В противном случае она попытается запросить память для буфера (если чтение должно быть с буферизацией).

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

Единственное, что осталось невыясненным, — это каким образом организовать начало счета. Массив _iob следует определить и инициализировать так, чтобы перед тем как программа начнет работать, в нем уже была информация о файлах stdin, stdout и stderr.

Инициализация flag как части структуры показывает, что stdin открыт на чтение, stdout — на запись, а stderr — на запись без буферизации.

Упражнение 8.2. Перепишите функции fopen и _fillbuf, работая с флажками как с полями, а не с помощью явных побитовых операций. Сравните размеры и скорости двух вариантов программ.

Упражнение 8.3. Разработайте и напишите функции _flushbuf, fflush и fclose.

Упражнение 8.4. Функция стандартной библиотеки

идентична функции lseek с теми, однако, отличиями, что fp — это файловый указатель, а не дескриптор, и возвращает она значение int, означающее состояние файла, а не позицию в нем. Напишите свою версию fseek. Обеспечьте, чтобы работа вашей fseek по буферизации была согласована с буферизацией, используемой другими функциями библиотеки.

8.6 Пример. Печать каталогов

При разного рода взаимодействиях с файловой системой иногда требуется получить только информацию о файле, а не его содержимое. Такая потребность возникает, например, в программе печати каталога файлов, работающей аналогично команде ls системы UNIX. Она печатает имена файлов каталога и по желанию пользователя другую дополнительную информацию (размеры, права доступа и т. д.). Аналогичной командой в MS-DOS является dir.

Так как в системе UNIX каталог — это тоже файл, функции ls, чтобы добраться до имен файлов, нужно только его прочитать. Но чтобы получить другую информацию о файле (например узнать его размер), необходимо выполнить системный вызов. В других системах (в MS-DOS, например) системным вызовом приходится пользоваться даже для получения доступа к именам файлов. Наша цель — обеспечить доступ к информации по возможности системно-независимым способом несмотря на то, что реализация может быть существенно системно-зависима. Проиллюстрируем сказанное написанием программы fsize. Функция fsize — частный случай программы ls: она печатает размеры всех файлов, перечисленных в командной строке. Если какой-либо из файлов сам является каталогом, то, чтобы получить информацию о нем, fsize обращается сама к себе. Если аргументов в командной строке нет, то обрабатывается текущий каталог.

Для начала вспомним структуру файловой системы в UNIXe. Каталог — это файл, содержащий список имен файлов и некоторую информацию о том, где они расположены. «Место расположения» — это индекс, обеспечивающий доступ в другую таблицу, называемую «списком узлов inode«. Для каждого файла имеется свой inode, где собрана вся информация о файле, за исключением его имени. Каждый элемент каталога состоит из двух частей: из имени файла и номера узла inode.

К сожалению, формат и точное содержимое каталога не одинаковы в разных версиях системы. Поэтому, чтобы переносимую компоненту отделить от непереносимой, разобьем нашу задачу на две. Внешний уровень определяет структуру, названную Dirent, и три подпрограммы opendir, readdir и closedir: в результате обеспечивается системно-независимый доступ к имени и номеру узла inode каждого элемента каталога. Мы будем писать программу fsize, рассчитывая на такой интерфейс, а затем покажем, как реализовать указанные функции для систем, использующих ту же структуру каталога, что и Version 7 и System V UNIX. Другие варианты оставим для упражнений.

Структура Dirent содержит номер узла inode и имя. Максимальная длина имени файла равна NAME_MAX — это значение системно-зависимо. Функция opendir возвращает указатель на структуру, названную DIR (по аналогии с FILE), которая используется функциями readdir и closedir. Эта информация сосредоточена в заголовочном файле dirent.h.

Системный вызов stat получает имя файла и возвращает полную о нем информацию, содержащуюся в узле inode, или -1 в случае ошибки. Так,

заполняет структуру stbuf информацией из узла inode о файле с именем name. Структура, описывающая возвращаемое функцией stat значение находится в и выглядит примерно так:

Большинство этих значений объясняется в комментариях. Типы, подобные dev_t и ino_t, определены в файле , который тоже нужно включить посредством #include.

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

Теперь мы готовы приступить к написанию программы fsize. Если режимные биты (st_mode), полученные от stat, указывают, что файл не является каталогом, то можно взять его размер (st_size) и напечатать. Однако если файл — каталог, то мы должны обработать все его файлы, каждый из которых в свою очередь может быть каталогом. Обработка каталога — процесс рекурсивный.

Программа main просматривает параметры командной строки, передавая каждый аргумент функции fsize.

Функция fsize печатает размер файла. Однако, если файл — каталог, она сначала вызывает dirwalk, чтобы обработать все его файлы. Обратите внимание на то, как используются имена флажков S_IFMT и S_IFDIR из при проверке, является ли файл каталогом. Здесь нужны скобки, поскольку приоритет оператора & ниже приоритета оператора ==.

Функция dirwalk — это универсальная программа, применяющая некоторую функцию к каждому файлу каталога. Она открывает каталог, с помощью цикла перебирает содержащиеся в нем файлы, применяя к каждому из них указанную функцию, затем закрывает каталог и осуществляет возврат. Так как fsize вызывает dirwalk на каждом каталоге, в этих двух функциях заложена косвенная рекурсия.

Каждый вызов readdir возвращает указатель на информацию о следующем файле или NULL, если все файлы обработаны. Любой каталог всегда хранит в себе информацию о себе самом в файле под именем «.» и о своем родителе в файле под именем «..»: их нужно пропустить, иначе программа зациклится. Обратите внимание: код программы этого уровня не зависит от того, как форматированы каталоги. Следующий шаг — представить минимальные версии opendir, readdir и closedir для некоторой конкретной системы. Здесь приведены программы для систем Version 7 и System V UNIX. Они используют информацию о каталоге, хранящуюся в заголовочном файле , который выглядит следующим образом:

Некоторые версии системы допускают более длинные имена и имеют более сложную структуру каталога.

Тип ino_t задан с помощью typedef и описывает индекс списка узлов node. В системе, которой пользуемся мы, этот тип есть unsigned short, но в других системах он может быть иным, поэтому его лучше определять через typedef. Полный набор «системных» типов находится в .

Функция opendir открывает каталог, проверяет, является ли он действительно каталогом (в данном случае это делается с помощью системного вызова fstat, который аналогичен stat, но применяется к дескриптору файла), запрашивает пространство для структуры каталога и записывает информацию.

Функция closedir закрывает каталог и освобождает пространство.

Наконец, readdir с помощью read читает каждый элемент каталога. Если некий элемент каталога в данный момент не используется (соответствующий ему файл был удален), то номер узла inode у него равен нулю, и данная позиция пропускается. В противном случае номер inode и имя размещаются в статической (static) структуре, и указатель на нее выдается в качестве результата. При каждом следующем обращении новая информация занимает место предыдущей.

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


функции стандартной библиотеки.

Упражнение 8.5. Модифицируйте fsize таким образом, чтобы можно было печатать остальную информацию, содержащуюся в узле inode.

8.7 Пример. Распределитель памяти

В главе 5 был описан простой распределитель памяти, основанный на принципе стека. Версия, которую мы напишем здесь, не имеет ограничений: вызовы malloc и free могут выполняться в любом порядке: malloc делает запрос в операционную систему на выделение памяти тогда, когда она требуется. Эти программы иллюстрируют приемы, позволяющие получать машинно-зависимый код сравнительно машинно-независимым способом, и, кроме того, они могут служить примером применения таких средств языка, как структуры, объединения и typedef.

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

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

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

Существует проблема, о которой мы уже упоминали в главе 5, состоящая в том, что память, выдаваемая функцией malloc, должна быть соответствующим образом выровнена с учетом объектов, которые будут в ней храниться. Хотя машины и отличаются друг от друга, но для каждой из них существует тип, предъявляющий самые большие требования на выравнивание, и, если по некоему адресу допускается размещение объекта этого типа, то по нему можно разместить и объекты всех других типов. На некоторых машинах таким самым «требовательным» типом является double, на других это может быть int или long.

Илон Маск рекомендует:  Псевдоэлемент first-line в CSS

Свободный блок содержит указатель на следующий блок в списке, свой размер и собственно свободное пространство. Указатель и размер представляют собой управляющую информацию и образуют так называемый «заголовок». Чтобы упростить выравнивание, все блоки создаются кратными размеру заголовка, а заголовок соответствующим образом выравнивается. Этого можно достичь, сконструировав объединение, которое будет содержать соответствующую заголовку структуру и самый требовательный в отношении выравнивания тип. Для конкретности мы выбрали тип long.

Поле Align нигде не используется: оно необходимо только для того, чтобы каждый заголовок был выровнен по самому «худшему» варианту границы.

Затребованное число символов округляется в malloc до целого числа единиц памяти размером в заголовок (именно это число и записывается в поле size (размер) в заголовке); кроме того, в блок входит еще одна единица памяти — сам заголовок. Указатель, возвращаемый функцией malloc, указывает на свободное пространство, а не на заголовок. Со свободным пространством пользователь может делать что угодно, но, если он будет писать что-либо за его пределами, то, вероятно, список разрушится.

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

Для организации начала работы используется переменная base. Если freep есть NULL (как это бывает при первом обращении к malloc), создается «вырожденный» список свободного пространства; он содержит один блок нулевого размера с указателем на самого себя. Поиск свободного блока подходящего размера начинается с этого указателя (freep), т. е. с последнего найденного блока; такая стратегия помогает поддерживать список однородным. Если найденный блок окажется слишком большим, пользователю будет отдана его хвостовая часть; при этом потребуется только уточнить его размер в заголовке найденного свободного блока. В любом случае возвращаемый пользователю указатель является адресом свободного пространства, размещающегося в блоке непосредственно за заголовком.

Функция morecore получает память от операционной системы. Детали того, как это делается, могут не совпадать в различных системах. Так как запрос памяти у системы — сравнительно дорогая операция, мы бы не хотели для этого каждый раз обращаться к malloc. Поэтому используется функция morecore, которая запрашивает не менее NALLOC единиц памяти; этот больший кусок памяти будет «нарезаться» потом по мере надобности. После установки в поле размера соответствующего значения функция morecore вызывает функцию free и тем самым включает полученный кусок в список свободных областей памяти.

Системный вызов sbrk(n) в UNIXе возвращает указатель на n байт памяти или -1, если требуемого пространства не оказалось, хотя было бы лучше, если бы в последнем случае он возвращал NULL. Константу -1 необходимо привести к типу char *, чтобы ее можно было сравнить с возвращаемым значением. Это еще один пример того, как операция приведения типа делает функцию относительно независимой от конкретного представления указателей на различных машинах. Есть, однако, одна «некорректность», состоящая а том, что сравниваются указатели на различные блоки, выдаваемые функцией sbrk. Такое сравнение не гарантировано стандартом, который позволяет сравнивать указатели лишь в пределах одного и того же массива. Таким образом, эта версия malloc верна только на тех машинах, в которых допускается сравнение любых указателей.

В заключение рассмотрим функцию free. Она просматривает список свободной памяти, начиная с freep, чтобы подыскать место для вставляемого блока. Искомое место может оказаться или между блоками, или в начале списка, или в его конце. В любом случае, если подлежащий освобождению блок примыкает к соседнему блоку, он объединяется с ним в один блок. О чем еще осталось позаботиться, — так это о том, чтобы указатели указывали в нужные места и размеры блоков были правильными.

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

Упражнение 8.6. Стандартная функция calloc(n, size) возвращает указатель на n элементов памяти размера size, заполненных нулями. Напишите свой вариант calloc, пользуясь функцией malloc или модифицируя последнюю.

Упражнение 8.7. Функция malloc допускает любой размер, никак не проверяя его на правдоподобие: free предполагает, что размер освобождаемого блока — правильный. Усовершенствуйте эти программы таким образом, чтобы они более тщательно контролировали ошибки.

Упражнение 8.8. Напишите программу bfree(p, n), освобождающую произвольный блок p, состоящий из n символов, путем включения его в список свободной памяти, поддерживаемый функциями malloc и free. C помощью bfree пользователь должен иметь возможность в любое время добавить в список свободной памяти статический или внешний массив.

Closedir — Освободить дескриптор каталога

Есть проблема для систем реального времени. По некоторым причинам растет таблица дескрипторов, что очевидно видно в TaskMenager. При переполнении таблицы приложения и операционная система начинают глючить. Необходимо перезагружать все. Знает ли кто нибудь из мастеров способ очистки таблицы из проги от неиспользованных, и некорректно освобожденных дескрипторов. Заранее благодарен.

Про какую ОС идёт разговор?

mzu (19.07.03 11:27)
ОС указана в заголовке.

Не проще ли перестартовать прогу скажем в 24.00 ?

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

Помоему, StanislavB что путает, так как W2K/XP не являются системами реальног времени в принципе!

А убрать дескрипторы (при этом не о их ненужности можно толко догадываться) можно из Process Explorer от Sysinternals. Как он это делает, пока не знаю, но можно поискать в недокументированных API и NTDDK что-то на ту тему.

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

На этой «системе реального времени» обычно крутится постоянно одна прога — скорее всего написанная StanislavB, так нельзя ли проверить как эта прога освобождает дескрипторы

Можеть быть вопрос в стиле программирования: типа жизнь всех дескрипторов должна быть однозначно привязана к жизни объектов. Или к количеству instances этого объекта (Не знаю как по-русски сказать, извините).

А если не переписывать программу заново, то в MS Windows Resource Kit есть замечательная программулинка (oh.exe) которая
позволяет видеть список открытых дескрипторов любого процесса с указанием на что они ссылаются. Может это поможет найти место, где дескриптор создаётся и не удаляется.

Кстати, для уточнения. Под дескрипторами уважаемый StanislavB имеет в виду handles?

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

если не было вызова CloseHandle кто знает что дескриптор «неиспользуется» . на основании чего какаято ф-ия или утилита может сделать такой вывод ?

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

>StanislavB (26.07.03 20:12)

Увеличение количества дескрипторов — однозначно в ошибке в программе.
Недавно боролся с такой же проблемой при программировании сокетов в Socket API.

Дай более подробную информацию о коде.

>> Но бывают ситуации, когда CloseHandle не приводит к желаемому результату, и дескриптор не освобождается. Это особенно характерно для сетевых интерфейсов.
Это бывает только в том случае, если ему передают кривой дескриптор. А для сетевых интерфейсов существуют хорошие функции shutdown() и CloseSocket()

Я не понял, что такое кривой дескриптор. Я их не создаю. Например. Поставил компонент ClientSocket и делаю коннект по необходимому порту и хосту. Все в порядке. идет обмен информацией. Нет проблем. Таблица дескрипторов не растет. Но в конце рабочего дня. Клиент, который находится в другом конце города, выключил компьютер. Сокет периодически пытается наладить коннект и генерит сообщение об ошибке коннекта. Когда утром включается удаленный узел, сокет радостно сообщает об этом факте и передает этому узлу информацию. А за это время таблица дескрипторов увеличилась на несколько сотен или тысяч. Это один из примеров. Сейчас я делаю систему на основе WebAplication. Там начинаю сталкиваться с похожей проблемой. Системы должны работать круглосуточно и без перезагрузок многие месяцы, пока не получается.

неправильно обрабатываешь disconnect, там нужно освобождать handle

Для Polevi. Освобождать handle какого объекта? Я не встречал в литературе ни намека на необходимость этого действия, считая, что компонент сам делает все необходимое. Мною делались подобные попытки, но они ни к чему не приводили.

Ну так а что же ты тогда на «кривую систему» валишь, рассказываешь нам про какие-то переполненные таблицы дескрипторов, если ты на элементарном уровне не можешь отследить отсутствие клиента и освободить ресурсы?


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

В том то и дело, что я «на элементарном уровне» отслеживаю отсутствие «сервера», а не «клиента», мне в этом помогает компонент ClientSocket, но делать Disconnect не имеет смысла, так как Connecta то и не было. И освобождать ресурсы то же нечего. Открытого сокета не было. Очевидно, TCP/IP пытается найти серверный сокет и где то бродит, занимая дескриптор для не открытого сокета. Возможно так. Очевидно как то можно остановить этот процесс и осводить дескриптор. Тайм аут не помогает. Или я действительно ничего не понимаю. Завал.

2StanislavB (29.07.03 08:41)
ты исходный код TClientSocket смотрел ?
ты делаешь Socket.Close в обработчике OnError ?

продолжай в том же духе, пусть плодятся и множаться дескприпторы !

проверь при помощи oh.exe — какие дескрипторы не закрыты. В соответствии с этим и действовать

«ты делаешь Socket.Close в обработчике OnError ?»
Кажется делал. Точно не помню, но кажется нарывался на исключение. Попробую еще, имеет смысл. Сейчас не могу, надо ехать на объект в командировку. Если это то, что нужно, то я должник.
oh.exe не могу найти. Это интересно и для других целей. Например закрывать не нужные процессы.

Спасибо, нашел. Только что это за проекты «.ohp»?

Closedir — Освободить дескриптор каталога

Файлы и каталоги в php

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

Итак перед нами стоит задача:

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

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

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

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

Вот как я это сделал:

Давайте пошагово разберем код скрипта.
Сначала я описывю функцию read2Dir(), которая собственно и будет выполнять всю работу.

Она принимает следующие параметры:

$dir Директория, которую мы будем сканировать $offs Параметр который определяет вид отсупа при выводе данных в браузер

Затем с помощью условного оператора if (он просто записан немного по другому) проверяем содержится ли слэш (/) в конце директории.

И если он там есть, то оставляем все как есть, а в противном случае добавляем его в конец.

Сначала проверяем является ли директорией на самом деле, адрес по которму нужно произвести сканирование — функция is_dir().

И если это не так выходим из функции возвращая FALSE. Ну а если это все-таки каталог, то с помощью функции opendir() получаем дескриптор каталога для последующего использования, который в свою очередь помогает функции readdir() получить элемент каталога по этому самому дескриптору.

А получать элементы каталога мы будем в цикле whille до тех пор пока элемент каталога существуют — т.е. не тождественнен FALSE. Или говоря проще в цикле будем получать элементы пока они не закончаться.

Обратите внимание, что проверять нужно именно на тождественность.

В итоге должна получится примерно такая строка — (documents/file.txt), конечно если полученный элемент является файлом, ну а в противном случае полученный элемент это каталог и соответственно нам нужно снова выполнить его сканирование.

Для этого сначала функцией basename(), которая возвращает имя файла из указанного пути, проверяем не является ли полученный элемент » . » или » .. »
Если это так то пропускаем итерацию цикла whille используя оператор continue.

Далее определяем с помощью функции filetype() определяем тип файла.
Эта функция возвращает одно из возможных значений: fifo, char, dir, block, link, file или unknown.

Но в нашем конкретном случае нас интересует тольлько dir, и если это так то тут то и запускаем рекурсию.

То есть снова вызываем нашу функцию read2Dir(), только на этот раз передаем ей текущий элемент и пристыковываем (конкатенируем) к переменной $offset дополнительный отступ.

Ну а если полученный при чтении каталога элемент, то проверяем расширение файла. Сделать это проще всего проверив на соответствие искомому расширению три последние символа.

Получаем их используя функцию substr(), которая возвращает подстроку. Причем я указал, что меня интересуют именно последние 3 символа.

Ну а дальше совсем просто.
Если расширение соответствует назначенному нами, тогда с помощью функции filesize(), которая возвращает размер указанного файла в байтах или FALSE в случае возникновения ошибки, получаем размер файла и оператором echo выводим его в браузер.

И так снова, снова и снова, при необходимости опять рекурсивно вызывая нашу функцию read2Dir() работает цикл whille, пока не будет прочитан последний файл в заданной директории.

Поработали? Тогда не забываем освободить дескриптор каталога функцией closedir (), которая закрывает поток связанный с каталогом.

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

Экспериментируйте на здоровье!

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

На этом сегодня все. С удовольствием отвечу на ваши вопросы, если они у вас есть. До свидания.

Автор: Сергей Зарубин

Дата: 2012-03-03

Колличество просмотров: 6147

Комментарии к заметке:

Комментарий добавил(а): ivanscm
Дата: 2012-10-18

Комментарий добавил(а): mara
Дата: 2012-11-24

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