PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : PHP/RPi: 64 bit int-String auf 32bit-Hardware berechnen



Jaecko
11.10.2021, 07:26
Moin.

Kleines Problem:
Ich hab einen alten Raspberry Pi (einer der ersten, noch mit Wheezy), 32 bit, PHP5, keine GMP-Erweiterung. Updates gibts nicht mehr, da die ganzen online repos schon weg sind. Eine Änderung dieser Hardware sind technisch und wirtschaftlich bedingt nicht möglich oder sinnvoll. An diesem Pi hängen 14 (Archiv-)Festplatten, die über SSH ein/aus geschaltet werden können. Es ist also nur die Platte aktiv, deren Daten ich brauche. Über smartctl les ich von den Platten u.a. die Temperatur, Power On Hours etc. ein und lass mir das Ganze auf ner einfachen php-Seite anzeigen.
Das alles funktioniert jetzt schon seit 8 Jahren.

Hab jetzt aber schon 2 neue Festplatten dabei, wo der Wert für die Betriebsstunden unmöglich gross erscheint, z.B. 196889890783256. Da dieser Wert bis zu diesm Zeitpunkt ein String ist, kommt PHP noch damit klar, muss ja nichts gerechnet werden.
Rechne ich das dann mit dem Windows Calc nach Hex um, kommt 0xB31200000018 raus. Diese 0x....18 wären dann 24 Stunden, das geht sich bei der neuen Platte auch sehr genau hin.
Idee: Ich mach von diesem Wert einfach ein $val = $val & 0xFFFFFFFF, also hol mir nur die untersten 32 bit.
Problem: Ich krieg da dann 0x7FFFFFFF raus, da PHP auf dem 32bit-Pi eben mit diesem riesigen Wert nicht klarkommt.

Den Wert von smartctl direkt als hex kriegen geht scheinbar leider nicht, zumindest hab ich keine Option dazu gefunden.

Wie könnte man jetzt auf einem 32bit-System von diesem 64bit-String nur die unteren 32bit behalten und den Rest verwerfen?
24bit würden auch noch reichen.

MfG
Stefan

Moppi
11.10.2021, 12:22
"von diesem 64bit-String nur die unteren 32bit behalten"

196889890783256:

a = 6
a = a + (5*10)
a = a + (2*100)
a = a + (3*1000)
...

