Configuración del entorno de trabajo.
Si no se configura bien el entorno de trabajo, se rendirá mucho
menos en la realización de las prácticas. Primero hay que
configurar el terminal. Para ello, hay que poner de acuerdo
al programa de terminal que tenéis instalado en el aula de
informática con la configuración que tiene el
servidor donde se realizarán las prácticas.
Antiguamente los servidores eran accedidos mediante un
dispositivo denominado terminal. Fundamentalmente constaban
de un monitor y un teclado y la suficiente electrónica para
recoger los caracteres tecleados y mostrar otros por la
pantalla. Los caracteres recogidos se enviaban a través de
una línea de conexión (normalmente serie) hasta el servidor
y, la respuesta de este llegaba al terminal por la misma
línea.
Actualmente, el esquema es diferente. En lugar de un terminal
se dispone de PCs de propósito general y un programa que
emula el comportamiento de los antiguos terminales.
La conexión en lugar de hacerse a través de una línea dedicada,
se realiza por Internet y siguiendo un protocolo, que normalmente
para conexiones de texto será TELNET. Es preferible, no obstante,
usar protocolos cifrados de comunicación, como SSH.
Programas para PC emuladores de los distintos modelos de
terminales existen muchos.
Debéis usar aquel que más os guste y que mejor sepáis
configurar. Aquí os doy tres ejemplos que os pueden servir
de guía para configurar cualquier otro programa, aunque os
recomiendo que uséis el primero, pues usa SSH en lugar de
TELNET.
- PuTTY (SSH) [Recomendado]
- Programa TELNET por defecto de
Windows NT 4.x y sucesivos
- Wyse Term (Aula de informática 5)
No se atenderán dudas relativas a la
presentación, falta de caracteres en español, descolocación de
las pantallas del editor de quienes utilicen programas emuladores
distintos o no los
configuren del modo antedicho. Trabajar con
una mala configuración entorpece la programación.
Si usáis linux, no necesitáis ningún
programa externo. Os basta con abrir un terminal y poner:
ssh -l vuestro_login_de_encina encina.fis.usal.es
La huella digital que os debe poner la primera vez que os
conectéis es:
4D:FE:15:17:09:18:41:30:2E:0A:E4:38:73:42:45:29
Verificar esta huella sirve para evitar ataques al protocolo
de seguridad encriptado ssh.
Encina usa la codificación iso8859-1 para tratar con los
caracteres propios del español (ñ, acentos, aperturas de
interrogación y exclamación, etc.). Vuestro Linux, sin
embargo, usa UTF-8, que es mejor. Para que podáis ver bien
estos caracteres en el terminal de Linux, cambiad
la codificación en el menú de "Preferencias", "Codificación",
"Europeo occidental (iso8859-1)"
Para poder realizar un FTP y subir o bajar ficheros al servidor,
debéis usar la versión segura del protocolo: sftp
.
En el caso de Windows, se puede usar PSFTP
, dentro
del directorio de PuTTY. Si hablamos de Linux, la orden que hay
que usar es:
sftp vuestro_login_de_encina@encina.fis.usal.es
Conexión con el servidor.
El servidor que usaremos para hacer las prácticas se llama
en Internet
encina.usal.es
.
En el programa emulador que uséis debéis indicar el nombre
del servidor para conectaros.
Teclead encina.usal.es
. Si no
lograrais conexión, probad con la dirección IP de encina, pues
puede que no funcione el servidor de nombres de la universidad.
Teclead en ese caso: 212.128.144.28
.
Os tiene que aparecer una pantalla parecida a esta:
###### # # #### # # # ##
# ## # # # # ## # # #
##### # # # # # # # # # #
# # # # # # # # # ######
# # ## # # # # ## # #
###### # # #### # # # # #
######## UNIVERSIDAD DE SALAMANCA ########
Departamento de Informática y Automática
Facultad de Ciencias
"Este ordenador sólo puede ser utilizado por personas debidamente autorizadas"
login:
Teclead ahora vuestro login y contraseña.
En el caso de que estéis usando el programa TELNET de
Windows NT 4.x, aumentad el tamaño de
la ventana de modo que no queden barras de desplazamiento ni
horizontales ni verticales en la ventana del terminal.
Configuración de vuestra cuenta en encina.
Para que la conexión con encina funcione correctamente, es
necesario
configurar bien vuestra cuenta.
Editad el fichero .profile
situado en el
directorio de conexión. Dicho fichero es de tipo "script" y
se ejecutará cada vez que os conectéis al servidor.
De las instrucciones que aparecen a continuación, aquellas
que sean nuevas las debéis
añadir en un lugar donde se vayan a ejecutar
(no las pongáis dentro de un if
, por ejemplo).
Aquellas que ya existan, debéis modificarlas
adecuadamente
(puede ser que alguna de ellas ya estén correctamente
en vuestra cuenta). Tened cuidado porque sólo tenéis que
copiar lo que aparece señalado en azul, no los signos de
puntuación que aparecen a continuación:
stty -istrip
(ojo con el signo menos):
sirve para que encina no
borre el bit más significativo de los caracteres que
le llegan por la entrada. Si los borrara, no podríais
teclear caracteres propios del español (ñ, acentos,...).
stty cs8
:
parecida razón que la anterior.
PATH=$PATH:/home/gyermo/bin:/usr/bin:/usr/ucb:/usr/ccs/bin:/etc:.
: añade a
los directorios de búsqueda de ejecutables (PATH) los
directorios /home/gyermo/bin
,
/usr/bin
, /usr/ucb
,
/usr/ccs/bin
, /etc
y el
directorio de trabajo. Colocadlo inmediatamente
después de la última modificación de la variable
PATH
.
export LANG=es_ES
:
esto servirá para que alguna información de errores
aparezca en español.
- Sólo en el caso de que uséis el programa TELNET de
Windows NT 4.x, añadid:
export LINES=25
export COLUMNS=79
Con esto le indicamos a encina el tamaño de nuestro
terminal
para que no nos desorganice la salida por pantalla.
set -o vi
:
para que la shell ksh nos
permita reusar las órdenes previamente tecleadas
para ganar en rapidez. Para hacer esto, pulsad la
tecla escape en la línea de órdenes y usad las
letras "h", "j", "k" y "l" para moveros por las
órdenes previas y las teclas del editor "vi" para
modificarlas.
export PS1='<ENCINA>$PWD$ '
:
para que
aparezca el nombre del directorio de trabajo en la
línea de órdenes.
alias vi=vim
: para
que usemos el editor vim
al teclear
vi
, que funciona mejor con las teclas del
cursor.
Probad si os funciona la tecla de borrado. Si no es así, volved
a editar el fichero .profile
. Añadid una línea que
diga: stty erase
<CTRL+V><TECLA_DE_RETROCESO>
.
Debéis pulsar primero las teclas "Control" y "V" y,
a continuación, pulsar la tecla de retroceso.
Comprobad que las modificaciones que habéis hecho funcionan
desconectándoos y volviéndoos a conectar para que se ejecute el
fichero .profile
. También podéis, alternativamente,
teclear exec login
.
Primera prueba.
Vamos a compilar un programa como si fuera una de las prácticas
que tenéis que realizar:
- Cread un directorio para la prueba y posicionaos en él:
mkdir PRUEBA
cd PRUEBA
.
- La mayoría de las prácticas no necesitarán más que un
fichero fuente, pero aquí consideraremos un caso más
general. El programa va a calcular la raíz cuadrada de
2. El primer fichero se llama
prueba.c
y
su contenido es el siguiente:
#include <stdio.h> /* Cabecera de la función printf */
#include "raiz.h" /* Cabecera de la función raiz_cuadrada */
int main(void)
{printf("La raíz cuadrada de 2 es %f.\n",
raiz_cuadrada(2.0));
}
Procurad que ninguna línea del programa pase de los 79
caracteres, es decir, que se pase a la siguiente línea,
y así se verá bien en cualquier lugar. El buen
sangrado de un programa ayuda bastante a su comprensión.
Aunque el estilo concreto depende del programador, os
recomiendo dar cuatro espacios por cada entrada de
sangrado. Además, os recomiendo que no sangréis con el
tabulador, sino con espacios. De este modo, sabréis que
vuestro programa se verá igual de bien en cualquier editor.
Mirad cómo un programa sencillo puede resultar
complicado de entender con un mal sangrado:
#include <stdio.h> /* Cabecera de la función printf */
#include "raiz.h" /* Cabecera de la función raiz_cuadrada */
int main(void) {printf(
"La raíz cuadrada de 2 es %f.\n", raiz_cuadrada
(2.0)); }
Este programa compilará igual de bien que la versión
anterior.
- El segundo fichero que compone el ejemplo se llamará
raiz.c
. Es el siguiente:
#include <math.h> /* Cabecera de la función sqrt */
float raiz_cuadrada(float x)
{return sqrt(x);
}
- El tercer fichero es un fichero con la cabecera de la
la función
raiz_cuadrada
. Se llama
raiz.h
.
float raiz_cuadrada(float);
Es un error bastante común incluir el código de las
funciones
en los ficheros de cabecera (.h
). Ahí sólo van
las cabeceras, como el propio nombre indica.
- Ahora tiene que haber tres ficheros en el directorio
PRUEBA
. Miradlo con ls -l
.
Ya tenemos los ficheros fuente que componen el programa. Ahora
es el momento de crear los ejecutables. Para ello, debéis crear
lo que se denomina un makefile
. Un
makefile
es un fichero que le dará al ordenador una serie de reglas
acerca de los pasos que ha de ejecutar para llegar a producir el
ejecutable. Para recordar lo que vimos en teoría acerca de los
makefiles, pulsa aquí.
Recordemos también los pasos que hay que dar para llegar al
ejecutable:

