Необработанное исключение Fortran (msvcr100d.dll)

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

Unhandled exception at 0x102fe274 (msvcr100d.dll) in Parameters.exe: 0xC0000005: Access violation reading location 0x00000005.

Отладчик останавливается в модуле с именем crtdll.c на этой строке:

onexitbegin_new = (_PVFV *) DecodePointer(__onexitbegin);

Верхняя строка в стеке вызовов гласит:

msvcr100d.dll!__clean_type_info_names_internal(__type_info_node * p_type_info_root_node=0x04a6506c) Строка 359 + 0x3 байта С++

Затем программа остается в памяти, пока я не закрою IDE.

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

SUBROUTINE READ_MMF ()
  USE IFWIN
  USE, INTRINSIC :: iso_c_binding
  USE, INTRINSIC :: iso_fortran_env 

  INTEGER(HANDLE) file_mapping_handle
  INTEGER(LPVOID) memory_location
  TYPE(C_PTR) memory_location_cptr
  INTEGER memory_size  
  INTEGER (HANDLE) file_map      
  CHARACTER(5)                   :: map_name 
  TYPE(C_PTR)                    :: cdata
  integer                        :: n = 3
  integer(4), POINTER            :: A, C
  real(8), POINTER               :: B

  TYPE STRUCT
    integer(4) :: A 
    real(8)    :: B
    integer(4) :: C
  END TYPE STRUCT
  TYPE(STRUCT), pointer :: STRUCT_PTR

  memory_size = 100000
  map_name = 'myMMF'

  file_map = CreateFileMapping(INVALID_HANDLE_VALUE,
 + NULL,
 + PAGE_READWRITE,
 + 0,
 + memory_size,
 + map_name // C_NULL_CHAR )  

  memory_location = MapViewOfFile(file_map,
 +IOR(FILE_MAP_WRITE, FILE_MAP_READ),
 + 0, 0, 0 )          

  cdata = TRANSFER(memory_location, memory_location_cptr)
  call c_f_pointer(cdata, STRUCT_PTR, [n]) 

  A => STRUCT_PTR%A
  B => STRUCT_PTR%B
  C => STRUCT_PTR%C

  RETURN
  END

Должен ли я освобождать c-указатели, когда закончу с ними? Я изучил это, но не вижу, как я это делаю на Фортране...

Спасибо за любую помощь!


person qu1ckdry    schedule 28.03.2013    source источник
comment
Было бы чище освободить память, но ОС сделает это за вас, когда программа выйдет. Fortran не может освободить память, выделенную в C... для этого вам понадобится процедура C. Кроме того, integer(4) и real(8) могут быть или не быть 4- и 8-байтовыми числами. Это типы 4 и 8, которые зависят от процессора (компилятора). См. stackoverflow.com/questions/ 3170239/ и stackoverflow.com/questions/10520819/what-does-real8-mean   -  person M. S. B.    schedule 28.03.2013
comment
P.S. Поскольку вы используете iso_fortran_env, у вас есть доступ к надежному и переносимому способу запроса чисел определенного размера. Для 4-байтовых целых чисел: integer (int32); для 8-байтовых вещественных чисел: real (real64). По крайней мере, если ваш компилятор имеет версию Fortran 2008 среды ISO Fortran Environment.   -  person M. S. B.    schedule 29.03.2013
comment
MSB, спасибо за ваш комментарий о размерах байтов. Я использую компилятор Intel, но я не знал, что могу использовать (int32) и т. д. для управления размером байта типа данных. Я буду использовать это в будущем.   -  person qu1ckdry    schedule 03.04.2013


Ответы (2)


Характер нарушения прав доступа (во время очистки библиотеки времени выполнения) предполагает, что ваша программа каким-то образом повреждает память. Существует ряд ошибок программирования, которые могут привести к этому, и ошибка или ошибки, ответственные за это, могут быть где угодно в вашей программе. Обычный подход «компиляция и запуск со всеми включенными параметрами диагностики и отладки» может помочь определить их.

Тем не менее, в показанном примере кода есть ошибка программирования. Процедура C_F_POINTER из встроенного модуля ISO_C_BINDING может работать либо со скалярными, либо с массивными указателями Fortran (второй аргумент). Если указатель Fortran является скаляром, то третий аргумент «форма» не должен присутствовать (он должен присутствовать, если указатель Fortran является массивом).

Ваш код нарушает это требование - указатель Фортрана STRUCT_PTR в вашем коде является скаляром, но вы предоставляете третий аргумент формы (как [n]). Вполне вероятно, что эта ошибка приведет к повреждению памяти — обычно реализация C_F_POINTER пытается заполнить дескриптор в памяти для указателя Fortran, а дескриптор указателя на массив может сильно отличаться от указателя на скаляр. .

Последующие ссылки на STRUCT_PTR могут усугубить искажение.

Хотя стандарт не требует диагностики этой ситуации, я немного удивлен, что компилятор не выдает диагностику (при условии, что ваш пример кода — это то, что вы на самом деле компилируете). Если бы вы сообщили об этом поставщику вашего компилятора (Intel, предположительно с учетом IFWIN и т. д.), я подозреваю, что они сочли бы это недостатком своего компилятора.

Чтобы освободить память, связанную с сопоставлением файлов, вы используете API UnmapViewOfFile и CloseHandle. Чтобы использовать их, вы должны «сохранить» (ваша программа должна каким-то образом запомнить) базовый адрес (memory_location, который также можно получить, вызвав C_LOC на STRUCT_PTR после устранения описанной выше проблемы), возвращенный MapViewOfFile, и дескриптор для сопоставление (file_map), возвращаемое функцией CreateFileMapping; соответственно.

person IanH    schedule 28.03.2013
comment
Ян, спасибо за подробный ответ - теперь я лучше понимаю, что происходит. В настоящее время я пытаюсь понять, как использовать упомянутые вами API UnmapViewOfFile и CloseHandle. Не могли бы вы уточнить, что вы подразумеваете под хранением данных? Я заметил, что освобождение представления (UnmapViewOfFile) приводит к тому, что переменные (a, b, c) становятся неопределенными. - person qu1ckdry; 03.04.2013

Я когда-либо делал это только с Cray Pointers: не с привязками ISO, и я знаю, что это работает с Cray Pointers.

Чего вы не говорите, так это того, происходит ли это в первый раз или во второй раз, когда вызывается подпрограмма. Если он вызывается более одного раза, то в коде есть проблема, заключающаяся в том, что Create/OpenFileMapping следует вызывать только один раз, чтобы получить дескриптор.

Вам не нужно освобождать память, потому что память не принадлежит вам для освобождения: вам нужно вызвать UnmapViewOfFile(memory_location). После того, как вы вызвали это, memory_location, memory_location_cptr и, возможно, cdata больше недействительны.

Это работает с двумя или более программами:

Одна программа вызывает CreateFileMapping, другие вызывают OpenFileMapping, чтобы получить дескриптор данных. Это нужно вызывать только один раз при запуске программы: не каждый раз, когда вам нужно получить доступ к файлу. Несколько вызовов Create/OpenFileMapping без соответствующего закрытия могут привести к сбоям.

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

Когда все сделано, вызовите close для дескриптора, созданного Create/OpenFileMapping.

person cup    schedule 29.03.2013