Social Icons

среда, 3 июля 2013 г.

Получение информации о системе в OSX и iOS с помощью Delphi (XE2, XE3, XE4) - Часть 1

В статье будут рассмотрены вопросы использования функций модулей sysctl, sysctlbyname и sysctlnametomib для получения информации о системе (параметры ядра, железа, сети, файловой системы, информации о компьютере и пользователе) в OSX и iOS.

sysctl
int sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
Модуль sysctl содержит функции для получения/установки системной информации; типы данных, возвращаемые функцией sysctl - целые числа (int32, int64), строки (AnsiString) и записи (records). Данная функция объявлена в модуле Posix.SysSysctl (SysSysctlAPI.inc).

Примечание: модуль Posix.SysSysctl - частичное портирование файла sysctl.h.
function sysctl(name: PInteger; namelen: cardinal; oldp: Pointer; oldlen: Psize_t; newp: Pointer; newlen: size_t): Integer; cdecl; external libc name _PU + 'sysctl';
name: указатель на Management Information Base (MIB), который "внутри" является массивом Integer. Каждый элемент массива должен быть заполнен значениями, связанными с какими-либо данными для чтения/записи. Количество элементов массива зависит от читаемой/записываемой информации, в большинстве случаев используется 2 первых значения: первый элемент отражает позицию данных в MIB (тип информации), второй содержит значение.

Существует несколько значений первого элемента в MIB (все они прописаны в модуле Posix.SysSysctl):
Идентификатор  Значение         Описание

CTL_DEBUG      $00000005        Debugging
CTL_VFS        $00000003        File system
CTL_HW         $00000006        Generic CPU, I/O
CTL_KERN       $00000001        High kernel limits
CTL_MACHDEP    $00000007        Machine dependent
CTL_NET        $00000004        Networking
CTL_USER       $00000008        User-level
CTL_VM         $00000002        Virtual memory 

Итак, если нужно получить информацию о ядре, необходимо заполнить массив следующим образом:
var
  mib : array[0..1] of Integer;
  ...
  ...
 mib[0] := CTL_KERN;

Второй элемент массива связан с первым, его возможные значения также определены в модуле Posix.SysSysctl. Так, например, для получения величины максимального количества поддерживаемых процессов необходимо использовать значение KERN_MAXPROC($00000006).
var
  mib : array[0..1] of Integer;
  ...
  ...
  mib[0] := CTL_KERN;
  mib[1] := KERN_MAXPROC;

Теперь рассмотрим остальные параметры функции sysctl.
namelen: размер записи mib.
oldp: указатель на получаемые данные типа Integer, int64, AnsiString (MarshaledAString) или записью.
oldlen: размер параметра oldp.
newp: указатель на записываемые данные. Если ничего не изменяется - необходимо передать nil.
newlen: размер параметра newp.

С учетом рассмотренного выше, приведем функцию для получения величины максимального количества поддерживаемых процессов:
function MaxProcesses : Integer;
var
  mib: array[0..1] of Integer;
  res: Integer;
  len: size_t;
begin
 
  mib[0] := CTL_KERN;
  mib[1] := KERN_MAXPROC;
 
  len := sizeof(Result);
  res := sysctl(@mib, Length(mib), @Result, @len, nil, 0);
  if res <> 0 then
   RaiseLastOSError;
end;
Получить Int64-значение можно аналогично, в качестве примера рассмотрим код для получения размера установленной памяти, использую идентификаторы CTL_HW и HW_MEMSIZE.
function MemSize : Int64;
var
  mib: array[0..1] of Integer;
  res: Integer;
  len: size_t;
begin
  mib[0] := CTL_HW;
  mib[1] := HW_MEMSIZE;
 
  len := sizeof(Result);
  res := sysctl(@mib, Length(mib), @Result, @len, nil, 0);
  if res <> 0 then
   RaiseLastOSError;
end;
Для получения строкового значения, в первую очередь необходимо передать длину предполагаемой строки для создания буфера, сделать это можно следующим образом (передав nil в качестве параметра oldp):
sysctl(@mib, Length(mib), nil, @len, nil, 0)
Следующий пример демонстрирует получения строки (ASCII), используя функцию sysctl.
function KernelVersion : AnsiString;
var
  mib: array[0..1] of Integer;
  res: Integer;
  len: size_t;
  p: MarshaledAString; // в XE2 можно использовать PAnsiChar 
begin
  mib[0] := CTL_KERN;
  mib[1] := KERN_VERSION;
  // получаем размер буфера
  res := sysctl(@mib, Length(mib), nil, @len, nil, 0);
  if res<>0 then
    RaiseLastOSError;
   
  // устанавливаем размер буфера
  GetMem(p, len);
  try
    res := sysctl(@mib, Length(mib), p, @len, nil, 0);
    if res <> 0 then
      RaiseLastOSError;
    Result := p;
  finally
    FreeMem(p);
  end;
