Как именно работают fopen(), fclose()?

Мне просто было интересно узнать о функциях fopen, fclose, socket и closesocket. Что именно происходит при вызове fopen или открытии сокета (особенно с точки зрения памяти)?

Может ли открытие файлов/сокетов без их закрытия вызывать утечку памяти?

И в-третьих, как создаются сокеты и как они выглядят с точки зрения памяти?

Меня также интересует роль операционной системы (Windows) в чтении сокетов и отправке данных.


person Fabian    schedule 26.02.2011    source источник
comment
Эти функции имеют тенденцию переходить в прерывания, которые перехватываются ядром.   -  person alternative    schedule 27.02.2011
comment
@mathepic: Вы имеете в виду системные вызовы, верно? Вызов ОС больше не так дорог, как обработка прерываний. Конечно, существуют физические аппаратные прерывания, когда сетевая карта получает данные, но они довольно далеки от перечисленных функций.   -  person Ben Voigt    schedule 27.02.2011


Ответы (2)


Отказ от ответственности: я в основном не имею права говорить об этом. Было бы здорово, если бы кто-то более знающий тоже написал.

Файлы

Детали реализации таких функций, как fopen(), во многом зависят от операционной системы (например, в UNIX также есть функция fopen()). Даже версии Windows могут сильно отличаться друг от друга.

Я дам вам свое представление о том, как это работает, но в основном это предположения.

  • При вызове fopen размещает объект FILE в куче. Обратите внимание, что данные в объекте FILE недокументированы — FILE — это непрозрачная структура, вы можете использовать только указатели на FILE. из вашего кода.
  • Объект FILE инициализируется. Например, что-то вроде fillLevel = 0, где fillLevel — это объем буферизованных данных, которые еще не были сброшены.
  • A call to the filesystem driver (FS driver) opens the file and provides a handle to it, which is put somewhere in the FILE struct.
    • To do this, the FS driver figures out the HDD address corresponding to the requested path, and internally remembers this HDD address, so it can later fulfill calls to fread etc.
      • The FS driver uses a sort of indexing table (stored on the HDD) to figure out the HDD address corresponding to the requested path. This will differ a lot depending on the filesystem type - FAT32, NTFS and so on.
      • Драйвер FS полагается на драйвер жесткого диска для выполнения фактического чтения и записи на жесткий диск.
  • Кэш может быть выделен в оперативной памяти для файла. Таким образом, если пользователь запрашивает чтение 1 байта, C++ может на всякий случай прочитать КБ, поэтому последующие операции чтения будут мгновенными.
  • Указатель на выделенный ФАЙЛ возвращается из fopen.

Если вы откроете файл и никогда не закроете его, некоторые вещи будут просачиваться, да. Будет утечка структуры FILE, утечка внутренних данных драйвера FS, утечка кеша (если он есть).

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

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

Розетки

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

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

Вкратце:

  • точно так же, как файл, сокет хранит некоторую информацию в ОЗУ, описывая вещи, относящиеся к его работе, такие как IP-адрес удаленного хоста.
  • он также может иметь кеши в ОЗУ по соображениям производительности.
  • он может удерживать ограниченные ресурсы ОС, такие как открытые порты, делая их недоступными для использования другими приложениями.

Все эти вещи будут протекать, если вы не закроете сокет.

Роль ОС в сокетах

ОС реализует стандарт TCP/IP, Ethernet и другие протоколы, необходимые для планирования/диспетчеризации/приема соединений и предоставления доступа к ним пользовательскому коду через API, например Berkeley Sockets.

ОС делегирует сетевой ввод-вывод (связь с сетевой картой) сетевому драйверу.

person Stefan Monov    schedule 27.02.2011
comment
Хороший ответ. Я бы просто добавил, что в большинстве современных ОС библиотека не вызывает драйвер напрямую, а через системный вызов. - person Chinmay Kanchi; 27.02.2011
comment
Я бы также добавил, что хотя невозможность закрыть файловые дескрипторы является своего рода утечкой памяти, вы также теряете ресурс, который, вероятно, будет более дефицитным, чем память, то есть количество возможных одновременно открытых файловых дескрипторов. . Так что ожидайте сбоя с EMFILE или чем-то еще задолго до того, как вам действительно удастся потратить впустую огромный объем памяти. - person Steve Jessop; 27.02.2011

