Показаны сообщения с ярлыком Windows Server. Показать все сообщения
Показаны сообщения с ярлыком Windows Server. Показать все сообщения

воскресенье, 9 октября 2011 г.

WSRM и память с точки зрения админа

Всем доброе время суток. Недавно столкнулся с windows system resource manager. Не то чтобы я раньше с ним не сталкивался. Просто как-то не задумывался над этим. Это средство позволяет, кроме всего прочего, задать некий набор ограничений процессам в операционной системе. В частности, возможно ограничение рабочего набора (working set), определенного процесса. В рамках этой заметки я хотел бы разобраться с тем, как это происходит. Не скрою, что в самый первый момент, когда я увидел, как срабатывает этот механизм – я удивился. Меня сразу затерзали сомнения.
Но все же, начнем. С небольшого теста. Используем программу testlimit, которую почему-то не распространяют с sysinternals suite. Это, кстати, тоже удивило меня, когда я попытался ее найти. Сначала настроим WSRM для нашего теста. Для этого нужно создать фильтр для приложения, в нашем случае это testlimit. А за тем – resource allocation policy, в которой мы и определим ограничения для процесса. Делаем вот таким образом:
1. Создаем новый фильтр
mem1mem2
2. Задаем новую политику выделения ресурсов, в которой задаем размер рабочего набора в 300МБ
mem3mem4
3. Активируем новую политику
mem5

Теперь запускаем приложение
mem6

Прежде всего следующая командная строка: Testlimit.exe -d 10 -c 100, выделяет 1 Гб памяти, и коснется ее, заставив систему предоставить реальную оперативную память приложению. Подробности об этом процессе смотрите вот тут. Как видно из скриншота – это и происходит. Размер рабочего набора – 1Гб. Но это длится всего несколько секунд. Затем происходит “странное”:
mem7

Бабах! Размер рабочего набора уменьшился до 300 МБ, заданных в настройках WSRM. Возникает вопрос – куда делать память? А что если в ней были данные? И как приложения отреагируют на такое поведение? Не станут ли они вдруг падать?
Для того, чтобы ответить на эти вопросы я написал вот такой вот код. Не плюйтесь, я не разработчик а админ, посему код может быть и не красивый:
// memtest1.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <Windows.h>


int _tmain(int argc, _TCHAR* argv[])
{
    char *a = (char*) malloc(1024*1024*1024);
    for (int k = 0; k<=100; ++k)    {
        for (int i = 0; i<=1024*1024*1024-1; ++i){
            a[i] = 'a';}
        
        std::cout << "allocated" << k << std::endl;
        Sleep (1000);
    }
    int g;
    std::cin >> g;
    return 0;
}

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

Для выяснения этого эпизода воспользуемся приложением RAMMap. Запускаем его и, сразу после запуска приложения testlimit видим следующую картинку:

до урезания
mem8

после урезания
mem9

Видим список активных страниц, и его размер в 3.5 ГБ. И, собственно Private Bytes  - 2.2 ГБ. Через некоторое время, когда сработает WSRM видим совсем другую картину. Размер памяти в списке активных страниц уменьшился на примерно 700МБ, а объем памяти обнуленных страниц вырос на эту цифру.  Итого, память не провалилась и никуда не исчезла, система переместила страницы из списка в список, предоставив при этом возможность другим приложениям употребить эту память. Однако есть один интересный момент. Testlimit не использует память а просто выделяет ее. Что будет, если эта память будет активно использоваться? Вернемся к моему тестовому коду для этого, и посмотрим, что покажет RAMMap. А показывает он вот что:

до урезания
mem10

после урезания
mem11

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

mem13mem14

Сразу после урезания начинается подкачка. Таким образом, использование этого функционала, это не только возможность ограничить различные приложения размером рабочего набора, что может быть полезно, к примеру на терминальных серверах. Это еще и своего рода размен памяти на дисковые операции. Посему – есть смысл тщательно подумать над применением этого инструмента.

вторник, 4 октября 2011 г.

Получения списка выданных dhcp адресов скриптом

