Ответ 1
На самом низком уровне (по крайней мере, для кода пользователя) вы будете использовать системные вызовы. На UNIX-подобных платформах они включают:
-
open
-
close
-
read
-
write
-
lseek
... и другие. Эти работы проходят мимо этих вещей, называемых файловыми дескрипторами. Файловые дескрипторы - это просто непрозрачные целые числа. Внутри операционной системы каждый процесс имеет таблицу дескриптора файла, содержащую все дескрипторы файла и соответствующую информацию, такую как файл, какой файл он и т.д.
Существуют также вызовы Windows API, похожие на системные вызовы в UNIX:
Windows проходит вокруг HANDLE
s, которые похожи на файловые дескрипторы, но, я считаю, немного менее гибкими. (например, в UNIX файловые дескрипторы могут не только представлять файлы, но также сокеты, трубы и т.д.)
Стандартные функции библиотеки C fopen
, fclose
, fread
, fwrite
и fseek
- это просто обертки вокруг этих системных вызовов.
Когда вы открываете файл, обычно содержимое содержимого не считывается в память. Когда вы используете fread
или read
, вы указываете операционной системе считывать определенное количество байтов в буфер. Это определенное количество байтов может быть, но не обязательно, длиной файла. Таким образом, при желании вы можете прочитать только часть файла в памяти.
Ответ на ninja-edit:
Вы спросили, как это работает на уровне машинного кода. Я могу только объяснить, как это работает в Linux и 32-битной архитектуре Intel. Когда вы используете системный вызов, некоторые аргументы помещаются в регистры. После того, как аргументы помещаются в регистры, прерывается 0x80
. Так, например, чтобы прочитать один килобайт от stdin
(дескриптор файла 0) до адреса 0xDEADBEEF
, вы можете использовать этот код сборки:
mov eax, 0x03 ; system call number (read = 0x03)
mov ebx, 0 ; file descriptor (stdin = 0)
mov ecx, 0xDEADBEEF ; buffer address
mov edx, 1024 ; number of bytes to read
int 0x80 ; Linux system call interrupt
int 0x80
вызывает прерывание программного обеспечения, которое обычно регистрирует операционная система в таблице векторов прерываний или таблице дескриптора прерывания. Во всяком случае, процессор перейдет в определенное место в памяти. Как только там, как правило, операционная система переходит в режим ядра (если необходимо), а затем выполняет эквивалент C switch
на eax
. Оттуда он перейдет в реализацию для read
. В read
он обычно считывает некоторые метаданные об дескрипторе из таблицы дескриптора файла вызывающего процесса. После того, как он имеет все необходимые ему данные, он делает свои вещи, а затем возвращается к пользовательскому коду.
Чтобы "сделать свой материал", допустим, что он читает с диска, а не через трубку или stdin
или какое-то другое нефизическое место. Пусть также предполагается, что он читается с основного жесткого диска. Кроме того, предположим, что операционная система все еще может обращаться к прерываниям BIOS.
Чтобы получить доступ к файлу, ему нужно сделать кучу файловой системы. Например, перемещая дерево каталогов, чтобы найти, где находится фактический файл. Я не собираюсь это делать, много, так как я уверен, вы можете догадаться.
Интересной частью является чтение данных с диска, будь то метаданные файловой системы, содержимое файла или что-то еще. Сначала вы получаете логический адрес блока (LBA). LBA - это всего лишь индекс блока данных на диске. Каждый блок обычно составляет 512 байтов (хотя этот показатель может быть датирован). Тем не менее, предполагая, что у нас есть доступ к BIOS, и его использует ОС, он затем преобразует LBA в нотацию CHS. Обозначение CHS (Cylinder-Head-Sector) - это еще один способ ссылки на части жесткого диска. Он привык к физическим концепциям, но в настоящее время он устарел, но почти каждый BIOS поддерживает его. Оттуда ОС будет загружать данные в регистры и прерывать прерывание 0x13
, прерывание чтения диска BIOS.
Это самый низкий уровень, который я могу объяснить, и я уверен, что часть после того, как я предположил, что используемая операционная система устарела. Все, что до этого, как все еще работает, я считаю, если не на упрощенном уровне.