Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Утечки памяти при монтировании сетевых дисков  (Прочитано 7277 раз)
0 Пользователей и 1 Гость смотрят эту тему.
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« : 15-03-2011 10:32 » 

Столкнулся с очень непонятной проблемой...
При работе написанного мной приложения (.NET C#) начинает течь память, но не в самом приложении, а в системном процессе csrss.exe (!)...
Приложение работает как сервис и постоянно опрашивает удаленные сервера, читает с них файлы следующим способом: монтирует, например, шару "C$" на свой сетевой диск, читает нужные файлы, затем отмонтирует. Все это работает в нескольких параллельных потоках (5 потоков), поскольку серверов много (около 150) и опрос идет каждые 5 минут.
Методом комментирования всего по порядку было выяснено, что память начинает течь именно в функции монтирования (или размонтирования) дисков.
После плясок с бубном и попыток обнаружить неосвобождаемые ресурсы я переписал эту функцию с использованием другого (другой библиотеки) способа - через прямой вызов WinAPI.
К сожалению, это не помогло и утекать оно не перестало.. Жаль Жаль  А черт его знает...
Привожу ниже оба примера для наглядности.
Первый - с использованием Windows Script Host (IWshRuntimeLibrary) - "вредный" вызов обозначил "// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
Код: (C++)
using System.IO;
using IWshRuntimeLibrary;

// ...

namespace Test1
{
class Test1
{

// ...

private static System.Collections.Generic.List<char> mountDrives=new List<char>(); // Will contain drive letters that mounted by this appication
private const string listMountLetters = "GHIJKLMNOPQRSTUVWXYZ"; // We can use these drives to mount on remote PCs

public char MountDrive(string server, string relpath, string user, string pass)
{
    logger.Debug("MountDrive start. server='" + server + "', path='" + relpath + "'");
    char driveLetter = ' ';
    const int MOUNTRETRYMAXCOUNT = 10; // Try some times (wait if anyone free drive letter)
    WshNetworkClass owNetwork = null;
    try
    {
        owNetwork = new WshNetworkClass();
        for (int retryc = 0; retryc < MOUNTRETRYMAXCOUNT; retryc++)
        {
            foreach (char curd in listMountLetters)
            {
                if (mountDrives.Contains(curd)) continue; // We're already using this drive
                try
                {
                    DriveType dt = DriveType.Unknown;
                    foreach (System.IO.DriveInfo di in System.IO.DriveInfo.GetDrives())
                    {
                        if (di.Name == curd + ":\\")
                        {
                            dt = di.DriveType;
                            break;
                        }
                    }
                    if (dt != DriveType.Unknown) continue;
                    driveLetter = curd;
                    WshNetworkClass owNetwork = new WshNetworkClass();
                    object obj1 = false;
                    object ouser = server + "\\" + user, opass = pass;
                    if (user.Contains("\\"))
                        ouser = user; // domain user - do not concatenate with server's name
                    owNetwork.MapNetworkDrive(curd + ":", "\\\\" + server + "\\" + relpath, ref obj1, ref ouser, ref opass); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                    mountDrives.Add(driveLetter);
                    break;
                }
                catch (System.Runtime.InteropServices.COMException cex)
                {
                    driveLetter = ' ';
                    switch ((uint)cex.ErrorCode)
                    {
                        case 0x800704B3:
                            logger.Error("Server '" + server + "' or its shared folder '" + relpath + "' is not available !");
                            return ' ';
                        case 0x8007052E:
                            logger.Error("User or password is wrong for server '" + server + "'!");
                            return ' ';
                        case 0x800704C3:
                            logger.Error("Multiple connections to a server '" + server + "' or shared resource by the same user!");
                            return ' ';
                        case 0x8007089A:
                            logger.Error("The specified username is invalid for server '" + server + "'!");
                            return ' ';
                        default:
                            //
                            break;
                    }
                    logger.Debug("MountDrive cex: " + cex.ToString());
                }
                catch (Exception ex)
                {
                    logger.Debug("MountDrive: Exception : " + ex.ToString());
                    driveLetter = ' ';
                    continue;
                }
            }
            if (driveLetter != ' ') break;
            else if (retryc < MOUNTRETRYMAXCOUNT - 1)
                System.Threading.Thread.Sleep(3000); // Sleep before next retry
        }
    }
    finally
    {
        if (owNetwork != null)
            if (System.Runtime.InteropServices.Marshal.IsComObject(owNetwork))
                System.Runtime.InteropServices.Marshal.ReleaseComObject(owNetwork);
    }
    logger.Debug("MountDrive end. driveLetter='" + driveLetter + "'");
    return driveLetter;
}

public bool UnMountDrive(char letter)
{
    logger.Debug("UnMountDrive start. letter='" + letter + "'");
    WshNetworkClass owNetwork = null;
    try
    {
        owNetwork = new WshNetworkClass();
        object obj1 = true, obj2 = false;
        owNetwork.RemoveNetworkDrive(letter + ":", ref obj1, ref obj2); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        mountDrives.Remove(letter);
    }
    catch (Exception ex)
    {
        logger.Error("UnMountDrive('" + letter + "'): Exception: " + ex.ToString());
    }
    finally
    {
        if (owNetwork != null)
            if (System.Runtime.InteropServices.Marshal.IsComObject(owNetwork))
                System.Runtime.InteropServices.Marshal.ReleaseComObject(owNetwork);
    }
    logger.Debug("UnMountDrive end");
    return true;
}

}
}

Второй - с использованием Win32API: WNetAddConnection2:

Код: (C++)
using System.IO;
using System.Runtime.InteropServices;

// ...

namespace Test1
{
class Test1
{

// ...

private static System.Collections.Generic.List<char> mountDrives=new List<char>(); // Will contain drive letters that mounted by this appication
private const string listMountLetters = "GHIJKLMNOPQRSTUVWXYZ"; // We can use these drives to mount on remote PCs
[StructLayout(LayoutKind.Sequential)]
public struct NETRESOURCEA
{
    public int dwScope;
    public int dwType;
    public int dwDisplayType;
    public int dwUsage;
    [MarshalAs(UnmanagedType.LPStr)]
    public string lpLocalName;
    [MarshalAs(UnmanagedType.LPStr)]
    public string lpRemoteName;
    [MarshalAs(UnmanagedType.LPStr)]
    public string lpComment;
    [MarshalAs(UnmanagedType.LPStr)]
    public string lpProvider;
}

[DllImport("mpr.dll", CharSet = CharSet.Auto)]
public static extern int WNetAddConnection2A(
    [MarshalAs(UnmanagedType.LPArray)] NETRESOURCEA[] lpNetResource,
    [MarshalAs(UnmanagedType.LPStr)] string lpPassword,
    [MarshalAs(UnmanagedType.LPStr)] string UserName,
    int dwFlags);
[DllImport("mpr.dll", CharSet = CharSet.Auto)]
public static extern int WNetCancelConnection2A(
    [MarshalAs(UnmanagedType.LPStr)] string lpName,
    int dwFlags,
    int fForce);

public char MountDrive(string server, string relpath, string user, string pass)
{
    logger.Debug("MountDrive start. server='" + server + "', path='" + relpath + "'");
    char driveLetter = ' ';
    const int MOUNTRETRYMAXCOUNT = 10; // Try some times (wait if anyone free drive letter)
    for (int retryc = 0; retryc < MOUNTRETRYMAXCOUNT; retryc++)
    {
        foreach (char curd in listMountLetters)
        {
            if (mountDrives.Contains(curd)) continue; // We're already using this drive
            try
            {
                NETRESOURCEA[] n = new NETRESOURCEA[1];
                n[0] = new NETRESOURCEA();
                n[0].dwType = 1;
                int dwFlags = 4;
                n[0].lpLocalName = curd + ":";
                n[0].lpRemoteName = "\\\\" + server + "\\" + relpath;
                n[0].lpProvider = null;
                int res = WNetAddConnection2A(n, pass, user, dwFlags);  // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                logger.Info("MountDrive, code=" + res.ToString());
                if (res != 0) throw new Exception("error mount, code=" + res.ToString());
                driveLetter = curd;
                mountDrives.Add(driveLetter);
                break;
            }
            catch (Exception mex)
            {
                logger.Debug("MountDrive: Exception : " + mex.ToString());
                driveLetter = ' ';
                continue;
            }
        }
        if (driveLetter != ' ') break;
        else if (retryc < MOUNTRETRYMAXCOUNT - 1)
            System.Threading.Thread.Sleep(3000); // Sleep before next retry
    }
    logger.Debug("MountDrive end. driveLetter='" + driveLetter + "'");
    return driveLetter;
}

public bool UnMountDrive(char letter)
{
    logger.Debug("UnMountDrive start. letter='" + letter + "'");
    try
    {
        int rv = WNetCancelConnection2A(letter + ":", 0, 1); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        mountDrives.Remove(letter);
    }
    catch (Exception ex)
    {
        logger.Error("UnMountDrive('" + letter + "'): Exception: " + ex.ToString());
    }
    logger.Debug("UnMountDrive end");
    return true;
}

Память утекает здорово - около 2 Gb (!) за 3 дня Жаль
Поскольку она течет в системном процессе - помогает только ребут всего сервера, что очень напрягает.

ЧТО я делаю неправильно? Что нужно освобождать еще?

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


Добавлено через 3 минуты и 8 секунд:
Вот скриншот для примера:

* csrss_leak.JPG (41.27 Кб - загружено 1201 раз.)
« Последнее редактирование: 15-03-2011 10:35 от baldr » Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
zubr
Гость
« Ответ #1 : 15-03-2011 11:30 » 

С параметром dwFlags играться пробовал? Попробуй его в 0 поставить.
Записан
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #2 : 15-03-2011 11:42 » 

С параметром dwFlags играться пробовал? Попробуй его в 0 поставить.
Бесперспективняк Жаль
Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
zubr
Гость
« Ответ #3 : 15-03-2011 11:50 » 

А что возвращает функция WNetCancelConnection2? Поставь лог на возврат этой фкнкции, возможно она не всегда срабатывает.
Записан
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #4 : 15-03-2011 11:57 » 

Если бы она не всегда срабатывала - буквы дисков бы оооочень быстро закончились.. Но оно работает довольно долго.
Для дебага я вызываю их очень часто - чтоб быстрее увидеть утечки.

Добавлено через 15 дней, 3 часа, 2 минуты и 36 секунд:
В общем, если кто-то столкнется с этим - решил это так...
Как выяснилось, утечки возникают только при монтировании дисков.
При использовании WNetAddConnection2A() просто для получения сетевого ресурса (вида "\\servername\c$") утечка, вроде, не наблюдается.
Переписал все приложение для использования UNC-путей вместо подмонтированных дисков и стал освобождать эти ресурсы только при выходе из приложения (для ускорения).
Вроде не течет пока Улыбаюсь
« Последнее редактирование: 30-03-2011 15:00 от baldr » Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines