...someplace, where there isn't any trouble? Do you suppose there is such a place, Toto?

64 bit Windows - Teil 3

So, wir wissen nun nicht nur über das Schisma zwischen AMD64 und IA64 was den Bau von Prozessoren angeht, sondern wir wissen auch, daß es in Form des W2K3 Server SP1 PlatSDK auch frei erhältlich geeignete Entwicklungstools gibt, mit denen man zumindest mal alles für die x64 Plattform übersetzen kann, was auf makefiles basiert. Aber natürlich ist es ein Ding der Unmöglichkeit, sorgsam gepflegte dsw und dsp files von VC6 oder ihre Äquivalente in den neueren Entwicklungsumgebungen gegen makefiles einzutauschen. Glücklicherweise gibt es aber hier Abhilfe in Form eines kaum bekannten Kommandozeilenparameters von Visual Studio, dem switch /useenv. Wird msdev mit diesem Switch gestartet, so übernimmt es für alle Pfade, die unter "Tools"-"Options"-"Directories" angezeigt werden, nicht diejenigen, die es in der Registry findet unter HKEY_CURRENT_USER\Software\Microsoft\Devstudio\6.0\Build System\Components\Platforms\Win32 (x86)\Directories, sondern befüllt diese Werte mit den Werten aus den Environmentvariablen %INCLUDE%, %LIB% usw.

Das bedeutet, um mit Visual Studio 6.0 komfortabel ein x64 Binary erstellen zu können, muss man eigentlich nur aus einer der Buildkonsolen des PlatSDKs folgendes aufrufen:

msdev /useenv

denn die Buildkonsolen des PlatSDK bringen die korrekten Environmentvariablen für das jeweilige Buildtarget mit. Diese Konsolen rufen beim Start zu diesem Zweck die Batchdatei setenv.cmd in der Root der PlatSDK-Installation mit entsprechenden Parametern für das jeweilige Buildtarget auf. Um mir selbst die Sache zu vereinfachen/vereinheitlichen und um auf jedem meiner Rechner (wo überall das PlatSDK irgendwo anders im Filesystem liegt) den Start von msdev für x64 als Target zu gestatten, habe ich meine PlatSDK-Installation jeweils für "Authenticated Users" als Share "platsdk$" freigegeben. Die Datei msdev.exe ist eh auf jeder meiner Maschinen über den Suchpfad startbar, sodass ich zum Starten einer x64 Entwicklungsumgebung nur ein einheitliches batch file aufrufe (das natürlich auch in mein cvs eingecheckt ist) mit folgendem Inhalt:

call \\127.0.0.1\platsdk$\setenv.cmd /X64 /DEBUG
start msdev /useenv

Und voilá, ich kann aus dieser VC6-Devstudio Instanz für x64 builden. Voraussetzung ist allerdings für das Ganze, daß man ein aktuelles Service Pack für VC6 installiert hat (out-of-the-box mit VC6 Golden Code geht das nicht) und man muss schon mindestens einmal seit der VC6-Installation auch den msdev gestartet haben (lazy programming bei MS?).

So, wir können jetzt also schon mal builden aus Devstudio heraus, aber sobald man das für ein bestehendes x86-Projekt tut, hagelt es natürlich Fehlermeldungen von Compiler und Linker wie nix Gutes. Was man zu tun hat um zu einem sauberen x64 Build zu kommen, ausgehend von einem funktionierenden x86-Build, darüber schreibe ich dann das nächste Mal.

64 bit Windows - Teil 2

So, nun wissen wir also, wie es dazu kam, daß es zwei 64-bit Versionen von Windows gibt, wovon aber nur die x64 Editionen wirklich relevant sind. Heute will ich etwas über Entwicklungsplattformen für die Windows x64 Editions erzählen, denn das ist ja für einen Softwareentwickler eines der spannendsten Themen.

Die schlechte Nachricht bei diesem Thema ist, daß man bis dato keine Entwicklungsumgebung für x64 Windows kaufen kann. Die gute Nachricht ist die, daß man sich trotzdem helfen kann. Man muß sich eben schlimmstenfalls mit ein paar unschönen Kleinigkeiten arrangieren und dann geht das schon. Die einfachste Möglichkeit besteht sicherlich darin, die Beta 2 von Visual Studio 2005 zu installieren (sollte man auch aus anderen Gründen tun, Stichwort: prefast). Visual Studio 2005 hat eben schon den Support für x64 out-of-the-box, ist aber eben noch kein releastes und stabiles Produkt. Zudem musste man bei der Installation mit an Sicherheit grenzender Wahrscheinlichkeit eine EULA abnicken, nach der man keine Programme mit dieser Beta erstellen darf, die man dann auch verkauft, pardon: Deren Nutzungsrechte man an seine Kunden lizensiert. Für kommerzielle Software ist also Visual Studio 2005 noch nicht geeignet.

Der geneigte Leser mag sich nun fragen: "Ja Mensch, wie zur Hölle hat denn dann MS seinen notepad.exe und seinen IE für die x64 Editions gebuildet, wenn das alles noch im Betastadium ist?". Die Antwort findet man im Platform SDK. Ziemlich zeitgleich mit dem Release der XP x64 Edition wurde am 2. Mai 2005 das "Windows 2003 Server SP1 Platform SDK" releast. Dieses PlatSDK hat zum ersten Mal offiziellen Support für die Anwendungsentwicklung für die x64 Editions von Windows, es kommt also mit allen erforderlichen headern und libs für x64 Windows. Zusätzlich bringt es einen Compiler mit für x64 und sogar eine geeignete MFC Version für x64. Ich kann mich nicht erinnern, daß bereits früher einmal ein Platform SDK erhätlich gewesen wäre, das für ein offiziell verfügbares Betriebssystem die MFC Libs und Sourcen mitgeshippt hätte und dazu noch einen passenden Compiler! Vermutlich hat man bei MS das Ganze deswegen in diesem PlatSDK mitgeshippt, um der x64-Plattform auch ohne ein bereits verfügbares Visual Studio 2005 den erforderlichen Schub zu geben.

Wie auch immer, dieses PlatSDK bringt alles mit, was man braucht, um native x64 Binaries zu erzeugen. Wenn man dieses PlatSDK also installiert, erhält man den Eintrag "Microsoft Platform SDK for Windows Server 2003 SP1" im Startmenü, dazu ein Untermenüpunkt "Open Build Environment Window", der Links zu 5 möglichen Konsolen (also cmd.exe) enthält, die mit den passenden Umgebungsvariablen gestartet werden um all die Samples, die das PlatSDK mitbringt, per nmake builden zu können. Aber hey, sind makefile-builds nicht etwas für Masochisten? Was machen denn all die Leute, die ihre Softwareprojekte in dsp oder sln files organisiert haben? Müssen die jetzt alle ihre Projekte in makefiles exportieren oder gar ihre makefiles selber schreiben? Die Antwort auf diese Fragen gebe ich im nächsten Posting.

64 bit Windows - Teil 1

Irgendwie habe ich in Mission Control bei bestimmten Themen immer wieder den Eindruck, als müßte ich Dinge doppelt und dreifach erklären, weil immer wieder verschiedene Leute vorbeischauen, aber alle dieselben Fragen stellen. Und 64 bit Windows ist so ein Thema, weshalb ich mir vorgenommen habe, eine Serie von Postings zu diesem Thema zu schreiben. Dies ist also Teil 1 und ich habe noch keine Ahnung wieviele Teile das dann geben wird - schau'n 'mer 'mal.

Zunächst einmal muß gesagt werden, daß 64-bittige Betriebssysteme nun nichts wirklich fundamental Neues sind. Bereits in den 90er Jahren gab es von HP das Betriebssystem Tru64 als 64-bittiges Unix und von DEC gab's den Alpha Prozessor (der heute über den Umweg über Compaq sein Leben bei HP aushaucht), der 64 bit breite Register hatte. Aber der Weg in den Mainstream war lange Zeit nicht nötig und die Verbreitung von 32-bittigen Betriebssystemen war wohl auch ein Grund, warum 64-bit Betriebssysteme lange auf sich warten liessen. Aber es ist sicherlich nicht die doppelte Registerbreite von 64-bit Prozessoren, die 64-bit Betriebssysteme jetzt allmählich in den Mainstream Einzug halten lassen, sondern der immens viel größere Adreßraum. Bei 32-bit Prozessoren kann man pro Prozeß einen virtuellen Adreßraum von 2^32 Byte adressieren, also genau 4GB. Bei NT-basierten Betriebssystemen kommt hinzu, daß diese 4GB virtueller Adreßraum partitioniert sind in 2GB für den user mode und in 2GB für den kernel mode, so daß so ohne weiteres kein Prozeß mehr als 2GB an Speicher allozieren kann. Darum wurde IIRC auch mit NT4 SP3 die Möglichkeit eingeführt, dem user mode 3GB zuzuteilen und dem kernel mode nur 1 GB. Das Ganze geht aber nur unter Windows XP, Windows Server 2003, Windows NT 4 Enterprise Edition, Windows 2000 Advanced Server oder Datacenter Server mit dem /3GB switch in der boot.ini und die Applikation muß mit einem speziellen Linkerflag gelinkt sein, um das auszunutzen. Um die Sache mit dem knappen Adreßraum etwas weiter zu entschärfen, hat Intel dann noch die AWE (address window extensions) eingeführt, um auf 36 bit Adreßraum zu kommen (jedoch muß der Speicher auch wirklich physikalisch vorhanden sein, mit virtual memory und demand paging geht dann nichts mehr). Das Ganze funktioniert offenbar irgendwie mit dem Einblenden von Speicherseiten ähnlich dem LIMS EMS Standard von weiland Anfang der 90er Jahre mit dem Windows 3.1 Real mode. Sei es wie es will, das alles ist ein übler Hack und die 4GB Grenze, unter der speicherintensive Applikationen wie etwa große Datenbanken leiden, kann man nur mit einer Vergrößerung des Adreßraums knacken.

