Nano Hash - криптовалюты, майнинг, программирование

Мьютекс С++ не блокируется между учетными записями Windows

Когда я запускаю свою программу на C++, мьютекс winAPI правильно блокируется, поэтому, если я пытаюсь запустить второй экземпляр, он проверяет, могу ли я открыть заблокированный мьютекс: OpenMutex(SYNCHRONIZE, FALSE, mutexName.c_str());

Я получаю ожидаемую ошибку: mutexName << " is already running on this machine! Aborting!";

Все идет нормально

Проблема в том, что у меня запущен 1 экземпляр программы и я переключаюсь на другую учетную запись Windows. Мьютекс должен быть заблокирован, но когда я запускаю второй экземпляр, он игнорирует, что мьютекс заблокирован, и все равно запускается!

поэтому мой вопрос для этой строки:

 m_hMutex = ::OpenMutex(SYNCHRONIZE, FALSE, mutexName.c_str());

Когда он запускается во второй раз, почему он устанавливает m_hMutex != NULL, когда второй экземпляр запускается с той же учетной записью Windows...

но устанавливает m_hMutex == NULL, когда второй экземпляр запускается под новой учетной записью?

В обоих случаях параметры функции абсолютно одинаковы, и этот код запускается на диске D:\

08.11.2018

  • Что вы подразумеваете под разными учетными записями Windows? Также я совершенно уверен, что мьютексы работают только с одним экземпляром программы. Вам понадобится другой механизм, чтобы делать то, что вы пытаетесь сделать. 08.11.2018
  • имя вашего мьютекса должно начинаться с префикса Global\, чтобы оно было одинаковым в разных сеансах 08.11.2018
  • То, что вы описываете, связано с быстрым переключением пользователей и выполняется с использованием сеансов служб терминалов. Вы изучали требования к именованию объектов ядра для этого? Я спрашиваю, потому что это конкретное обстоятельство указано в документы для OpenMutex 08.11.2018
  • @ShadowMitia, если вы используете функции мьютекса из WINAPI они могут охватывать несколько процессов/пользователей. 08.11.2018
  • @AlgirdasPreidžius Приятно знать, спасибо! 08.11.2018

Ответы:


1

Каждый пользователь, вошедший в систему, работает в своем собственном сеансе рабочего стола. Чтобы получить доступ к объекту ядра named, такому как мьютекс, между сеансами пользователя, вам необходимо добавить к имени объекта префикс пространства имен Global\, в противном случае вы в конечном итоге создадите отдельные объекты мьютекса локально в каждом сеансе пользователя. .

Согласно CreateMutex() и OpenMutex():

Имя может иметь префикс «Global» или «Local» для явного создания объекта в глобальном пространстве имен или пространстве имен сеанса. Остальная часть имени может содержать любой символ, кроме символа обратной косой черты. Дополнительные сведения см. в разделе Пространства имен объектов ядра. .

И в соответствии с пространствами имен объектов ядра:

Отдельные пространства имен клиентских сеансов позволяют нескольким клиентам запускать одни и те же приложения, не мешая друг другу. Для процессов, запущенных в рамках сеанса клиента, система по умолчанию использует пространство имен сеанса. Однако эти процессы могут использовать глобальное пространство имен, добавляя префикс "Global\" к имени объекта. Например, следующий код вызывает CreateEvent и создает объект события с именем CSAPP в глобальном пространстве имен:

CreateEvent( NULL, FALSE, FALSE, "Global\\CSAPP" );

...

Другое использование глобального пространства имен — приложения, использующие именованные объекты для обнаружения того, что экземпляр приложения уже запущен в системе во всех сеансах. Этот именованный объект должен быть создан или открыт в глобальном пространстве имен, а не в пространстве имен сеанса. Более распространенный случай запуска приложения один раз за сеанс поддерживается по умолчанию, поскольку именованный объект создается в пространстве имен для каждого сеанса.

Кроме того, вы должны использовать CreateMutex() вместо OpenMutex(), чтобы избежать состояния гонки, которое позволит другому экземпляру вашего приложения создать мьютекс до того, как текущий экземпляр сможет его создать. GetLastError() сообщит ERROR_ALREADY_EXISTS, если CreateMutex() успешно и мьютекс уже существует. Не вызывайте OpenMutex(), если только CreateMutex() не сообщит об ошибке ERROR_ACCESS_DENIED, согласно CreateMutex документация:

Если lpName соответствует имени существующего именованного объекта мьютекса, эта функция запрашивает право доступа MUTEX_ALL_ACCESS. В этом случае параметр bInitialOwner игнорируется, так как он уже задан процессом создания.

...