Всем привет. Недавно в форуме technet появился вопрос: как получить список выданных адресов и когда они были выданы. После короткого погружения в тему выяснилось, что используя стандартный API узнать это нельзя, поскольку сервер, похоже, таких данных не хранит, ну или не предоставляет. Коллега, в обсуждении, предложил парсить журнал сервера с тем чтобы достать из журнала нужные данные. Однако этот вариант требовал написания достаточно сложной для меня схемы, с отслеживание состояний и изменений, происходящих с адресом. С другой стороны, можно было использовать netsh для получения данных о выданных адресах, например вот так: netsh dhcp server scope 172.20.0.0 show clients 1. Однако этот способ не возвращает данных о дате выдачи, только о времени окончания. В итоге мы пришли к мнению, что проще использовать скрипт, и рассчитать нужную дату. Но при подробном изучении и этот способ оказался несколько более сложным, чем мне казалось на первый взгляд. Проблема в том, что я очень не люблю, наверное потому что не умею, парсить строки. Это вызывает у меня баттхёрт и головокружение. В итоге я воспользовался вот этим скриптом и слегка его изменил исключительно в целях пруф оф концепт:

$DHCP_EnumSubnetClients = @'          
[DllImport("dhcpsapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern uint DhcpEnumSubnetClients(
string ServerIpAddress,
uint SubnetAddress,
ref uint ResumeHandle,
uint PreferredMaximum,
out IntPtr ClientInfo,
ref uint ElementsRead,
ref uint ElementsTotal);
'@

$DHCP_Structs = @'
namespace mystruct {
using System;
using System.Runtime.InteropServices;

public struct CUSTOM_CLIENT_INFO
{
public string ClientName;
public string IpAddress;
public string MacAddress;
}

[StructLayout(LayoutKind.Sequential)]
public struct DHCP_CLIENT_INFO_ARRAY
{
public uint NumElements;
public IntPtr Clients;
}

[StructLayout(LayoutKind.Sequential)]
public struct DHCP_CLIENT_UID
{
public uint DataLength;
public IntPtr Data;
}

[StructLayout(LayoutKind.Sequential)]
public struct DATE_TIME
{
public uint dwLowDateTime;
public uint dwHighDateTime;

public DateTime Convert()
{
if (dwHighDateTime== 0 && dwLowDateTime == 0)
{
return DateTime.MinValue;
}
if (dwHighDateTime == int.MaxValue && dwLowDateTime == UInt32.MaxValue)
{
return DateTime.MaxValue;
}
return DateTime.FromFileTime((((long) dwHighDateTime) << 32) | (UInt32) dwLowDateTime);
}
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct DHCP_HOST_INFO
{
public uint IpAddress;
public string NetBiosName;
public string HostName;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct DHCP_CLIENT_INFO
{
public uint ClientIpAddress;
public uint SubnetMask;
public DHCP_CLIENT_UID ClientHardwareAddress;
[MarshalAs(UnmanagedType.LPWStr)]
public string ClientName;
[MarshalAs(UnmanagedType.LPWStr)]
public string ClientComment;
public DATE_TIME ClientLeaseExpires;
public DHCP_HOST_INFO OwnerHost;
}
}
'@

$resumeHandle = 0
$clientInfo = 0
$ElementsRead = 0
$ElementsTotal = 0

Add-Type $DHCP_Structs
Add-Type -MemberDefinition $DHCP_EnumSubnetClients -Name GetDHCPInfo -Namespace Win32DHCP

$DHCPServerIP = "172.20.10.10" # change this to match your DHCP server IP

[void][Win32DHCP.GetDHCPInfo]::DhcpEnumSubnetClients($DHCPServerIP,0,[ref]$resumeHandle,0,[ref]$clientInfo,[ref]$ElementsRead,[ref]$ElementsTotal)

$clients = [system.runtime.interopservices.marshal]::PtrToStructure($clientInfo,[mystruct.DHCP_CLIENT_INFO_ARRAY])

[int]$size = $clients.NumElements
[int]$current = $clients.Clients
$ptr_array = new-object system.intptr[]($size)
$current = new-object system.intptr($current)

for ($i=0;$i -lt $size;$i++)
{
$ptr_array[$i] = [system.runtime.interopservices.marshal]::ReadIntPtr($current)
$current = $current + [system.runtime.interopservices.marshal]::SizeOf([system.IntPtr])
}

function uIntToIP {
param ($intIP)
$objIP = new-object system.net.ipaddress($intIP)
$arrIP = $objIP.IPAddressToString.split(".")
return $arrIP[3] + "." + $arrIP[2] + "." + $arrIP[1] + "." + $arrIP[0]
}

[array]$clients_array = new-object mystruct.CUSTOM_CLIENT_INFO

for ($i=0;$i -lt $size;$i++)
{
$objDHCPInfo = New-Object psobject
$current_element = [system.runtime.interopservices.marshal]::PtrToStructure($ptr_array[$i],[mystruct.DHCP_CLIENT_INFO])
add-member -inputobject $objDHCPInfo -memberType noteproperty -name ClientIP -value $(uIntToIP $current_element.ClientIpAddress)
add-member -inputobject $objDHCPInfo -memberType noteproperty -name ClientName -value $current_element.ClientName
add-member -inputobject $objDHCPInfo -memberType noteproperty -name OwnerIP -value $(uIntToIP $current_element.Ownerhost.IpAddress)
add-member -inputobject $objDHCPInfo -memberType noteproperty -name OwnerName -value $current_element.Ownerhost.NetBiosName
add-member -inputobject $objDHCPInfo -memberType noteproperty -name SubnetMask -value $(uIntToIP $current_element.SubnetMask)
add-member -inputobject $objDHCPInfo -memberType noteproperty -name LeaseExpires -value $current_element.ClientLeaseExpires.Convert()
if (($current_element.ClientLeaseExpires.Convert()).Year -ne 9999)
{add-member -inputobject $objDHCPInfo -memberType noteproperty -name LeaseObtained -value ($current_element.ClientLeaseExpires.Convert()).AddDays(-8)}

$mac = [System.String]::Format(
"{0:x2}-{1:x2}-{2:x2}-{3:x2}-{4:x2}-{5:x2}",
[system.runtime.interopservices.marshal]::ReadByte($current_element.ClientHardwareAddress.Data),
[system.runtime.interopservices.marshal]::ReadByte($current_element.ClientHardwareAddress.Data, 1),
[system.runtime.interopservices.marshal]::ReadByte($current_element.ClientHardwareAddress.Data, 2),
[system.runtime.interopservices.marshal]::ReadByte($current_element.ClientHardwareAddress.Data, 3),
[system.runtime.interopservices.marshal]::ReadByte($current_element.ClientHardwareAddress.Data, 4),
[system.runtime.interopservices.marshal]::ReadByte($current_element.ClientHardwareAddress.Data, 5)
)

add-member -inputobject $objDHCPInfo -memberType noteproperty -name MacAddress -value $mac
$objDHCPInfo
}

По сути я вставил только одну строку:

if (($current_element.ClientLeaseExpires.Convert()).Year -ne 9999) 
{add-member -inputobject $objDHCPInfo -memberType noteproperty -name LeaseObtained -value ($current_element.ClientLeaseExpires.Convert()).AddDays(-8)}
Которая добавляет дополнительное поле в выводимый объект. Значение этого поля равно значению параметра LeaseExpires - 8 дней. Можно поправить как себе задумается. В принципе можно докрутить все это счастье и узнавать на сколько был выдан адрес из параметров скоупа. Но это уже совсем другая история.

понедельник, 3 октября 2011 г.

Конфигурирование single sign-on для RD Services

Итак. Задача, сделать так, чтобы при запуске клиентского подключения система не спрашивала имя и пароль. Для этого нужно создать GPO, с соответствующими настройками. Основная настройка:

Computer configuration\Policies\Administrative Templates\System\Credentials Delegation\Allow Delegating Default Credentials

Самый главный вопрос, что писать в настройках политики. Если внимательно изучить пояснение, то структура записи должна быть ясна. По сути вы должны указать SPN того сервера, которому вы собираетесь делегировать свои Credentials. Узнать этот самый SPN, можно командой setspn:


где, nyc-ts - это имя сервера терминалов, SPN которого необходимо узнать. Помеченные желтым записи - это SPN связанные со службой терминального сервера, то есть как раз те, которые нас интересуют. При этом нужно иметь ввиду, что, для корректной работы, при указании имени сервера в командной строке mstsc или в его окне, нужно писать имя так, как оно написано после слэша в строке SPN.
Как водится после создания готовую политику нужно назначить на соответствующие OU, для ускорения применения выполнить gpupdate /force. Кроме того в этой же политике можно сконфигурировать и дополнительные параметры для подключающихся клиентов.

Powershell 3

Свершилось! Группа разработки Powershell в своем блоге опубликовала, правда довольно давно, заметку о новом powershell 3 и о его возможностях. В принципе, все движется к тому, что управление новыми клинтскими и серверными ОС будет основано на нем. Особенно учитывая тот факт, что в сессии, посвященной введению в Windows Server 8, было сказано что основным типом инсталляции нового сервера будет Server Core. В общем, учим.