Der erste Prozessor, der als 64-bitter Einzug in den Mainstream halten sollte war eine gemeinsame Entwicklung von Intel und HP, der Itanium 1. Mittlerweile beim Itanium 2 angekommen, scheint dieser Prozessor aber nicht wirklich abheben zu wollen, obwohl es auch von MS eine dazu passende Windows 2000 Server Edition gibt. Der Grund dafür sind die exorbitanten Kosten für solch einen Prozessor, die dafür sorgen, daß ein IA64 (so heisst die Prozessorfamilie) wohl auf absehbare Zeit in keinem Aldi-PC zu finden sein wird. Ein weiteres Hindernis ist, daß ein IA64 die x86 Binaries, die für Windows auf der x86 Plattform gebuildet wurden, nicht nativ ausführen kann, sondern emulieren muß und dabei nicht unbedingt seine Stärken ausspielen kann. Mittlerweile hat sich HP aus der Itanium-Entwicklung zurückgezogen und MS hat die IA64-Versionen von Windows XP und Windows Vista abgekündigt. Für die weiteren Überlegungen in dieser Serie wird daher der IA64 keine Rolle mehr spielen.

Zwischenzeitlich hat nämlich AMD eine behutsame Erweiterung des x86-Befehlssatzes entwickelt, die Registerbreite verdoppelt und das Ganze als AMD64 auf den Markt gebracht. Das Ergebnis ist ein Prozessor, der in einem x86-Mode betrieben werden kann (man kann also ein herkömmliches NT/W2K/XP für x86 auf so einem Rechner installieren) oder in einem 64 bit Modus. Intel hat natürlich Patentaustauschabkommen mit AMD und darf daher einen befehlssatzkompatiblen Pentium 4 Prozessor mit der "EM64T" extension anbieten, was im wesentlichen dasselbe ist wie die AMD64 Prozessoren, zumindest für das Betriebssystem und darauf laufende Applikationen. MS war mit AMD schon früh zugange, ihre NT-basierten Betriebssysteme auch auf den AMD64 zu portieren, so daß seit Mai 2005 ein "Windows XP x64 Edition" zu kaufen ist, das auf einem AMD64 oder einem Intel EM64T im native 64 bit Mode läuft. Das Schöne ist, daß diese Prozessoren relativ günstig sind und ein Windows XP oder ein W2K3 Server x64 Edition (wurde erst kürzlich releast) die x86 Binaries in der Regel problemlos ausführt und zwar direkt auf dem Prozessor und nicht über einen Emulator. Das bedeutet, daß vorhandene Investitionen in x86 Software erhalten bleiben. Zusätzlich fällt die oben erwähnte Partitionierung in 2GB user mode und 2GB kernel mode weg, wenn ein x86 Binary auf einer x64 Edition von Windows betrieben wird. Dadurch hat man bei vielen Aplikationen schon ohne erneutes und natives Übersetzen für AMD64 bereits einen Vorteil, wenn man sie auf einer x64 Edition ausführt.

So meine Mittagspause ist jetzt vorbei. Das nächste Mal erzähle ich was über Entwicklungswerkzeuge für die x64 Editions von Windows.

(Edited: Paar Typos beseitigt und das mit dem physikalisch vorhandenen Speicher korrekterweise den AWE zugeordnet, mit dem /3GB switch hat das nichts zu tun, Links ergänzt, Standard Mode(das war nämlich daselbe wie der 286 Protected Mode) ersetzt durch Real Mode)

Funny Code - Die Auflösung des Rätsels

