Как пакет может снизить приоритет ТОЛЬКО для cmd, который его запускает?

494
cdlvcdlv

У меня есть пакет, который запускает серию команд, которые будут долго и интенсивно использовать процессор. Я не против, когда он будет закончен, поэтому я бы предпочел, чтобы он использовал только незанятые циклы процессора. Если я установлю приоритет процесса, который запускает пакет (некоторые cmd.exe) в том же пакете, любой дочерний процесс должен наследовать его приоритет. Я знаю, что вы можете изменить приоритет процесса, используяwmic . Например:

wmic process where name="cmd.exe" CALL setpriority "idle" 

действительно выполнит работу, НО это также снизит любой другой cmd.exeэкземпляр, который работает тогда, что не всегда может быть подходящим.

Есть ли способ определить, какой процесс выполняет пакет и установить ТОЛЬКО его приоритет idle?

Чтобы уточнить: я ищу общий метод, который вы можете включить в любую партию такого рода или партию, которую любой .batможет вызвать для снижения.

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

1

3 ответа на вопрос

2
TOOGAM

Один из подходов состоит в том, чтобы начать с поиска PID (сокращенного от «ID процесса»).

Роль процесса WMIC поддерживает не только установление приоритета. Вы также можете получить подробную информацию. например, следующее (запустить в одной строке):

WMIC process where name="cmd.exe" get Caption,CommandLine,Name,ParentProcessId,ProcessId /FORMAT:LIST

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

Однако у вас может быть несколько копий CMD.EXE, что может вызвать проблемы. Это может быть проще, если у вас есть некоторый контроль над тем, как запускается ваш пакетный файл. Вопрос Дина StackOverflow.com, «получение идентификатора процесса exe-файла, запущенного в Bat File», предоставляет несколько решений для получения PID в переменной среды. Я предлагаю вам взглянуть на эти решения, чтобы определить, какое из них выглядит наиболее приятным для вас, но вот один из возможных подходов:

C:\> wmic Process WHERE "CommandLine LIKE '%%MyProgWMICflag%%'" Get ParentProcessID /format:list

Это должно показать вам ParentProcessID для команды WMIC, который будет PID программы, которая называется WMIC (предположительно, ваша оболочка).

Это основано на ответе foxidrive на приведенной выше странице с гиперссылкой решений о получении PID. (Этот ответ также показывает получение этого в переменную окружения, если хотите.)

Затем включите ProcessId в ваше предложение WHERE. например:

WMIC process where "name='cmd.exe' AND ProcessId='12345'" CALL setpriority "idle"

Примечание: это только один из подходов. Там могут быть некоторые другие общие черты, которые вы считаете, лучше проверить. Вы можете увидеть список доступных свойств для проверки, используя что-то вроде:
WMIC /OUTPUT:"Results.txt" Process Get /FORMAT:LIST