Dazu müsste man die Zeichen einzeln aus dem String extrahieren. count_chars (https://www.php.net/manual/de/function.count-chars.php) (Anzahl ermitteln) und str_split (den String in ein Array umwandeln)können dabei behilflich sein.

MfG

Jaecko
11.10.2021, 13:13
hm scheint nicht ganz das richtige zu sein.
Angenommen ich würde das z.B. für nur 6 stellen machen. Also ....783256.

Dann wäre das ja:
a = 6
a = a + (5*10)
a = a + (2*100)
a = a + (3*1000)
a = a + (8*10000)
a = a + (7*100000)

echo $a => 783256

Das wäre ja nur ne Alternative zu $a = (int) substr($val, -6);
Den String so zerschneiden geht ja, solange diese Zahl kleiner als 0x7FFFFFFF ist.
Aber: Es sind in diesen kleinen Dezimalstellen auch noch eben Werte der oberen 32bit drin.

Im Orignal-Wert (0xB31200000018) ist ja z.B. Bit 33 gesetzt (0x000200000000, 8589934592)
Wenn ich dann davon nur die letzten 6 Stellen nehme (934592) kann ich ohne Kenntnis der oberen 32bit eigentlich nichts sagen.

Nehmen wir vereinfach einfach mal den ausgelesenen Wert (0x000200000002, 8589934594).
Die unteren 32 bit sind 2, d.h. die Platte hätte 2 Stunden hinter sich.
Aber nur aus den letzten Stellen der Dezimalzahl (934594) komm ich da nie auf die tatsächliche Laufzeit.

Wenn ich nun weiss, dass 0x000200000000 = 8589934592, dann kann ich 934594 - 934592 rechnen und krieg die wirklichen 2.
Kann ich aber nicht, da 0x000200000000 > 0x7FFFFFFF.

Scheint also entweder tatsächlich nicht so ohne weiteres möglich zu sein, oder es ist so dermassen einfach, dass man's vor lauter denken nicht sieht.

MfG

Moppi
11.10.2021, 15:25
Hallo!

Der Wert, den Du von smartctl als String bekommst, wie kommst Du an den Wert? Automatisch? Oder nur manuell? Was ist das genau für ein Wert, Sekunden, Stunden, Minuten? Und was möchtest Du daraus haben, Stunden, Minuten, Sekunden?

Alles, was man mit einem Computersystem mathematisch nicht verarbeiten kann, weil der Wertebereich mit der vorhandenen Bitbreite nicht abgebildet werden kann, muss mathematisch so zerlegt werden, dass man damit arbeiten kann. Wie auch immer. Dabei ist auch das Ziel interessant. Zum Beispiel wegen der Genauigkeit. Sobald nur eine Manuelle Eingabe (oder eben ein String) vorhanden ist, muss der dann analysiert und sinnvoll zerlegt werden. Deswegen oben meine Fragen. Ich verstehe das nämlich noch nicht, was genau von was in was umgewandelt werden sollte. Man kann auch Hex-Werte, die als String vorliegen, zerlegen und umwandeln.
Ich habe leider in PHP auch erst einmal keine Funktion gefunden, welche die Arbeit abnehmen könnte. Deswegen gehe ich in solchen Fällen den mühseligeren Weg, der aber irgendwie immer funktioniert.


(0x000200000002, 8589934594)
Die unteren 32 bit sind 2, d.h. die Platte hätte 2 Stunden hinter sich.
Aber nur aus den letzten Stellen der Dezimalzahl (934594) komm ich da nie auf die tatsächliche Laufzeit.


Was ist mit dem Hex-Wert - 0x000200000002 ? Ist das der originale Wert, wie er sein soll, weil die letzte "2" 2 Stunden representiert? Und Du bekommst aber nicht den Hex-Wert, sondern nur den entsprechenden Wert in Dezimalschreibweise und das als String?



Das wäre ja nur ne Alternative zu $a = (int) substr($val, -6);
Den String so zerschneiden geht ja, solange diese Zahl kleiner als 0x7FFFFFFF ist.

Ja. Wird die Zahl größer muss die Rechnung dann zerlegt werden. In einen höherwertigen Teil und einen niederwertigen Teil. So, wie bei 0x1234. Ist ja eigentlich ein 16 Bit-Wert. Angenommen, deine CPU kann aber nur 8 Bit. Dann kann man das zerlegen in 0x12 und 0x34. Mit diesen beiden Teilen kann man weiter rechnen, th. beliebig große Zahlen.

Jaecko
11.10.2021, 16:16
Moin.

Ich führ in einem PHP-Skript den Aufruf für alle gemounteten Platten nacheinander aus mit:



$cmdSmart = "sudo -u root /usr/sbin/smartctl -A $device -d sat 2>&1";
$resultSmart = shell_exec($cmdSmart);


In $resultSmart geh ich dann Zeilenweise durch und hol mir die interessanten Werte (Power On Hours, Start Stop Count, Temperature)
Da ich die Werte einfach nur als String einlese und auf ner Webseite anzeige, musste ich auch nicht wirklich rechnen.
Lediglich bei der Temperatur gibts Warnungen, wenns zu heiss wird. Aber das sind Werte, die ich mit 6 bit auch schaffen würde.


Die Ausgabe sieht z.B. so aus für eine schon ältere Platte mit 10931 Betriebsstunden (ID 9):



ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE
1 Raw_Read_Error_Rate 0x002f 200 200 051 Pre-fail Always - 0
3 Spin_Up_Time 0x0027 172 170 021 Pre-fail Always - 2383
4 Start_Stop_Count 0x0032 099 099 000 Old_age Always - 1931
5 Reallocated_Sector_Ct 0x0033 200 200 140 Pre-fail Always - 0
7 Seek_Error_Rate 0x002e 100 253 000 Old_age Always - 0
9 Power_On_Hours 0x0032 086 086 000 Old_age Always - 10931
10 Spin_Retry_Count 0x0032 100 100 000 Old_age Always - 0
11 Calibration_Retry_Count 0x0032 100 100 000 Old_age Always - 0
12 Power_Cycle_Count 0x0032 099 099 000 Old_age Always - 1825
192 Power-Off_Retract_Count 0x0032 198 198 000 Old_age Always - 1804
193 Load_Cycle_Count 0x0032 200 200 000 Old_age Always - 126
194 Temperature_Celsius 0x0022 120 088 000 Old_age Always - 23
196 Reallocated_Event_Count 0x0032 200 200 000 Old_age Always - 0
197 Current_Pending_Sector 0x0032 200 200 000 Old_age Always - 0
198 Offline_Uncorrectable 0x0030 200 200 000 Old_age Offline - 0
199 UDMA_CRC_Error_Count 0x0032 200 200 000 Old_age Always - 12
200 Multi_Zone_Error_Rate 0x0008 200 200 000 Old_age Offline - 0


Und das ist eine der neuen mit einem "unmöglichen" Wert 168598941204504 = 0x995700000018, "unten" 0x18 = 24 Betriebsstunden.
Was das in den vorderen 32Bit (bzw. 16) ist, keine Ahnung, aber wechselt bei jedem Auslesen scheinbar willkürlich. Hab da noch kein Muster feststellen können.
Vorher: 196889890783256 0xB31200000018
Jetzt: 168598941204504 0x995700000018



ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE
1 Raw_Read_Error_Rate 0x000f 074 073 006 Pre-fail Always - 24503642
3 Spin_Up_Time 0x0003 099 099 000 Pre-fail Always - 0
4 Start_Stop_Count 0x0032 100 100 020 Old_age Always - 6
5 Reallocated_Sector_Ct 0x0033 100 100 010 Pre-fail Always - 0
7 Seek_Error_Rate 0x000f 065 060 045 Pre-fail Always - 2896370
9 Power_On_Hours 0x0032 100 100 000 Old_age Always - 168598941204504
10 Spin_Retry_Count 0x0013 100 100 097 Pre-fail Always - 0
12 Power_Cycle_Count 0x0032 100 100 020 Old_age Always - 6
183 Runtime_Bad_Block 0x0032 100 100 000 Old_age Always - 0
184 End-to-End_Error 0x0032 100 100 099 Old_age Always - 0
187 Reported_Uncorrect 0x0032 100 100 000 Old_age Always - 0
188 Command_Timeout 0x0032 100 100 000 Old_age Always - 0
189 High_Fly_Writes 0x003a 100 100 000 Old_age Always - 0
190 Airflow_Temperature_Cel 0x0022 073 057 040 Old_age Always - 27 (Min/Max 25/27)
191 G-Sense_Error_Rate 0x0032 100 100 000 Old_age Always - 0
192 Power-Off_Retract_Count 0x0032 100 100 000 Old_age Always - 4
193 Load_Cycle_Count 0x0032 100 100 000 Old_age Always - 91
194 Temperature_Celsius 0x0022 027 043 000 Old_age Always - 27 (0 25 0 0)
195 Hardware_ECC_Recovered 0x001a 074 073 000 Old_age Always - 24503642
197 Current_Pending_Sector 0x0012 100 100 000 Old_age Always - 0
198 Offline_Uncorrectable 0x0010 100 100 000 Old_age Offline - 0
199 UDMA_CRC_Error_Count 0x003e 200 200 000 Old_age Always - 0
240 Head_Flying_Hours 0x0000 100 253 000 Old_age Offline - 126937758433301
241 Total_LBAs_Written 0x0000 100 253 000 Old_age Offline - 1932648833
242 Total_LBAs_Read 0x0000 100 253 000 Old_age Offline - 3374992


Und die Werte werden eben leider nur dezimal angezeigt. Wären das Hex-Werte, wäre das Problem keins mehr. Dann würd ich nur die letzten 8 Hex-"Ziffern" einlesen und gut. Nur akzeptiert dieses smartctl den Parameter "hex,val" nicht für -f, sondern nur "old" und "brief".
Update-Versuch dieses einen Pakets: "smartmontools ist schon die neueste Version", weils für den alten Hobel nichts mehr gibt.

Das mit 0x000200000002 war nur mal ein Beispiel, dass eben auch die 2 in den oberen 32 Bit da quasi "stört".


MfG
Stefan

Moppi
11.10.2021, 16:22
Schau mal hier rein, ob das nützlich ist: https://www.php.net/manual/de/function.dechex.php

// Input: A decimal number as a String.
// Output: The equivalent hexadecimal number as a String.

Freundlichen Gruß

Jaecko
11.10.2021, 16:38
Die Funktion hab ich auch schon versucht, aber die spuckt auch nur 0x7FFFFFFF raus, da der Input eben zu gross ist.

Moppi
11.10.2021, 22:39
168598941204504 sind doch niemals die Betriebsstunden des Datenträgers.

Jaecko
11.10.2021, 22:49
Genau das ist ja das Problem an der Sache.

Umgerechnet in Hex ist der Wert 0x995700000018.
Und von diesem Wert brauche ich die unteren 32 bit => 0x995700000018 = 24 Stunden.
Diese 0x9957 oben rum sind "irgendwas" anderes. Keine Ahnung, ob das Sekunden sind, oder Minuten, Mondphase, Stärke vom Erdmagnetfeld, Anzahl Staubfussel aufm Gehäuse.... jedenfalls uninteressant und ohne erkennbares Muster, scheinbar völlig random.

Und Smartctl checkt das nicht, dass das da nicht dazugehört. Für den ist das der ausgelesene Raw Value.

Moppi
12.10.2021, 07:11
Was Du bekommst, ist ein wenigstens (angeblich) 48Bit großer Wert. Wenn die oberen Bits davon zufällig und willkürlich wechseln, würde ich von einem Fehler ausgehen. So weit hast Du das wohl auch schon bemerkt. Vermutlich was im Programm. Nächster Schritt für mich wäre, rausfinden, ob es an der neuen Platte liegt. Wäre für mich zwar nicht vorstellbar, aber ich würde nun eins nach dem anderen ausschließen.

Und ja, recht hast Du, an irgendeiner Stelle komme ich auch mit dem Umwandeln des Strings, aus Dezimalschreibweise, nicht weiter, weil irgendwann die 31 Bit während einer Berechnung überschritten werden. Eins ist vermutlich ein Vorzeichen-Bit.

MfG

Moppi
12.10.2021, 10:41
Gibt es einen Unterschied zwischen "smartctl -A" und "smartctl -a" ?
Schon sehr alt, aber vielleicht gibt es hier (https://de.comp.hardware.laufwerke.festplatten.narkive.com/v2x1Sg68/betriebsstunden-einer-samsung-hd) Hinweise, denen man folgen kann.

Jaecko
12.10.2021, 11:53
Nene das ist kein Fehler.
Es sind aktuell 14 Platten, und von denen haben das nur die 2 2TB von Seagate; auch alles unterschiedliche Modelle, also kein Serienfehler.
Alle anderen (Toshiba, Maxtor, WD) haben das Phänomen nicht. Hab auch vorher die Platte mal in Windows mit Speedfan ausgelesen, der liest den gleichen "Mist", nur eben brav als Hex. Aber der sieht den Wert auch unendlich hoch und die Festplatte damit als "uralt".

-A filtert die Ausgabe, so dass nur die Smart-Werte in der Tabelle ausgegeben werden.
Der Header fehlt dabei (also keine Disk-s/n, Modell, Firmware, etc.)

-a beinhaltet -A und noch bisschen was anderes (Health check, Capabilities, Fehlerausgaben, falls Fehler vorhanden)


Aber der Tipp mit dem Link war genial! Die Ausgabe in Minuten, Halbe Stunden etc. versagt halt zwar total, weil der gelesene Wert einfach Mist ist, ABER:
Ich kann die Ausgabe des Wertes als hex erzwingen mit "-v 9,hex48"; was komischerweise in meiner manpage nicht drinsteht.
Da heissts nur "Note that the printed output of smartctl displays numerical values in base 10 (decimal).", also kein Wort davon, dass das hex auch könnte.
Wundert mich etwas, da der Beitrag ja deutlich älter ist, als "meine" Version von smartctl.

Da hol ich mir dann einfach mit substr die hinteren 8 "Ziffern", hexdec, und fertig.

Jetzt schauen die Ausgaben nach was aus. Oben mit den noch fehlerhaften Werten, unten die richtigen Werte über Hex:
35617

DAS war der perfekte Tip! Merci!
Der Wert mit den 36013 Stunden stimmt übrigens, die Platte hat schon einiges hinter sich.

MfG
Stefan

shedepe
13.10.2021, 19:18
Vorschlag: Schreib dir ein C Programm, das kann problemlos 64bit zahlen verarbeiten.
In dem Programm rufst du dann smartctl auf, parst das und gibst halt die gewünschten 32bit zurück. Dieses Programm rufst du dann aus deinem PHP Skript auf