Letzte Woche habe ich in einer Email aufgefordert, die Fehler in folgendem Codeabschnitt zu finden:

CString GetComputerName ()
{
  static CString szComputerName;
  if (szComputerName.IsEmpty())
  {
    DWORD dwDataSize = MAX_PATH;

    ::GetComputerName( szComputerName.GetBuffer( MAX_PATH ), 
      &dwDataSize );
    szComputerName.ReleaseBuffer();
  }
  return szComputerName;
}

Der absolute Gewinner war Olli, denn er hat kurz und knapp die drei wichtigsten Probleme gefunden:

"- Anstatt MAX_PATH muss es "MAX_COMPUTERNAME_LENGTH + 1" heissen
- GetBuffer kann eine Exception werfen.
- Nicht threadsafe"

Warum ist das Ganze nicht threadsafe? Weil ein nichttrivialer Typ als globale Variable in szComputerName verwendet wird. Vom Crash bis zum Speicherleck kann alles mögliche passieren wenn zwei threads dumm preempted werden, wenn sie parallel durch diesen Code laufen. Und tatsächlich kann CString::GetBuffer eine CMemoryException werfen. Ob wohl jeder Aufrufer auf sowas vorbereitet ist? Besser wäre es, die Exception in der Funktion aufzufangen und den Fehler mit einem return value anzuzeigen oder mit Setzen eines last error für den aufrufenden Thread, so daß man die Funktion nach "Fire and Forget"-Manier aufrufen kann und sich um Exceptions nicht kümmern muß. Und last but not least ist die Größe des Puffers mit 260 Zeichen für MAX_PATH auch etwas übertrieben, gemessen an den schlappen 15 für MAX_COMPUTERNAME_LENGTH. Was Olli aber nicht gefunden hat, sind vier weitere Probleme:

  • <Korinthenkacker>Wenn schon ungarische Notation (App's Hungarian, um genau zu sein), dann richtig. Es darf dann nicht szComputerName heißen, sondern muß strComputerName heißen.</Korinthenkacker>
  • Selbst wenn die Funktion intern alle Exceptions abfängt, so kann immer noch eine Exception auftreten, wenn der Rückgabewert der Funktion einer CString-Variablen im Scope des Aufrufers zugewiesen wird. Der Aufrufer kommt also in keinem Fall um einen try...catch herum, es sei denn man gibt den Computer-Namen nicht als Rückgabewert zurück sondern hat eine Referenz auf einen CString als formalen [out]-Parameter. Wow, und wenn man dann das tut, hat man plötzlich wieder unverhofft einen Rückgabewert frei, wo man FALSE im Fehlerfall zurückgeben kann. Und weil dann auch kein lokales Objekt mehr da ist, ist sogar ein SetLastError im Fehlerfall bombensicher.
  • Anstatt dem GetBuffer die Konstante MAX_PATH zu übergeben, hätte man durchaus die Variable dwDataSize übergeben können, dann hätte man eine einzige Stelle im Code, wo man den MAX_PATH auf (MAX_COMPUTERNAME_LENGTH+1) hätte abändern müssen.
  • Statt CString::GetBuffer wäre es angebrachter, CString::GetBufferSetLength einzusetzen, da diese Methode den String garantiert erstmal nullterminiert. CString::GetBufferSetLength einzusetzen ist eine gute Angewohnheit, da diese Methode auch in denjenigen Fällen den String nullterminiert, wo die Funktion, die den String dann beschreibt, dies nicht tut.

Ich persönlich finde die fehlende Exception-Awareness nicht so suupertragisch, weil MFC-Code ist normalerweise UI code und was macht es schon, wenn solcher Code dann mal die Grätsche macht? Viel tragischer fände ich es, wenn solcher Code in Services eingesetzt wird, wo eigentlich 24/7 gefragt sein sollte. Das Bestürzende ist allerdings meiner Meinung nach, daß dieser Code in jedem Thread aufgerufen wird, der Logfiles schreibt und zwar ziemlich genau am Anfang, wenn die Logfile-Präambel geschrieben wird. Wenn zwei Threads also ziemlich zeitgleich starten, ist also die Gefahr einer Race-Condition in dieser Funktion durchaus gegeben.

Wie spricht man eigentlich "Pracujemy nad rewoluja. Przylaczysz sie?" ?

Eine immerwiederkehrende Frage von Besuchern in επτ€σ Mission Control. Hier ist die schnelle Antwort und hier ist die etwas langsamere Antwort

<< 1 ... 35 36 37 38 39 40 41 42 43 44 45