С VS2017 в Windows 10 вы можете увидеть внутренний по стеку вызовов:

ntdll.dll!NtCreateFile()   Unknown
KernelBase.dll!CreateFileInternal() Unknown
KernelBase.dll!CreateFileW()   Unknown
ucrtbased.dll!create_file(const wchar_t * const path, _SECURITY_ATTRIBUTES * const security_attributes, const `anonymous-namespace'::file_options options) Line 234 C++
ucrtbased.dll!_wsopen_nolock(int * punlock_flag, int * pfh, const wchar_t * path, int oflag, int shflag, int pmode, int secure) Line 702    C++
ucrtbased.dll!_sopen_nolock(int * punlock_flag, int * pfh, const char * path, int oflag, int shflag, int pmode, int secure) Line 852    C++
ucrtbased.dll!__crt_char_traits<char>::tsopen_nolock<int * __ptr64,int * __ptr64,char const * __ptr64 const & __ptr64,int const & __ptr64,int,int const & __ptr64,int>(int * && <args_0>, int * && <args_1>, const char * const & <args_2>, const int & <args_3>, int && <args_4>, const int & <args_5>, int && <args_6>) Line 109  C++
ucrtbased.dll!common_sopen_dispatch<char>(const char * const path, const int oflag, const int shflag, const int pmode, int * const pfh, const int secure) Line 172  C++
ucrtbased.dll!_sopen_dispatch(const char * path, int oflag, int shflag, int pmode, int * pfh, int secure) Line 204  C++
ucrtbased.dll!_sopen_s(int * pfh, const char * path, int oflag, int shflag, int pmode) Line 895 C++
ucrtbased.dll!__crt_char_traits<char>::tsopen_s<int * __ptr64,char const * __ptr64 const & __ptr64,int const & __ptr64,int const & __ptr64,int>(int * && <args_0>, const char * const & <args_1>, const int & <args_2>, const int & <args_3>, int && <args_4>) Line 109 C++
ucrtbased.dll!common_openfile<char>(const char * const file_name, const char * const mode, const int share_flag, const __crt_stdio_stream stream) Line 38   C++
ucrtbased.dll!_openfile(const char * file_name, const char * mode, int share_flag, _iobuf * public_stream) Line 67  C++
ucrtbased.dll!__crt_char_traits<char>::open_file<char const * __ptr64 const & __ptr64,char const * __ptr64 const & __ptr64,int const & __ptr64,_iobuf * __ptr64>(const char * const & <args_0>, const char * const & <args_1>, const int & <args_2>, _iobuf * && <args_3>) Line 109 C++
ucrtbased.dll!common_fsopen<char>(const char * const file_name, const char * const mode, const int share_flag) Line 54  C++
ucrtbased.dll!fopen(const char * file, const char * mode) Line 104  C++

Большая часть кода находится в:

C:\Program Files (x86)\Windows Kits\10\Source\10.0.17763.0\ucrt\stdio\fopen.cpp
C:\Program Files (x86)\Windows Kits\10\Source\10.0.17763.0\ucrt\stdio\openfile.cpp
C:\Program Files (x86)\Windows Kits\10\Source\10.0.17763.0\ucrt\lowio\open.cpp

В _wsopen_nolock в open.cpp есть:

// Allocate the CRT file handle.  Note that if a handle is allocated, it is
// locked when it is returned by the allocation function.  It is our caller's
// responsibility to unlock the file handle (we do not unlock it before
// returning).
*pfh = _alloc_osfhnd();

Наконец, он вызывает Windows API CreateFileW, который вызывает скрытый API «NtCreateFile», код сборки которого:

NtCreateFile:
00007FFFD81A0120 mov         r10,rcx  
00007FFFD81A0123 mov         eax,55h  
00007FFFD81A0128 test        byte ptr[7FFE0308h],1  
00007FFFD81A0130 jne         NtCreateFile+15h(07FFFD81A0135h)
00007FFFD81A0132 syscall
00007FFFD81A0134 ret
00007FFFD81A0135 int         2Eh  
00007FFFD81A0137 ret
00007FFFD81A0138 nop         dword ptr[rax + rax]

Итак, наконец, он выполняет инструкцию системного вызова, которая входит в код ядра.

person jw_    schedule 25.04.2019