Если мьютекс является именованным мьютексом и объект существовал до вызова этой функции, возвращаемое значение является дескриптором существующего объекта, GetLastError возвращает ERROR_ALREADY_EXISTS, bInitialOwner игнорируется, а вызывающему потоку не предоставляется право собственности. Однако, если права доступа у вызывающей стороны ограничены, функция завершится с ошибкой ERROR_ACCESS_DENIED, и вызывающая сторона должна использовать функцию OpenMutex.

Попробуйте что-то вроде этого:

mutexName = "Global\\MyMutexName";

m_hMutex = ::CreateMutex(NULL, FALSE, mutexName.c_str());
if ((!m_hMutex) && (GetLastError() == ERROR_ACCESS_DENIED)) {
    m_hMutex = ::OpenMutex(SYNCHRONIZE, FALSE, mutexName.c_str());
}

if (!m_hMutex) {
    ... << mutexName << " cannot be accessed! Aborting!";
    //...
}
else if (GetLastError() == ERROR_ALREADY_EXISTS) {
    ... << mutexName << " is already running on this machine! Aborting!";
    //...
}
else {
    //...
}

Или, в Vista и более поздних версиях, вы можете использовать CreateMutexEx() вместо этого:

mutexName = "Global\\MyMutexName";

m_hMutex = ::CreateMutexEx(NULL, mutexName.c_str(), 0, SYNCHRONIZE);
if (!m_hMutex) {
    ... << mutexName << " cannot be accessed! Aborting!";
    //...
}
else if (GetLastError() == ERROR_ALREADY_EXISTS) {
    ... << mutexName << " is already running on this machine! Aborting!";
    //...
}
else {
    //...
}
08.11.2018
  • какой смысл отдельного вызова OpenMutex ? просто позвоните CreateMutexEx с доступом SYNCHRONIZE. также вместо мьютекса мы можем использовать, скажем, CreateEventEx, если нам нужен только объект, существующий не им 08.11.2018
  • если сказать, что CreateEventEx (CreateMutexEx) начинаются с Vista - ZwCreateEvent (NtCreateMutant) существуют даже в win2000 08.11.2018
  • @RbMm Я даже не знал, что CreateMutexEx() (или CreateEventEx()) вообще существует. 08.11.2018
  • если мы создаем какой-то объект только для предотвращения одновременного запуска нескольких экземпляров приложения - здесь подойдет любой почти именованный объект, а не только мьютекс. Потому что мы используем не свойства объекта, а только его имя. в данном случае самый лучший объект - самый маленький и самый простой (не знаю, почему на эту роль обычно выбирают мьютекс). на всякий случай можно использовать и нативный (даже задокументированный) API ZwCreateEvent. обычная история когда win32 оболочка менее функциональна по сравнению с родной. и только начиная с Vista добавлен дополнительный функционал с префиксом Ex 08.11.2018
  • @MichaelKlym, а откуда ты это знаешь? Если вы поставите перед именем мьютекса префикс Global, тогда он БУДЕТ использовать глобальное пространство имен, и это невозможно. Это означает, что ваш код отслеживания, вероятно, ошибочен. 12.11.2018
  • @RemyLebeau, как я могу увидеть, что этот процесс использует глобальное пространство имен, а не локальное? 12.11.2018
  • Взгляните на SysInternals Process Explorer. 13.11.2018
  • @МайклКлим нет. Это не должно быть ошибкой. В этом весь смысл. Открытие/создание именованного мьютекса, который уже существует, дает вам дескриптор этого мьютекса. Это позволяет нескольким процессам совместно использовать один и тот же мьютекс и синхронизироваться друг с другом. Но вас не интересует синхронизация, только существование. Итак, если вы открыли мьютекс и это удалось, мьютекс уже существует, поэтому выйдите из приложения. Если вы создаете мьютекс и это удается, вы должны проверить код ошибки, чтобы узнать, существует ли он уже или нет. Я показал это в своем ответе. 13.11.2018

  • 2

    Проблема в том, что Windows поддерживает отдельные пространства имен для различных объектов ядра. Объекты в одном пространстве имен могут иметь то же имя, что и объекты в другом пространстве имен, не мешая друг другу. Если вы хотите, чтобы объект был общим для всех сеансов, вы должны создать его в глобальном пространстве имен.

    Из MSDN

    Отдельные пространства имен клиентских сеансов позволяют нескольким клиентам запускать одни и те же приложения, не мешая друг другу. Для процессов, запущенных в рамках сеанса клиента, система по умолчанию использует пространство имен сеанса. Однако эти процессы могут использовать глобальное пространство имен, добавляя префикс «Global\» к имени объекта.

    Дополнительная литература: Пространства имен объектов ядра

    08.11.2018
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

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

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..