Académique Documents
Professionnel Documents
Culture Documents
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
SSPrá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 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
SSPrá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);
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
SSPráctica 1.
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.
4/9
SSPrá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):
¿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.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
SSPrá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
SSPráctica 1.
7/9
SSPrá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
SSPrá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