Таким образом, все сводится к точному определению фактического PID `cmd.exe`. Это _almost_ ответ. Но сейчас я не могу придумать, как это сделать. Я отправлю это мышление на задний план. Возможно, появляется какая-то идея. cdlvcdlv 5 лет назад 0
Ну, вот почему я упомянул этот вопрос [вопрос Дина StackOverflow.com, «получение идентификатора процесса exe, работающего в Bat-файле»] (https://stackoverflow.com/questions/21618914/getting-process-id-of-exe-running -in-bat-file) предоставляет несколько решений. Я не увидел никакой пользы, пытаясь угадать, какой подход (упомянутый в этом ответе) мог бы быть наиболее удобным для вас, а затем попытался скопировать информацию из этого одного метода, оставив при этом детали других возможных решений. Я немного поиграл с этим и добавил немного информации (см. «Один из возможных подходов») TOOGAM 5 лет назад 0
Я нашел [этот вопрос] (https://stackoverflow.com/questions/7486717/finding-parent-process-id-on-windows) и работал над этим, но этот трюк %% MyProgWMICflag %% умен! cdlvcdlv 5 лет назад 0
0
user902300

вам нужно будет запустить пакет с помощью START (из другого пакета) - тогда запуск может иметь отдельный приоритет

START "" batch.bat /B /LOW 
Может быть, я не очень хорошо объяснил. Я знаю, что таким способом вы можете запускать процессы с низким приоритетом, но это заставило бы меня создать модуль запуска для каждого `.bat` такого рода, который я написал. То, что я искал, это общий метод, который вы можете включить в любую партию, подобную этой. Или партию, которую может вызвать любой `.bat`. cdlvcdlv 5 лет назад 0
@cdlvcdlv Меня рекомендовали с https://www.prnwatch.com/prio/ - возможно, это поможет вам user902300 5 лет назад 0
0
cdlvcdlv

Это работа, которую вы можете использовать от другого, чтобы установить приоритет вашей партии. Если вы скопируете его в свой каталог, вы можете вызывать его в любое время, когда вам это нужно, и т. Д. Как обычно. Благодаря TOOGAM, который дал мне инструменты и метод, чтобы написать это. Переменная гарантирует, что процесс точно определяет, какой именно вам нужен, а не какой-либо другой экземпляр вашей партии (маловероятный, но возможный сценарий).SetMyPrio.bat MyPrio.bat%PATH%UniqueString

@echo off  GOTO :EndOfReminder  <HELP>  USAGE:  From any batch file or command prompt, write CALL MyPrio [/?|/h] [<priority>|get] to set the priority of the caller cmd. Default for <priority> is idle.  /?, /h This help.  <priority> If you want your priority change to  idle -- no parameter, 64, idle or "idle" below normal -- 16384 or "below normal" normal -- 32, normal or "normal" above normal -- 32768 or "above normal" high priority -- 128 or "high priority" real time -- 256, realtime or "realtime" get Returns current priority as errorlevel  TO DO - Parameter error control. - Support for Unicode characters in help. </HELP>  :EndOfReminder setlocal set Priority=%~1  if /I ["%Priority%"]==["/h"] ( call :PrintHelp & exit /b ) if ["%Priority%"]==["/?"] ( call :PrintHelp & exit /b )  set UniqueString=%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM%  if ["%Priority%"]==["get"] for /f "usebackq tokens=2 delims==" %%a in (`wmic Process WHERE "name='cmd.exe' AND CommandLine LIKE '%%%UniqueString%%%'" Get Priority /format:LIST`) do exit /b %%a  if not defined Priority set Priority=idle for /f "usebackq tokens=2 delims==" %%a in (`wmic Process WHERE "name='cmd.exe' AND CommandLine LIKE '%%%UniqueString%%%'" Get ParentProcessID /format:LIST`) do set myPID=%%a wmic process where "ProcessId='%myPID%'" CALL setpriority "%Priority%" > nul 2>&1  endlocal goto :eof  :PrintHelp setlocal DisableDelayedExpansion call :eln 0 set "levelerror=^%%errorlevel^%%" for /F "delims=" %%a in ('find /v /n "" "%~f0"') do ( set "line=%%a"  REM We store errorlevel in tmpel because setlocal will set errorlevel to 0... call set tmpel=%levelerror% setlocal EnableDelayedExpansion REM ... and now we restore it REM %levelerror% was expanded to %errorlevel% before running the iteration REM and the CALL SET allows actual errorlevel be stored in tmpel REM Finally, we set errorlevel again call :eln !tmpel!  set "line=!line:*]=!"  if /i "!line!"=="</HELP>" exit /b 0 if errorlevel 1 echo(!line! if /i "!line!"=="<HELP>" call :eln 1 REM errorlevel is the only value that survives the following endlocal endlocal ) exit /b 1  :eln REM Sets errorlevel to %1 exit /b %1 

Изменить (2018-06-12)

  • Изменено название MyPrio, добавлены опции /?, /hи get.
  • Параметры /?и /hраспечатать текст, который вы пишете в напоминании между <HELP>и </HELP>. Этот метод можно использовать в любой другой партии, которая вам нужна.

Если вы делаете вызов MyPrio.batв начале пакета, в любое время, когда вы запускаете его, независимо от командной строки или перетаскивания файлов из Проводника Windows (которые становятся параметрами), каждая его часть (и дочерние процессы) будет работать с Ваш желаемый приоритет.

Например:

@echo off call MyPrio  REM Here comes a set of commands that will run with idle priority.  call MyPrio normal  REM Here may come some interactive part to request information needed to continue.  call MyPrio  REM Back to idle (or put any other priority you prefer).  call MyPrio normal  REM You may want to set the priority to normal at the end just in case you call the batch from a command line. 

КОММЕНТАРИИ

Я сделал пакет с именем PrioTest.batдля проверки MyPrio.batвсех возможных вариантов и заказал их, чтобы при любом вызове приоритет менялся. Я сделал подпрограмму (:test1 ), которая:

1. Получает текущий приоритет и сохраняет его в myPrioBefore.
2. Вызовы MyPrioс параметром теста.
3. Получает новый приоритет и сохраняет его в myPrioAfter.
4. Запускает скрытый ping -t localhost.
5. Получает приоритет и PID pingпроцесса и сохраняет их в pingPrioи pingPID.
6. Вывести значения myPrioBefore, вызов MyPrio, myPrioAfterи pingPrio.
7. Убивает начатого ping(будучи уверенным, что это не еще один).

@echo off  setlocal  set UniqueString=%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM% for /f "usebackq tokens=2 delims==" %%a in (`wmic Process WHERE "name='cmd.exe' AND CommandLine LIKE '%%%UniqueString%%%'" Get ParentProcessID /format:LIST`) do set myPID=%%a for %%a in (64 16384 32 32768 128 256 idle "below normal" none normal "idle" "normal" "above normal" realtime "high priority" "realtime") do call :test1 %%a CALL MyPrio normal  endlocal goto :eof  :test1 for /f "usebackq tokens=2 delims==" %%b in (`wmic Process WHERE "ProcessID='%%myPID%%'" Get Priority /format:LIST`) do set myPrioBefore=%%b if [%1]==[none] ( call MyPrio ) else ( call MyPrio %1 ) for /f "usebackq tokens=2 delims==" %%b in (`wmic Process WHERE "ProcessID='%%myPID%%'" Get Priority /format:LIST`) do set myPrioAfter=%%b start "" /b ping -t localhost > nul for /f "usebackq tokens=2 delims==" %%b in (`wmic Process WHERE "name='ping.exe' AND ParentProcessID='%%myPID%%'" Get Priority /format:LIST`) do set pingPrio=%%b for /f "usebackq tokens=2 delims==" %%b in (`wmic Process WHERE "name='ping.exe' AND ParentProcessID='%%myPID%%'" Get ProcessID /format:LIST`) do set pingPID=%%b echo myPrioBefore==%myPrioBefore% / CALL MyPrio %1 / myPrioAfter==%myPrioAfter% / pingPrio==%pingPrio% taskkill /f /pid %pingPID% > nul exit /b 

Это вывод партии тестеров:

myPrioBefore==4 / CALL MyPrio 64 / myPrioAfter==4 / pingPrio==4 myPrioBefore==4 / CALL MyPrio 16384 / myPrioAfter==6 / pingPrio==6 myPrioBefore==6 / CALL MyPrio 32 / myPrioAfter==8 / pingPrio==8 myPrioBefore==8 / CALL MyPrio 32768 / myPrioAfter==10 / pingPrio==8 myPrioBefore==10 / CALL MyPrio 128 / myPrioAfter==13 / pingPrio==8 myPrioBefore==13 / CALL MyPrio 256 / myPrioAfter==13 / pingPrio==8 myPrioBefore==13 / CALL MyPrio idle / myPrioAfter==4 / pingPrio==4 myPrioBefore==4 / CALL MyPrio "below normal" / myPrioAfter==6 / pingPrio==6 myPrioBefore==6 / CALL MyPrio none / myPrioAfter==4 / pingPrio==4 myPrioBefore==4 / CALL MyPrio normal / myPrioAfter==8 / pingPrio==8 myPrioBefore==8 / CALL MyPrio "idle" / myPrioAfter==4 / pingPrio==4 myPrioBefore==4 / CALL MyPrio "normal" / myPrioAfter==8 / pingPrio==8 myPrioBefore==8 / CALL MyPrio "above normal" / myPrioAfter==10 / pingPrio==8 myPrioBefore==10 / CALL MyPrio realtime / myPrioAfter==13 / pingPrio==8 myPrioBefore==13 / CALL MyPrio "high priority" / myPrioAfter==13 / pingPrio==8 myPrioBefore==13 / CALL MyPrio "realtime" / myPrioAfter==13 / pingPrio==8 

Как видите, myPrioAfterне соответствует запрошенному в вызове, а иногда приоритет pingне равен родительскому cmdзначению, поэтому значения должны быть больше (посмотрите на пятую строку: текущий приоритет равен 10, вы запрашиваете 128, новый приоритет - 13, а дочерний процесс - 8, извините?). Кроме того, realtimeкажется, не имеют эффекта (права администратора требуются, я предполагаю).