Vous êtes sur la page 1sur 9

SS­Práctica 1.

Práctica 1. C para sistemas (I). 
Software de Sistemas. 3º IC.
6 de octubre de 2014. 
Práctica 1. C para sistemas. 
Objetivos. 
Qué hacer antes de entrar al laboratorio… 
Qué hacer en el laboratorio. 
Evaluación. 
Usando intrinsics: __cpuid. 
Para probar las funciones. 
Discutir en clase. 
Operaciones a realizar en LeeIDFabricante y LeeIDModelo. 
Usando el depurador para analizar el comportamiento de un programa. 
Más detalles… 
Identificación del procesador. La instrucción CPUID 
Funciones extendidas. 
Valores de EAX mayores o iguales a 0x8000 0000. 
 
 
 

1 Objetivos. 
Empezar a utilizar el entorno de desarrollo de Visual Studio 2010 y, sobre todo, su 
depurador.  
Empezar a usar las características de C para la programación en bajo nivel. 
Usar el depurador para estudiar la organización de la memoria virtual de programas 
creados a partir de código C: dónde se almacena el código y los distintos tipos de 
datos (dependiendo de si son datos globales o locales). 
 

1.1 Qué hacer antes de entrar al laboratorio… 
 
Repasar lo visto en clase o en éste enunciado: 
1
● Qué es un IDE . 
2
● Qué es un proyecto y un gestor de proyecto .  
3
● Las operaciones para generar y compilar una aplicación C a partir de un proyecto vacío . 
4
● Activar o desactivar puntos de interrupción (breakpoints ). 
5
● Qué son punteros de C y cómo se utilizan . 
6
● Cómo se definen matrices y matrices de caracteres .  

1
Entorno integrado de desarrollo. Ver Tema 1. 
2
Ver tema 1. 
3
Ver documentos 00.05_AntesDeEmpezar.pdf y 00.10_CrearNuevoProyectoC.pdf en la carpeta
SS-Pub/Videos. 
4
Ver tema 2. 
5
Visto en asignaturas anteriores, revisado en tema 1.  
6
Visto en asignaturas anteriores, revisado en tema 1. 
1/9 
SS­Práctica 1. 

 
Qué traer de casa (este programa entra en la evaluación): 
● Prueba P01.A. de enseñanza virtual. 
 

2 Qué hacer en el laboratorio. 
Programando en bajo nivel usando C: intrinsics, vectores, cadenas.  
1) Realizar LeeIDModelo.  
 
Usando el depurador para estudiar el funcionamiento interno de un programa. 
2) Modificar LeeIDModelo para que tenga variables globales y locales, y usarlo para hacer el
Ejercicio 1.2. 
El funcionamiento de la pila: 

3) Utilizar LeeIDModelo para hacer el Ejercicio 1.3 


 
 

3 Evaluación. 
7
● Pr1.A antes de entrar en el laboratorio: + 2 puntos máximo, 1,5 puntos mínimo .  
o Se activa el martes 30/9, a las 14:30. 
● Pr1.B después del laboratorio: + 8 puntos máximo. 
o Se activa el lunes 6/10, a las 11:00. 
 

4 Usando intrinsics: __cpuid. 
Vamos a realizar dos funciones C que devuelven la identificación del procesador: 
8
● LeeIDFabricante. Lee la cadena de identificador del fabricante del procesador .  
● LeeIDModelo. Lee la cadena de identificación de modelo del procesador.  
 
Se basan en ejecutar la instrucción CPUID. Es una instrucción del procesador IA32 que 
permite obtener información de identificación y características del procesador que la 
ejecuta. 
En ensamblador se usaría:  

MOV EAX, 0 ; Cargamos en EAX el número que codifica el tipo de 
 información solicitada 
CPUID ; ejecutamos CPUID. 
  ; ← Tras ejecutar CPUID, tenemos en los registros EAX, EBX, ECX y 
EDX  
;  la información pedida.  
; El contenido de los registros depende del valor de EAX antes de ejecutar CPUID. 
; Con EAX = 0, tras ejecutar CPUID tenemos: 