El orden en que se escriben las reglas en un fichero
makefile
no importa, salvo la
primera, que ha de ser la más general. Si observamos el dibujo,
la primera regla debe decir algo así como: "Para
obtener el fichero ejecutable prueba
,
necesito los ficheros objeto
prueba.o
y raiz.o
y lo
obtengo mediante la orden c89 prueba.o
raiz.o -o prueba -lm
". Esta regla, traducida al lenguaje
del fichero makefile
quedaría así:
prueba: prueba.o raiz.o
<TAB>c89 prueba.o raiz.o
-o prueba -lm
Observad cómo se añade -lm
a la orden de enlazado
porque el código de la función sqrt
no se
encuentra en raiz.o
, sino en la biblioteca del sistema
libm.a
. El código de la mayor parte de funciones
del C estándar se encuentra también en otra biblioteca de
funciones, la libc.a
pero, como excepción, no
hay que indicarle al enlazador que use esa biblioteca, lo
hace automáticamente.
El fichero makefile
completo queda así:
prueba: prueba.o raiz.o
<TAB>c89 prueba.o raiz.o
-o prueba -lm
prueba.o: prueba.c raiz.h
<TAB>c89 -c prueba.c
raiz.o: raiz.c
<TAB>c89 -c raiz.c
Tenemos, pues, cuatro ficheros en el directorio
PRUEBA
. Podemos decirle al ordenador que
compile el programa:
<T>/usuarios/gyermo/PRIVADO/SO/PRACTS/PRUEBA$ ls -l
total 8
-rw-r--r-- 1 gyermo profes 156 Feb 16 12:01 makefile
-rw-r--r-- 1 gyermo profes 316 Feb 16 11:17 prueba.c
-rw-r--r-- 1 gyermo profes 110 Feb 16 11:18 raiz.c
-rw-r--r-- 1 gyermo profes 29 Feb 16 11:18 raiz.h
<T>/usuarios/gyermo/PRIVADO/SO/PRACTS/PRUEBA$ make
c89 -c prueba.c
c89 -c raiz.c
c89 prueba.o raiz.o -o prueba -lm
Vemos que tenemos los ficheros objeto (.o
) y
el ejecutable, que ya podemos ejecutar:
<T>/usuarios/gyermo/PRIVADO/SO/PRACTS/PRUEBA$ ls -l
total 132
-rw-r--r-- 1 gyermo profes 135 Feb 16 12:29 makefile
-rwxr-xr-x 1 gyermo profes 61440 Feb 16 12:37 prueba
-rw-r--r-- 1 gyermo profes 314 Feb 16 12:32 prueba.c
-rw-r--r-- 1 gyermo profes 988 Feb 16 12:37 prueba.o
-rw-r--r-- 1 gyermo profes 77 Feb 16 12:37 raiz.c
-rw-r--r-- 1 gyermo profes 29 Feb 16 12:37 raiz.h
-rw-r--r-- 1 gyermo profes 764 Feb 16 12:37 raiz.o
<T>/usuarios/gyermo/PRIVADO/SO/PRACTS/PRUEBA$ prueba
La raíz cuadrada de 2 es 1.414214.
Lo bueno de los ficheros makefile
es que si
realizáis alguna modificación, el ordenador sólo realizará
las compilaciones necesarias. Probad a cambiar el fichero
prueba.c
para que calcule la raíz cuadrada de
3 y haced a continuación un make
.
Puede que, en
alguna circunstancia, deseéis que make
recompile
todo de nuevo. En ese caso, teclead touch *.c *.h
.
Esta orden actualiza la fecha de modificación de todos los
ficheros con extensiones .c
o .h
para que make
crea que los acabamos justo de
editar.
NOTA: Si no os funciona touch
,
probad con tocar
.
-
Argumentos y códigos de retorno de los programas.
Los programas que se lanzan en la shell pueden llevar argumentos
y devolver valores al sistema operativo. En el caso de la orden:
<T>/usuarios/gyermo/PRIVADO/SO/PRACTS/PRUEBA$ ls -l
total 132
-rw-r--r-- 1 gyermo profes 135 Feb 16 12:29 makefile
-rwxr-xr-x 1 gyermo profes 61440 Feb 16 12:37 prueba
[...]
diremos que el fichero ejecutable /bin/ls
se ha
ejecutado con un argumento (-l
). Diremos que
-l
es el argumento número 1 de esa ejecución.
Abusando del lenguaje, el propio nombre del fichero ejecutable
(en este caso ls
) se dice que es el argumento
número 0.
Los programas también devuelven un valor al sistema operativo.
Para conocer qué valor ha devuelto un programa, hay que dar
justo a continuación de haber ejecutado el
programa la orden echo $?
a la shell:
<T>/usuarios/gyermo/PRIVADO/SO/PRACTS/PRUEBA$ ls -l
total 132
-rw-r--r-- 1 gyermo profes 135 Feb 16 12:29 makefile
-rwxr-xr-x 1 gyermo profes 61440 Feb 16 12:37 prueba
[...]
<T>/usuarios/gyermo/PRIVADO/SO/PRACTS/PRUEBA$ echo $?
0
<T>/usuarios/gyermo/PRIVADO/SO/PRACTS/PRUEBA$ ls -l NoWomanNoCry.mp3
NoWomanNoCry.mp3 not found
<T>/usuarios/gyermo/PRIVADO/SO/PRACTS/PRUEBA$ echo $?
2
El valor devuelto por un programa ha de ser un número comprendido
entre 0 y 255. Es costumbre en UNIX que un valor de 0 signifique
ejecución exitosa y los valores mayores que cero indiquen error.
El prototipo de main
.
main
es la primera función que se ejecutará dentro
de un programa. Todo ejecutable debe contener una función
main
. El prototipo es variable, sin embargo. Si
definimos el prototipo de la función main
de la
manera siguiente, podemos saber qué argumentos nos ha pasado
el usuario en esta ejecución y devolver un valor al sistema
operativo:
int main(int argc, char *argv[]);
El valor que devolveremos al sistema operativo será justo ese
entero que devuelve la función main
. Los argumentos
los recibiremos en los parámetros con los que el sistema operativo
va a invocar a la función main
cuando comience la
ejecución del programa.
El primer parámetro es un entero e indica cuántos argumentos ha
tecleado el usuario, incluyendo el propio nombre del programa.
El segundo parámetro es un array de cadenas de caracteres de
longitud igual al número de argumentos y que contiene los propios
argumentos en sí.
Ejemplo: la función main
del programa ls recibe
los siguientes parámetros cuando un usuario teclea "ls
-l
":
Argumentos de main
para ls -l |
argc | 2 |
argv[0] | "ls" |
argv[1] | "-l" |
Nota: al ser argc
y argv
parámetros
de una función (la función main
), son variables
mudas, es decir, no tienen por qué llamarse así. Lo que importa
es que coincida su tipo. De todos modos, es costumbre en C
denominarlos así.
Las llamadas al sistema.
Manejar bien las llamadas al sistemas es un objetivo muy
muy importante en esta asignatura. Una llamada al sistema tiene
el aspecto de una función de C. Sin embargo, se trata de una
auténtica invocación al núcleo del sistema operativo para que
realice una tarea para nuestro programa. En cada sesión se
presentarán unas cuantas llamadas al sistema para vuestro estudio.
Para usar una llamada al sistema en vuestros programas sólo
debéis conocer el nombre de la llamada, qué parámetros hay que
pasarle, qué devuelve y el fichero de cabecera o los ficheros
de cabecera que tenéis que incluir en vuestro programa.
Las llamadas al sistema de cada sesión las podréis encontrar
aquí. Este fichero se os dejará el
día del examen. Es conveniente que lo manejéis con soltura. Para
una descripción detallada de lo que hace la llamada al sistema,
debéis consultar la página de manual de la llamada. Por ejemplo,
man -s 2 chmod
. El -s 2
viene de que
todas las llamadas
al sistema se encuentran en la sección 2 del manual.
Ejemplo: si queremos cambiar los permisos del fichero
pepe.txt
para que todo el mundo tenga acceso
de lectura y escritura a él, la llamada al sistema sería:
#include <sys/types.h>
#include <sys/stat.h>
[...]
chmod("pepe.txt",0666);
Todas las funciones de biblioteca de C (p. ej.
printf
), se basan internamente en las llamadas
al sistema y sus páginas de manual se encuentran en la sección
3c.
Condiciones de error.
Una llamada al sistema puede fallar. Por ejemplo, la llamada al
sistema del apartado anterior puede fallar porque el fichero
pepe.txt
no exista, porque no tengamos los permisos
adecuados para cambiar los permisos del fichero, etc. Es
responsabilidad del programador detectar cuándo se ha producido
un error. Normalmente sabemos que se ha producido un
error porque la llamada al sistema devuelve un valor concreto,
habitualmente -1 (menos uno). En cualquier caso, se puede
consultar el resumen o las páginas de
manual.
Por lo tanto, mejor que el código del apartado anterior
deberíamos poner algo así:
#include <sys/types.h>
#include <sys/stat.h>
[...]
if (chmod("pepe.txt",0666)==-1)
{fprintf(stderr,"No se puede cambiar los permisos.\n");
return 1;}
Para saber qué tipo de error se ha producido, debemos consultar
una variable global del sistema denominada errno
.
En la página de manual de la llamada al sistema con la que estemos
trabajando se nos dice qué
valores toma errno
según el error que se produzca.
En particular, en la página de manual de chmod podemos leer:
[...]
ERRORES
Si chmod() o fchmod() fallan, el modo del fichero sigue igual. A errno
se le asigna uno de los siguientes valores.
[...]
[ENOENT] Un componente de path o el fichero nombrado
por path no existe.
[...]
Por lo tanto, si queremos considerar ese error en concreto, el
código quedaría:
#include <sys/stat.h>
#include <errno.h>
[...]
if (chmod("pepe.txt",0666)==-1)
{if (errno==ENOENT)
fprintf(stderr,"El fichero pepe.txt no existe.\n");
else
fprintf(stderr,"Imposible cambiar los permisos debido a un error.\n");
return 1;}
Las acciones que el programa debe tomar si se produce un error
pueden variar. Puede ser que el programa se pueda recuperar del
error o puede que tenga que acabar su ejecución. En todo caso,
es el programador el que debe situarse en las circunstancias
de cada error y decidir. En el caso del ejemplo, decidimos
acabar el programa y devolver 1 al sistema operativo.
La función de biblioteca perror
.
Como considerar todos y cada uno de los posibles errores de una
llamada al sistema sería una labor muy tediosa y costosa, existe
una función de biblioteca de C que nos ahorra todas estas
comprobaciones. La función perror
comprueba
la variable errno
y escribe por el canal de error
estándar del programa una etiqueta que le pasamos como parámetro,
dos puntos y un mensaje indicativo del error que se ha producido.
La etiqueta suele hacer referencia al nombre del programa,
a la función dentro del programa o cualquier otra referencia
que sirva al programador para detectar en su código fuente
dónde se produjo el error. Aunque es una función muy útil,
hay veces que no aporta toda la información deseada.
El código del apartado anterior puede quedar así usando la
función perror
:
#include <sys/stat.h>
#include <stdio.h>
[...]
if (chmod("pepe.txt",0666)==-1)
{char etiqueta[80];
sprintf(etiqueta,"%s:chmod:pepe.txt",argv[0]);
perror(etiqueta);
return 1;}
Práctica.
Como primera práctica (que no hay que entregar), hay que realizar
un programa que admita el nombre de un fichero como argumento.
El programa debe cambiar el grupo al que pertenece el fichero
al grupo cuyo identificador de grupo es el 666. El programa
debe detectar e imprimir las codiciones de error y devolver
0 al sistema operativo si no se produjeron errores y 1 en caso
contrario. Notas: en caso de que el fichero sea un enlace
simbólico, debe cambiar el grupo del propio enlace, no de
aquello a lo que apunta. El propietario del fichero debe
permanecer inalterado.
Soluciones y comentarios.
Órdenes de la shell relacionadas.
chmod
- modifica los permisos de un fichero
chown
- cambia el propietario de un fichero
chgrp
- cambia el grupo de un fichero
ln
- hace un enlace duro o simbólico a un fichero
rm
- borra un fichero
ls
- lista el contenido de un directorio
Funciones de biblioteca relacionadas.
-
-
LPEs.
- El terminal se desorganiza, ¿qué puedo hacer?
Solución.
- Cuando ejecuto
make
, me ocurre algo parecido
a esto:
<T>/usuarios/gyermo/PRIVADO/SO/PRACTS/PRUEBA$ make
Make: Must be a separator on rules line 3. Stop.
Pista: Las apariencias engañan.
Solución.
- Yo edito mis ficheros. Yo los compilo mediante
make
. Y, cuando voy a ejecutar la primera
prueba de esta sesión, me aparece:
<ENCINA>/home/gyermo/PRUEBA$ prueba
ksh: prueba: no encontrado
Y yo es que alucino porque el fichero prueba sí está:
<ENCINA>/home/gyermo/PRUEBA$ ls
prueba prueba.c prueba.o raiz.c raiz.h raiz.o Makefile
¿Qué me está pasando?
Pista: Sólo el que busca, halla.
Solución.
-
Al ejecutar
make
sobre la práctica de esta
sesión, ocurre lo siguiente:
<T>/usuarios/gyermo/PRIVADO/SO/PRACTS/SESION1$ make
c89 -c sesion1.c
cc: "sesion1.c", line 5: error 1588: "UID_NO_CHANGE" undefined.
*** Error exit code 1
Stop.
Pista: Buscando una aguja en un pajar.
Solución.
- Cuando compilo la práctica me da el siguiente error:
<T>/usuarios/gyermo/PRIVADO/SO/PRACTS/SESION1$ cc sesion1.c -o sesion1
cc: "sesion1.c", line 3: error 1705: Function prototypes are an ANSI feature.
He mirado en la línea 3 de mi programa y sólo está el
prototipo de la función main
.
Solución.
- El programa que cambia el grupo de los ficheros funciona
bien. Sin embargo cuando meto:
cambia *
para que me cambie el grupo de todos los ficheros del
directorio actual, sólo me cambia el grupo de uno de
ellos.
Solución.
He hecho la práctica de la sesión,
la he repasado mil veces, pero resulta que cuando la
ejecuto me da el error:
<ENCINA>/home/gyermo$ cambia pepe
lchown:pepe: Not owner
Solución.
Al compilar la práctica desde
Linux o Solaris, me dice que no reconoce la macro
UID_NO_CHANGE
. ¿Le han hecho un
"trabajo" al compilador y he de acudir a la
Pitonisa Lola?
Solución.
Ejecuto la práctica en Linux
y aparece el siguiente error:
~$ cambia pepe.txt
./cambia:chmod:pepe.txt: Operation not permitted
Solución.