Как получить список всех процессов в 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.
Ваш комментарий