end;
Наконец, функцию sysctl можно использовать для получения записей, передавая указатель на заранее подготовленный record.
Рассмотрим пример получения значений тактовой частоты из ядра.
procedure GetClockInfo;
type
 clockinfo = record
    hz      : Integer;
    tick    : Integer;
    tickadj : Integer;
    stathz  : Integer;
    profhz  : Integer;
  end;
 
(*
struct clockinfo {
    int hz;     
    int tick;      
    int tickadj;
    int stathz;   
    int profhz; 
};
*)
 
var
  mib: array[0..1] of Integer;
  res: Integer;
  len: size_t;
  clock: clockinfo;
begin
  FillChar(clock, sizeof(clock), 0);
  mib[0] := CTL_KERN;
  mib[1] := KERN_CLOCKRATE;
  len := sizeof(clock);
  res := sysctl(@mib, Length(mib), @clock, @len, nil, 0);
  if res <> 0 then
    RaiseLastOSError;
  
  Writeln(Format('clock frequency             %d',[clock.hz]));
  Writeln(Format('micro-seconds per hz tick   %d',[clock.tick]));
  Writeln(Format('clock skew rate for adjtime %d',[clock.tickadj]));
  Writeln(Format('statistics clock frequency  %d',[clock.stathz]));
  Writeln(Format('profiling clock frequency   %d',[clock.profhz]));
end;
sysctlbyname
int sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
Функция sysctlbyname работает по тому же принципу, что и sysctl, однако получение значений осуществляется путем передачи специального строкового идентификатора. Это в свою очередь позволяет избежать передачи в функцию mib-записи и ее размера.
function sysctlbyname(Name: MarshaledAString; oldp: Pointer; oldlen: Psize_t; newp: Pointer; newlen: size_t): Integer; cdecl; external libc name _PU + 'sysctlbyname';
name: идентификатор, определяющий возвращаемое значение и состоящий из двух "уровней".
В качестве элементов "первого" уровня, могут использоваться следующие идентификаторы:
Идентификатор  Строка

CTL_DEBUG      debug
CTL_VFS        vfs
CTL_HW         hw
CTL_KERN       kern
CTL_MACHDEP    machdep
CTL_NET        net
CTL_USER       user
CTL_VM         vm
Приведенные идентификаторы также необходимо дополнить идентификатором "второго" уровня, конкретизирующим информацию, которую нужно получить/записать.

В качестве примера рассмотрим следующие возможные значения, которые могут использоваться в качестве идентификатора name:
Идентификатор                   Тип 
         
kern.ostype                     string      
kern.osrelease                  string      
kern.osrevision                 integer     
kern.version                    string      
kern.maxvnodes                  integer      
kern.maxproc                    integer      
kern.maxfiles                   integer      
kern.argmax                     integer     
kern.securelevel                integer      
kern.hostname                   string       
kern.hostid                     integer      
kern.clockrate                  struct      
kern.posix1version              integer     
kern.ngroups                    integer     
kern.job_control                integer     
kern.saved_ids                  integer     
kern.link_max                   integer     
kern.max_canon                  integer     
kern.max_input                  integer     
kern.name_max                   integer     
kern.path_max                   integer     
kern.pipe_buf                   integer     
kern.chown_restricted           integer     
kern.no_trunc                   integer     
kern.vdisable                   integer     
kern.boottime                   struct      
vm.loadavg                      struct      
vm.swapusage                    struct      
machdep.console_device          dev_t       
net.inet.ip.forwarding          integer      
net.inet.ip.redirect            integer      
net.inet.ip.ttl                 integer      
net.inet.icmp.maskrepl          integer     
net.inet.udp.checksum           integer      
hw.machine                      string      
hw.model                        string      
hw.ncpu                         integer     
hw.byteorder                    integer     
hw.physmem                      integer     
hw.usermem                      integer     
hw.memsize                      integer     
hw.pagesize                     integer     
user.cs_path                    string      
user.bc_base_max                integer     
user.bc_dim_max                 integer     
user.bc_scale_max               integer     
user.bc_string_max              integer     
user.coll_weights_max           integer     
user.expr_nest_max              integer     
user.line_max                   integer     
user.re_dup_max                 integer     
user.posix2_version             integer     
user.posix2_c_bind              integer     
user.posix2_c_dev               integer     
user.posix2_char_term           integer     
user.posix2_fort_dev            integer     
user.posix2_fort_run            integer     
user.posix2_localedef           integer     
user.posix2_sw_dev              integer     
user.posix2_upe                 integer    
Примечание: получить полный список поддерживаемых команд можно путем ввода sysctl -A в терминале операционной системы.

Приведенный ниже код показывает, как использовать SysCtlByName для получения количества установленных процессоров.
function NumberOfCPU: Integer;
var
  res: Integer;
  len: size_t;
begin
  len := SizeOf(Result);
  res := SysCtlByName('hw.ncpu', @Result, @len, nil, 0);
  if res <> 0 then
    RaiseLastOSError;
end;
Примечание: функция sysctl выполняется примерно в 3 раза быстрее, чем sysctlbyname, поэтому, если производительность является ключевым аспектом - лучше использовать sysctl.

