Как получить список всех процессов в Mac OS X
Привет, мир. Затеял писать одну утилиту. Нужно было получить список всех процессов в Mac OS X. Рабочий язык — Objective-C. Обнаружил, что не все так просто. Здесь приведу полученное решение и путь к нему.
До это Objective-C я видел дважды. На семинаре по iPhone SDK на втором HackDay и после, уже в самостоятельных поисках. Еще есть стойкое подозрение, что в Рунете вообще нет информации на эту тему. По крайней мере, я не нашел даже намека. Не судите строго.
Для начала надо понять, что подразумевается под процессом. Если вам нужно получить список запущенных приложений, надо смотреть в сторону метода GetNextProcess. Он вернет список всех запущенных приложений для Carbon, Cocoa и Classic. Но не вернет список демонов. Для православных братьев с Windows: не вернет службы.
Но мы же правильные парни. Правильные парни хотят получить список всех запущенных процессов, что для BSD системы выполнимо, если привлечь sysctl. Возвращаемая структура kinfo_proc будет содержать всю информацию. Например, идентификатор процесса хранится в kp_proc.p_pid, а его имя в kp_proc.p_comm. Для обращения к syscrl не нужны никакие особенные привилегии, то есть любой пользователь может получить список всех процессов в системе. Для поклонников UNIX way замечу, что можно использовать ps, а потом разбирать и интерпретировать то, что команда даст на выходе. Не думаю, что это будет эффективно. Но вдруг. О других способах ничего не знаю.
Прежде, чем начать, чуть-чуть о задействованных заголовочных файлах. Есть в стандартной библиотеке C макрос assert(). Этот макрос разворачивается в if. Если аргумент в assert принимает нулевое значение, то программа прерывается с помощью abort() и в stderr выводится сообщение. Файл errno.h нужен, так как в нем определена ENOMEM — недостаточно памяти. Из stdlib.h нам понадобятся malloc(), free() и abort(). Наконец, stdbool.h содержит несколько макросов для работы с bool.
Последнее отступление перед делом. Если вам нужна информацию о текущем процессе, то есть о себе, можно использовать такой вот код.
NSProcessInfo *processInfo = [NSProcessInfo processInfo]; NSString *processName = [processInfo processName]; int processID = [processInfo processIdentifier]; NSLog(@"Process Name: '%@' Process ID:'%d'", processName, processID);
Но если дело серьезнее, как описано, тогда надо быть замысловатее.
#import <assert.h> #import <errno.h>> #import <stdbool.h>> #import <stdlib.h>> #import <sys/sysctl.h>> #import <Foundation/Foundation.h>> typedef struct kinfo_proc kinfo_proc; static int GetBSDProcessList(kinfo_proc **processList, size_t *processCount) { kinfo_proc *buffer = NULL; size_t length; int sysctlResult; bool isDone = false; static const int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0}; assert(processList != NULL); assert(*processList == NULL); assert(processCount != NULL); *processCount = 0; do { assert(buffer == NULL); // Вызываем sysctl со значением буфера NULL length = 0; sysctlResult = sysctl((int *)name, (sizeof(name) / sizeof(*name)) - 1, NULL, &length, NULL, 0); if (sysctlResult == -1) { sysctlResult = errno; } // Выделение буфера соответствующего размера на основе результатов из предыдущего вызова if (sysctlResult == 0) { buffer = malloc(length); if (buffer == NULL) { sysctlResult = ENOMEM; } } // Вызываем sysctl с новым буфером. // Если получим ошибку ENOMEM, надо обнулить буфер и повторить все снова if (sysctlResult == 0) { sysctlResult = sysctl((int *) name, (sizeof(name) / sizeof(*name)) - 1, buffer, &length, NULL, 0); if (sysctlResult == -1) { sysctlResult = errno; } if (sysctlResult == 0) { isDone = true; } else if (sysctlResult == ENOMEM) { assert(buffer != NULL); free(buffer); buffer = NULL; sysctlResult = 0; } } } while (sysctlResult == 0 && !isDone); // Обнуление буфера if (sysctlResult != 0 && buffer != NULL) { free(buffer); buffer = NULL; } *processList = buffer; if (sysctlResult == 0) { *processCount = length / sizeof(kinfo_proc); } assert((sysctlResult == 0) == (*processList != NULL)); return sysctlResult; } int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; kinfo_proc *allProcesses = 0; size_t numberOfProcesses; GetBSDProcessList(&allProcesses, &numberOfProcesses); for(int i = 0; i < numberOfProcesses; i++ ) { NSLog(@"%d: %sn", allProcesses[i].kp_proc.p_pid, allProcesses[i].kp_proc.p_comm); } free(allProcesses); [pool drain]; return 0; }
Описанная функция вернет список всех процессов. Результат будет в processList, а общее количество процессов в processCount. В случае успеха функция вернет 0, в случае ошибки — номер ошибки для BSD. Память под список будет выделена внутри функции. Не забудьте ее освободить.
Начнем с вызова sysctl. Значение result — NULL. Значение length — 0. В случае успешного выполнения мы получим значение для length, а значит, можно выделить память под result. После этого повторим вызов sysctl с новым буфером. Если прошло успешно, цель достигнута. Если имеет место недостаток памяти (ENOMEM), придется обнулить result и повторить цикл. Обратите внимание, вызов sysctl снова будет с NULL. Это необходимо, потому что в случае ошибки ENOMEM значение length устанавливается равным количеству уже возвращенных данных, а не количеству данных, которые могли бы быть возвращены.
В main() можно видеть объявление указателя allProcesses на структуру, которая будет хранить данные о процессах. Количество процессов будет в переменной numberOfProcesses. Вывод информации в цикле. Не забудьте освободить память для allProcesses.
Ваш комментарий