7
Una puntuación menor en esta prueba invalida las demás pruebas. 
8
Esta función ya se ha hecho en clase de aula.  
2/9 
SS­Práctica 1. 

; EAX ← máximo valor que soporta la instrucción CPUID en este procesador.
9
 
; EBX:EDX:ECX ← Cadena de identificación del fabricante (cuatro caracteres ASCII  
;     por registro). Según el fabricante, puede ser: “GenuineIntel”, 
“AuthenticAMD”,  
; “CyrixInstead”10  

 
CPUID está soportado por el compilador C de Visual Studio 2010 con la función  
11

void __cpuid(  int CPUInfo[4],  int ValorAcargarEnEAX ); 

donde:
● ValorAcargarEnEAX es el valor que se cargará en EAX,  
● CPUInfo es un vector de 4 enteros donde se guardan los registros EAX (elemento 0), EBX
(1), ECX (2) y EDX (3) tras ejecutar CPUID. 
Más información sobre CPUID en el la sección 6.1. 
 

4.1 Para probar las funciones. 
 
Los prototipos de las funciones a realizar son: 

int LeeIDFabricante (char * CadFabricante); 
/* CadFabricante: dirección inicial de la cadena donde se almacenará  
          el mensaje del fabricante. Debe tener al menos 13 caracteres. 
    Devuelve el valor de EAX tras ejecutar CPUID.  */ 
void LeeIDModelo (char * CadenaModelo); 
/* Devuelve en CadModelo la cadena de modelo del procesador */ 

Para probar las funciones se puede usar el siguiente programa: 
 

#include <stdio.h> 
#include <stdio.h>
#include <locale.h>
// Prototipos
int LeeIDFabricante (char * CadFabricante);
void LeeIDModelo (char * CadenaModelo);

int main(int argc, char *argv[])


{
int Resultado;

setlocale( LC_ALL, "Spanish" );

Resultado= LeeIDFabricante(CadenaFabricante);
printf("\nLa identificación del fabricante es: %s. El máximo valor de CPUID es

9
La primera versión de CPUID aparece en el 80486 y permite leer solo la identificación del fabricante
(EAX=0) y la palabra de características del procesador (EAX=1). Conforme aparecieron nuevos
procesadores de la familia, se fueron añadiendo más características a CPUID, que se obtienen
ejecutando la instrucción con valores más altos en EAX. Ejecutar CPUID con EAX a 0 permite conocer
hasta qué características de CPUID es capaz de reconocer el procesador en el que estamos
ejecutando actualmente.  
10
Estos son los fabricantes más comunes en España. Puede haber hasta 10 cadenas distintas,
correspondientes a otros tantos fabricantes de procesadores “compatibles” con los IA32. 
11
CPUID no está soportada por la mayoría de lenguajes. De hecho, tampoco lo está en C estándar, ya
que es una instrucción propia del IA32, por tanto, no compatible con otras arquitecturas. La función
__cpuid no es portable a otros compiladores C. 
3/9 
SS­Práctica 1. 

%d.\n", CadenaFabricante, Resultado);


LeeIDModelo(CadenaModelo);
printf("\nLa cadena de modelo es: %s\n", CadenaModelo);

printf("\nPulse tecla RETORNO para terminar\n");


getchar();
return 0;
}

Listado 1.1.  
 
El programa imprime en pantalla algo similar a: 

La identificación del fabricante es: GenuineIntel. El máximo valor de CPUID es 
10. 
La cadena de modelo es: Intel(R) Pentium(R) CPU  T2370 @ 1.73GHz 

4.2 Discutir en clase. 
 
● Posibles formas de definir CadenaFabricante y CadenaModelo. 
● Posibles formas de definir una variable para contener el primer parámetro de __cpuid. 
● Qué “fallos” tiene el programa anterior (aparentemente funciona, pero puede dar fallos en
el futuro). 
 

4.3 Operaciones a realizar en LeeIDFabricante y LeeIDModelo.  
 
● LeeIDFabricante (hecho en clase) 
o Ejecutar __cpuid con ValorAcargarEnEAX a 0. 
o Copiar los datos de los registros a la cadena a CadResultado 

Codificar y probar que funciona.  
 
● LeeIDModelo. 
o Ejecutar __cpuid con ValorAcargarEnEAX a 0x8000 0000 (funciones extendidas
de CPUID). 
o Comprobar que la función devuelve en EAX (CPUInfo[0]) un número mayor que 8000
0000h. Si no es así, terminar la función, ya que el procesador no soporta la cadena de
modelo. Pondremos en CadenaModelouna cadena vacía.  
o Ejecutar __cpuid con 3 valores distintos en ValorAcargarEnEAX: 0x8000 0002,
0x8000 0003 y 0x8000 0004. En cada ejecución de __cpuid, el procesador devuelve
una parte de la cadena en CPUInfo (en realidad, en EAX, EBX, ECX, EDX). Guardarla en
el sitio adecuado de CadenaModelo.  

5 Usando  el  depurador  para  analizar  el  comportamiento 


de un programa. 
El uso normal de un depurador es analizar posibles errores en un programa. Sin 

4/9 
SS­Práctica 1. 

embargo, también se puede usar para analizar detalles de implementación de un 
lenguaje de programación, un compilador, linker, juego de instrucciones, etc.  
Como ejemplo, veremos los pasos que hay que seguir para contestar el siguiente 
ejercicio: 
 

Ejercicio 1.1. Cuando un compilador de lenguaje de alto nivel genera una aplicación, puede
organizar la memoria siguiendo este esquema (Unix):

Posiciones más bajas de memoria virtual → introduce el código del programa.


Posiciones intermedias → introduce las variables globales.
Posiciones más altas de la mem. virtual → las reserva para la pila.

¿Utiliza el compilador de VC++ este esquema? Dibujar un esquema que indique la posición
relativa de estas zonas.  

Para contestar se puede utilizar un programa muy sencillo: 

int VariableGlobal = 0xaabbccdd; 
int main (void)
{
VariableGlobal ++;
return 0;
}

 
Con VC++ se compila y depura, colocando un punto de parada en la línea 4. Tras parar 
la ejecución, se puede examinar el contenido de los registros del procesador para ver 
dónde está la pila, y el código desensamblado para ver las posiciones de memoria 
donde se almacena el código y la variable global.  
 

Ejercicio 1.2. Repetir el ejercicio anterior usando LeeIDModelo.

¿Qué valores iniciales tiene una matriz si se define como global?


¿Y si se define como local?
¿Ocurre lo mismo en Java?  

Ejercicio 1.3. Hacer una función recursiva que se llame a sí misma 10 veces. Usarla para
contestar las siguientes preguntas:

1) ¿La pila de este procesador crece hacia arriba o hacia abajo (es decir, hacia direcciones
altas o bajas de la memoria)?
2) ¿Cuál es el tamaño del stack frame de la función?
3) ¿Qué influye en el tamaño del stack frame de la función? Esta pregunta se hace como
ejercicio de pizarra (la contesta el profesor).
4) Modificar la función recursiva para provocar un error de “stack overflow”.
 

5/9 
SS­Práctica 1. 

6 Más detalles… 

6.1 Identificación del procesador. La instrucción CPUID 
En muchas familias de procesadores puede haber diferencias en los modelos de 
programación de los distintos componentes. Afectan a aspectos tales como el juego de 
instrucciones (instrucciones que sólo se ejecutan en algunos modelos), la existencia o 
no de algunos registros internos, la estructura y funcionamiento de los caches internos 
y la unidad de manejo de memoria,… 
Las diferencias conciernen a un número muy reducido de programas, que 
normalmente son parte del sistema operativo12. Es necesario, por tanto, que el 
procesador disponga de algún mecanismo para conocer qué modelo se está usando. 
El mecanismo exacto puede ser muy distinto de un tipo de procesador a otro. En el 
caso de los IA3213 la instrucción CPUID permite obtener información sobre el modelo y 
algunas de las características del procesador que la ejecuta.  
En esta práctica sólo veremos una pequeña parte de las características de esta 
instrucción. Para una descripción más completa de su funcionamiento, se puede 
consultar: 
● Intel Architecture and Processor Identification With CPUID Model and Family Numbers.
● Para procesadores AMD: CPUID pecification. 
 
Antes de ejecutar CPUID se debe cargar en EAX un valor que indica qué información 
debe devolver. Sólo usaremos los valores 0 y 1 en la primera parte de esta práctica. 
 
EAX antes de CPIUD Registros después de ejecutar CPUID. 
EAX ← 0  EAX ← Valor más alto reconocido por la instrucción. 
EBX:EDX:ECX ← Cadena de identificación del fabricante. Según el
vendedor, puede ser: “GenuineIntel”, “AuthenticAMD”, “CyrixInstead”
14
 
EAX ← 1  EAX ← Firma o identificación del procesador (processor signature)
15

EDX, ECX ← Información sobre características del modelo de 
programación (feature information) 
 
Con EAX a 0 devuelve el máximo valor de EAX que CPUID puede reconocer para ese 
procesador en particular. Este mecanismo permite al fabricante añadir nuevas 
características a la instrucción CPUID de los nuevos modelos. En los Pentium más 
antiguos, conocidos como P5, CPUID devuelve 1, mientras que en los Pentium Pro y 
Pentium II (P6) devuelve 2.  
En la figura siguiente se muestra el resultado de CPUID en un Intel Pentium P5 o P6. El 
significado de los campos es idéntico si se ejecuta en un AMD K5 (o superior), y en un 
CIRIX M1 (o superior). 

12
Si las diferencias afectaran a la mayoría de los programas, tendríamos procesadores con modelos
de programación distintos.  
13
La instrucción CPUID también se implementa en algunas versiones de 80486 y en todos los
procesadores “compatibles” con los IA32 fabricados por AMD y Cirix. 
14
Ver una lista más completa aquí. 
15
En el Pentium III, aquí se almacenan los 32 bits más significativos del número de serie (96 bits). En
los Pentium 4 se dejó de implementar esta característica. 
6/9 
SS­Práctica 1. 

Figura 1.1. Resultado de ejecutar CPUID en un Pentium para EAX 0 y 1. Esta información es


del manual de un Pentium III. Procesadores más modernos pueden usar los campos reservados. 

7/9 
SS­Práctica 1. 

Figura 1.2. Valores devueltos en EDX y ECX cuando ejecutamos CPUID con EAX a 1 en un
procesador Intel. 

CPUID con EAX igual a 1 permite, entre otras cosas, obtener un código para identificar 
el modelo del procesador a través de los campos Processor Type, Family, Model y 
Stepping del registro EAX.  
 

6.2 Funciones extendidas. 
 

8/9 
SS­Práctica 1. 

Algunos procesadores de Intel y AMD de un mecanismo alternativo. Son las “funciones 
extendidas”, que implementan por hardware información que, de otra forma, se debería 
obtener con software de testado.  
Las funciones extendidas se usan de forma parecida al las “funciones básicas” que 
hemos visto anteriormente: primero se averigua hasta que número de función podemos 
usar y después las usamos colocando el código de función en EAX: 
 
EAX antes de CPIUD Registros después de ejecutar CPUID. 
EAX ← 0x8000 0000  EAX ← Valor más alto reconocido por la instrucción. Este 
valor 
Debe ser mayor que 0x8000 0000. En caso contrario, el 
procesador no soporta funciones extendidas 
EAX ← número mayor  Ejecuta la operación extendida correspondiente al código que
que 0x8000 0000  se le pase (ver los valores que hay que cargar en EAX en la 
sección siguiente) 
 

Valores de EAX mayores o iguales a 0x8000 0000. 
Valores devueltos en EAX para un procesador Intel (las características que usaremos 
en esta práctica están reflejadas en los mismos bits en procesadores de AMD). 

Figura 1.3. Están resaltados los valores que sirven para construir la cadena de la marca del
procesador. to: para numerar correctamente ejercicios. 

9/9 

Vous aimerez peut-être aussi