Выделение неуправляемой памяти в управляемом коде .NET - PullRequest
0 голосов
/ 23 января 2009

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

void managed_fx (byte data __gc[], size_t size)
{
    //  Pin the data
    byte __pin *pinned_data = &data [0];

    //  Copy data to the unmanaged buffer.
    void *unmanaged_data = malloc (size);
    memcpy (unmanaged_data, (byte*) pinned_data, size);

    //  Forward the call
    fx (unmanaged_data, size);
}

Ответы [ 5 ]

2 голосов
/ 23 января 2009

Мой MC ++ немного ржавый, но я думаю, что __pin закрепляет исходную переменную до тех пор, пока «pinned_data» не выйдет из области видимости (по крайней мере, это то же самое, что эквивалентный pin_ptr в C ++ / CLI). Как правило, вы должны откреплять его как можно скорее, то есть вы не должны вызывать fx в той же области видимости из соображений производительности.

Нужно ли вообще пиннинг в этом случае?

Да. Вы хотите получить доступ к управляемой памяти из неуправляемого кода.

Может ли gc освободить его перед memcpy

Нет. Существует сильная ссылка на данные, поэтому сборщик не будет их собирать. Однако он может собрать какой-то другой объект и переместить данные в память на этапе сжатия. Тогда malloc получит доступ к неправильной области памяти. Закрепление предотвращает это за счет дополнительной бухгалтерии для GC (вот почему вы должны откреплять объекты как можно скорее).

Распределяет ли malloc неуправляемую память, даже если она используется в управляемом коде?

Да.

0 голосов
/ 23 января 2009

Хорошо, давайте сделаем это еще проще:

void managed_fx (byte data __gc[])
{
     //  Pin the data
     byte __pin *pinned_data = &data [0];

     //  Copy data to the unmanaged buffer.
     void *unmanaged_data = malloc (data.Length);
     assert (unmanaged_data);
     memcpy (unmanaged_data, (byte*) pinned_data, data.Length);

     //  Forward the call
     fx (unamanged_data, data.Length);
 }

Тем не менее, у меня есть пара вопросов:

  1. Нужно ли вообще пиннинг в этом случае? Может ли gc освободить его до появления memcpy?
  2. Распределяет ли malloc неуправляемую память, хотя она используется в управляемом коде?
0 голосов
/ 23 января 2009

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

0 голосов
/ 23 января 2009

Вы

  1. при условии, что размер массива данных по крайней мере такой же большой. Это возможная ошибка в ожидании

  2. Не проверяется результат malloc ()

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

Имея это в виду, здесь исправлена ​​версия

void managed_fx (byte data __gc[], size_t size)
{
    if (data.Length < size)
        throw gcnew System.IndexOutOfRangeException(
            "the unmanaged buffer is not big enough");
    //  Pin the data
    byte __pin *pinned_data = &data [0];

    //  Copy data to the unmanaged buffer.
    void *unmanaged_data = malloc (size);
    if (unmanaged_data == NULL) 
        throw gcnew OutOfMemoryException(
            "could not allocate memory for the unmanaged buffer");
    memcpy (unmanaged_data, (byte*) pinned_data, size);

    //  Forward the call
    fx (unamanged_data, size);
}

Чтобы ответить на дополнительные вопросы:

Закрепление необходимо сделать memcpy. Он не предотвращает удаление памяти во время выполнения GC (ну, технически это делает, но делает просто удержание ссылки на него), он предотвращает ее перемещение в памяти. Поскольку memcpy - неуправляемая функция - она ​​не может обрабатывать указатели, которыми она манипулирует, перемещаясь в памяти. malloc полностью неуправляемый (хотя библиотеки времени выполнения могут свободно использовать зарезервированные области управляемой кучи для этого).

В исходном C ++ / CLI нет четкого различия относительно того, является ли «код» управляемым или неуправляемым. При компиляции некоторые части будут представлять собой промежуточный код CIL, другие - простой нативный код. Важно то, являются ли используемые переменные управляемыми или неуправляемыми. Управляемой ссылкой (что-либо с ^) можно манипулировать / использовать только тот код, который сам управляется. Это означает, что среда выполнения может свободно изменять значения используемых указателей по своему усмотрению, если она изменяет расположение управляемого объекта в памяти. Это делает это способом, безопасным для управляемых функций (они останавливаются, пока мир вокруг них меняется). Поскольку существует множество полезных функций, которые не знают, как обрабатывать такие управляемые ссылки, вам может понадобиться простой указатель на них. Так как эта операция небезопасна (если GC запускает указатель перемещения), существует указатель закрепления, чтобы сделать его безопасным и простым, поскольку он одновременно принимает указатель и в течение срока службы этого указателя предотвращает перемещение во время выполнения указателя на переменную .

0 голосов
/ 23 января 2009

вам нужно проверить ваш malloc на то, что он фактически выделил ваш указатель. Не знаю C ++ / CLI, поэтому я не знаю, будет ли работать ваш пин-код. Вам не нужно использовать библиотеку Marshalling, чтобы гарантировать, что память будет закреплена во время копирования?

...