sysctlnametomib
int sysctlnametomib(const char *name, int *mibp, size_t *sizep);
Функция sysctlnametomib заполняет mib по строковому идентификатору. Обычно, данная функция используется в приложениях, часто работающих с одними и теми же значениями.
function sysctlnametomib(name: MarshaledAString; mibp: PInteger; sizep: Psize_t): Integer; cdecl; external libc name _PU + 'sysctlnametomib';
name: строковый идентификатор значения (см. предыдущий пункт).
mibp: указатель на mib-запись.
sizep: указатель на длину mib-записи.
var
  mib : array[0..1] of Integer;
  res : Integer;
  len : size_t;
begin
  len := Length(mib);
  sysctlnametomib('hw.physicalcpu', @mib, @len);
  // теперь mib-запись правильно заполнена и готова для передачи в функцию sysctl 
Error Handling
Все указанные выше функции возвращают 0, если выполнились успешно, и код ошибки в противном случае. Такой код может быть получен через Posix.Errno или с использованием функции GetLastError.

Возможные коды ошибок могут быть найдены в файле C:\Program Files (x86)\Embarcadero\RAD Studio\{ВЕРСИЯ}\source\rtl\posix\osx\ErrnoTypes.inc (при установке IDE по умолчанию).

И наконец, итоговый пример, задействующий все положения настоящей статьи:
{$APPTYPE CONSOLE}
 
uses
  //System.Classes,
  //System.Types,
  //Posix.Errno,
  Posix.SysTypes,
  Posix.SysSysctl,
  System.SysUtils;
 
//https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/sysctl.3.html
//https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man8/sysctl.8.html
 
 
function NumberOfCPU: Integer;
var
  res: Integer;
  len: size_t;
begin
  len := SizeOf(Result);
  res := SysCtlByName('hw.ncpu', @Result, @len, nil, 0);
  if res <> 0 then
    RaiseLastOSError;
end;
 
 
function MaxProcesses: Integer;
var
  mib: array[0..1] of Integer;
  res: Integer;
  len: size_t;
begin
 
 mib[0] := CTL_KERN;
 mib[1] := KERN_MAXPROC;
 
 len := sizeof(Result);
 res := sysctl(@mib, Length(mib), @Result, @len, nil, 0);
 if res <> 0 then
   RaiseLastOSError;
end;
 
function MemSize : Int64;
var
  mib: array[0..1] of Integer;
  res: Integer;
  len: size_t;
begin
 mib[0] := CTL_HW;
 mib[1] := HW_MEMSIZE;
 
 len := sizeof(Result);
 res := sysctl(@mib, Length(mib), @Result, @len, nil, 0);
 if res <> 0 then
   RaiseLastOSError;
end; 
 
function KernelVersion : AnsiString;
var
  mib: array[0..1] of Integer;
  res: Integer;
  len: size_t;
  p: MarshaledAString; // в XE2 используйте PAnsiChar
begin
  mib[0] := CTL_KERN;
  mib[1] := KERN_VERSION;
  res := sysctl(@mib, Length(mib), nil, @len, nil, 0);
  if res <> 0 then
    RaiseLastOSError;

  GetMem(p, len);
  try
    res := sysctl(@mib, Length(mib), p, @len, nil, 0);
    if res <> 0 then
      RaiseLastOSError;
    Result := p;
  finally
    FreeMem(p);
  end;
end;
 
procedure GetClockInfo;
type
  clockinfo = record
    hz      : Integer;
    tick    : Integer;
    tickadj : Integer;
    stathz  : Integer;
    profhz  : Integer;
  end;
 
var
  mib: array[0..1] of Integer;
  res: Integer;
  len: size_t;
  clock: clockinfo;
begin
  FillChar(clock, sizeof(clock), 0);
  mib[0] := CTL_KERN;
  mib[1] := KERN_CLOCKRATE;
  len := sizeof(clock);
  res := sysctl(@mib, Length(mib), @clock, @len, nil, 0);
  if res <> 0 then
    RaiseLastOSError;
 
  Writeln(Format('clock frequency             %d',[clock.hz]));
  Writeln(Format('micro-seconds per hz tick   %d',[clock.tick]));
  Writeln(Format('clock skew rate for adjtime %d',[clock.tickadj]));
  Writeln(Format('statistics clock frequency  %d',[clock.stathz]));
  Writeln(Format('profiling clock frequency   %d',[clock.profhz]));
end;
 
begin
  try
    Writeln(Format('max processes     %d',[MaxProcesses]));
    Writeln(Format('number of cpus    %d',[NumberOfCPU]));
    Writeln(Format('physical ram size %s',[FormatFloat('#,', MemSize)]));
    Writeln(Format('Kernel Version    %s',[KernelVersion]));
    GetClockInfo;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Источник: http://theroadtodelphi.wordpress.com/2013/05/31/getting-system-information-in-osx-and-ios-using-delphi-xe2-xe3-xe4-part-1/

Комментариев нет:

Отправить комментарий

Поделитесь с друзьями!

 

Подписчики

Статистика