2016-04-16 13 views
1

Ich speichere eine Reihe von pid s (d. H. Linux-Prozess-IDs) in einem Array von long s. Ich stelle fest, dass ein pid kein long ist, aber ich habe keine Wahl in Bezug auf die Verwendung eines anderen Variablentyps.Drucken pid mit% d vs% ld, linux

Das Problem, das ich habe, tritt auf, wenn ich versuche, pid mit printf zu drucken. Wenn ich die long drucken, die pid mit %ld speichert, ich die falsche pids erhalten:

8435315771308 process_ancesto 
8358006359962 bash 
8353711392665 login 
4294967297 init 
0 swapper/0 

Wenn ich jedoch %d Drucke mit (die eine Compiler-Warnung erzeugt), habe ich das richtige Ergebnis erhalten (dh das Ergebnis zurückgegeben von ps in das Terminal eingeben):

1969 process_ancesto 
1946 bash 
1945 login 
1 init 
0 swapper/0 

Was dieses Verhalten verursacht? Ein Pid -> Long Cast ist eine Erweiterung und sollte keine Probleme verursachen.

Hier ist das Programm, das ich verwende, um den Systemaufruf zu machen, die die PIDs zurückzugibt:

int main(int argc, char *argv[]) 
{ 
    struct process_info arr[10]; 
    long n=0; 
    int result = syscall(_CS300_PROCESS_ANCESTORS_, arr,10,&n); 
    assert(result==0); 
    for(int i=0;i<n;i++) { 
     printf("%d %s\n",arr[i].pid,arr[i].name); 
    } 
    return 0; 
} 

Wenn ich die %d mit %ld druckt falsche Informationen ersetzen. Hier

ist die Linie von meinem Systemaufruf, wo ich notiere die pid:

if(copy_long_to_user(&info_array[num_filled_kernel].pid, &curr_task->pid)) {     
      return -EFAULT; 
     } 

info_array[num_filled_kernel].pid ein lang ist.

+0

uns zeigen eine vollständige aber Minimalprogramm Arbeitsprobe, die Ihre demonstriert Problem. –

+0

Welche Art von Computer verwenden Sie (Intel? 32 oder 64 Bit?) - erzählt uns mehr über Ihre Umgebung. – Soren

+0

Intel, 64-Bit Ubuntu 14.04 – Adam

Antwort

5

Zwei Probleme:

In Ihrem copy_to_user, ist das zweite Argument ein Zeiger auf pid_t, wie es scheint, die, wie Sie sagen, ist ein int (32 Bit). Sie kopieren also 64 Bits von einer 32-Bit-Variablen; die restlichen 32 Bits (die obere Hälfte, da x86 ein Little-Endian ist) werden mit dem gefüllt, was als nächstes in Erinnerung kommt, wenn Sie Glück haben.(Wenn Sie Pech haben, erhalten Sie hier einen Fehler.) Ganzzahlige Konvertierungen werden nicht ausgeführt, wenn Sie über Zeiger auf Objekte zugreifen.

Der einfachste Weg, um dies sicher zu tun, ist eine temporäre Variable zu verwenden:

long tmp = curr_task->pid; // sign-extension done here 
copy_long_to_user(..., &tmp); 

Dann in Ihrem Code Benutzerraum, mit dem Sie die %d Formatspezifizierer etwas zu drucken, die ein long anscheinend ist. Dies wird nicht funktionieren. printf, die eine Variadic-Funktion ist, kennt den Typ, dessen Argumente erwartet werden, nicht und kann sie daher nicht entsprechend konvertieren. Wenn Sie eine long übergeben möchten, verwenden Sie den Formatbezeichner %ld.

+0

[Ich stimme der Lösung] (http://stackoverflow.com/a/36658968/103167), aber das ist kein "temporäres". –

+1

@BenVoigt: Um grammatikalisch zu sein, hätte ich "temporäre Variable" sagen sollen. Ich meinte es im informellen Sinne des Wortes ["eine Variable, deren Zweck kurzlebig ist"] (https://en.wikipedia.org/wiki/Temporary_variable), nicht in irgendeinem formellen Sprachstandard-Sinn. –

+0

@BenVoigt: Oh, ich sehe das war eine vorherige Frage vom selben Benutzer. Unbeholfen, dass sie sich auf diese Weise überlappten. –

2

Ein Pid -> Long Cast ist eine Erweiterung der Konvertierung und sollte keine Probleme verursachen.

Sicher, aber es sei denn, Sie werden diese Besetzung tatsächlich ausführen, zumindest nicht mit varargs/stdargs. Übergeben Sie das Argument entweder selbst oder verwenden Sie den richtigen Spezifizierer.

+0

Was passiert, wenn ich etwas betone und dann den Adressoperator darauf verwende? Das ist es, was ich mit "copy_to_user" anfangen muss. Wie kann sich eine Adresse auf zwei verschiedene Variablentypen beziehen? – Adam

+1

Sie müssen sich Darsteller und Adress-/Dereferenzierungsoperatoren als No-Ops vorstellen. Sie ändern den Typ für den C-Compiler, aber in den allermeisten Fällen beeinflussen sie die Daten in keiner Weise. –

+0

Also, wenn ich eine 'pid' zu einer langen werfen, ändert sich die zugrunde liegende" pid-ness "der' pid' Variablen nicht. Die Besetzung erlaubt es uns, eine Kopie der pid-Variablen in eine Variable vom Typ long einzufügen. – Adam

2

Wenn Sie einen Blick auf Ihre Zahlen als Hex-Zahlen

8358006359962 bash 

in hex ist

79A0000079A 

und

1946 bash 

in hex ist

79A 

Also hier raten - aber Sie haben etwas schlecht gemacht, wenn Sie die Zahlen in Long-Longs in copy_long_to_user konvertiert, da die 79A-Sequenz in den höheren Bits wiederholt.

1

So ist die pid in den Ausgabe-Array kopiert wird unter Verwendung von:

copy_long_to_user(&info_array[num_filled_kernel].pid, &curr_task->pid) 

Wenn curr_task ist ein struct task_struct*, dann ein curr_task->pidint ist, die 4 Bytes auf Linux sind (entweder 32 oder 64 Bits). Allerdings long auf 64-Bit-Linux ist 8 Bytes, so dass Sie am Ende nicht nur die pid kopieren, sondern 4 andere benachbarte Bytes (die wahrscheinlich die tgid sind - ich weiß nicht genau, was das ist, aber es sieht aus wie es oft hat der gleiche Wert wie die PID).

Update: die tgid ist eine Thread-ID-Gruppe, und es hat oft den gleichen Wert wie die pid: