Université BORDEAUX 1 U.F.R. Mathématiques et Informatique
Epreuve de J.Bétréma 20 septembre 2000


Maîtrise d'Informatique
Architecture Unix-NT

Durée: 3 heures
Tous documents interdits.

1. Mémoire

Voici un programme C :
#include <unistd.h>
#include <stdlib.h>

void *allouer (int n) {
  void *p = malloc (n);
  void *b = sbrk (0);
  printf ("malloc : %u (0x%x) brk : %u (0x%x)\n", p, p, b, b);
  return p;
}

void liberer (void *p) {
  void *b;
  free (p);
  b = sbrk (0);
  printf ("   brk : %u (0x%x)\n", b, b);
}

void main (void) {
  void *p, *q;
  allouer (1000);
  p = allouer (10000);
  q = allouer (100);
  liberer (p);
  liberer (q);
  allouer (500);
  getchar ();
}
et le résultat de son exécution :
malloc : 134519000 (0x80498d8) brk : 134520832 (0x804a000)
malloc : 134520008 (0x8049cc8) brk : 134533120 (0x804d000)
malloc : 134530016 (0x804c3e0) brk : 134533120 (0x804d000)
   brk : 134533120 (0x804d000)
   brk : 134533120 (0x804d000)
malloc : 134520008 (0x8049cc8) brk : 134533120 (0x804d000)     

Pendant que le programme est bloqué en fin d'exécution, en attente d'un caractère, on entre les commandes suivantes :

+serveur2-1:~$ cd /proc

+serveur2-1:/proc$ ps -all | grep allouer
  F S   UID   PID  PPID  C PRI  NI ADDR    SZ WCHAN  TTY          TIME CMD     
000 S  1006 32469 32021  0  60   0    -   271 read_c pts/20   00:00:00 allouer

+serveur2-1:/proc$ cd 32469

+serveur2-1:/proc/32469$ cat maps
08048000-08049000 r-xp 00000000 00:05 1699755    /.automount/raid3/export/home/
betrema/systemes/allouer
08049000-0804a000 rw-p 00000000 00:05 1699755    /.automount/raid3/export/home/
betrema/systemes/allouer
0804a000-0804d000 rwxp 00000000 00:00 0
40000000-40012000 r-xp 00000000 08:02 256600     /lib/ld-2.1.2.so
40012000-40013000 rw-p 00012000 08:02 256600     /lib/ld-2.1.2.so
40013000-40015000 rw-p 00000000 00:00 0
4001a000-40105000 r-xp 00000000 08:02 256607     /lib/libc-2.1.2.so
40105000-40109000 rw-p 000ea000 08:02 256607     /lib/libc-2.1.2.so
40109000-4010d000 rw-p 00000000 00:00 0
bfffe000-c0000000 rwxp fffff000 00:00 0

+serveur2-1:/proc/32469$       
  1. Expliquer les différents champs (du moins ceux que vous reconnaissez) retournés par la commande ps pour ce processus.
  2. En analysant le résultat de la commande cat maps, donner une description aussi précise que possible des différentes régions de la mémoire du processus en cours d'exécution.
  3. Expliquer les commandes (cd /proc, etc. ) qui ont permis d'obtenir cette carte des régions.
  4. L'écart entre les pointeurs retournés par les deux premiers appels à la fonction allouer est de 1008 octets : pourquoi ?
  5. On rappelle que sbrk(0) retourne l'adresse de la fin du segment de données dynamiques ; pourquoi cette adresse n'est-elle pas modifiée lors de la troisième allocation de mémoire ? lors de la libération de mémoire ?
  6. Pourquoi la quatrième (et dernière) allocation de mémoire retourne-t-elle la même adresse que la seconde ?
  7. On suppose qu'on ajoute un nouvel appel allouer (x) en fin de programme. Quelle est, à votre avis, la plus petite valeur de x qui modifiera l'adresse de la fin du segment de données dynamiques ?

2. Programmation multi-thread

Voici des extraits d'un programme NT, qui réalise le squelette d'un serveur multi-thread. Expliquer son architecture, et commenter quelques instructions fondamentales que vous choisirez.

Note : d'une part ce programme est incomplet, d'autre part on ne peut envisager, dans le cadre de l'examen, son analyse ligne par ligne. Votre réponse doit être soigneusement rédigée, et sera jugée sur sa clarté et son intelligence. La répétition en pseudo-français des instructions du programme ne rapportera aucun point.

typedef struct {
	HANDLE hNamedPipe;		/* Named pipe instance. */
	DWORD ThreadId;
	TCHAR TmpFileName [MAX_PATH];
} THREAD_ARG;
typedef THREAD_ARG *LPTHREAD_ARG;

main (int argc, LPTSTR argv [])
{
	/* MAX_CLIENTS is defined in ClntSrvr.h. */

	THREAD_ARG ThArgs [MAX_CLIENTS];
	HANDLE hNp, hSrvrThread [MAX_CLIENTS], hMonitor;
	DWORD iNp, MonitorId;

	NumServers = MAX_CLIENTS;

	for (iNp = 0; iNp < MAX_CLIENTS; iNp++) {
		hNp = CreateNamedPipe ( SERVER_PIPE, PIPE_ACCESS_DUPLEX,
			PIPE_READMODE_MESSAGE | PIPE_TYPE_MESSAGE | PIPE_WAIT,
			MAX_CLIENTS, 0, 0, INFINITE, NULL);
		if (hNp == INVALID_HANDLE_VALUE)
			ReportError ("Failure to open named pipe.", 1, TRUE);
		ThArgs [iNp].hNamedPipe = hNp;
		GetTempFileName (".", "CLP", 0, ThArgs [iNp].TmpFileName);
		hSrvrThread [iNp] = (HANDLE) CreateThread (NULL, 0, Server,
				&ThArgs [iNp], 0, &ThArgs [iNp].ThreadId);
		if (hSrvrThread [iNp] == NULL)
			ReportError ("Failure to create server thread.", 2, TRUE);
	}
	
	WaitForMultipleObjects (MAX_CLIENTS, hSrvrThread, TRUE, INFINITE);
	for (iNp = 0; iNp < MAX_CLIENTS; iNp++)
		CloseHandle (hSrvrThread [iNp]);
	WaitForSingleObject (hMonitor, INFINITE);
	CloseHandle (hMonitor);

	return 0;
}

static DWORD WINAPI Server (LPTHREAD_ARG pThArg)
{
	HANDLE hNamedPipe, hTmpFile;
	DWORD nXfer;
	TCHAR ShutRqst [] = "$ShutDownServer";
	STARTUPINFO StartInfoCh;
	SECURITY_ATTRIBUTES TempSA = {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
	PROCESS_INFORMATION ProcInfo;
	FILE *fp;
	REQUEST Request;
	RESPONSE Response;

	GetStartupInfo (&StartInfoCh);
	hNamedPipe = pThArg->hNamedPipe;

	/* Wait for a connection. */

	ConnectNamedPipe (hNamedPipe, NULL);

	while (!ShutDown) {
		hTmpFile = CreateFile (pThArg->TmpFileName,
            	GENERIC_READ | GENERIC_WRITE,
			FILE_SHARE_READ | FILE_SHARE_WRITE, &TempSA,
			CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

		while (!ReadFile (hNamedPipe, &Request, RQ_SIZE, &nXfer, NULL)) {
			DisconnectNamedPipe (hNamedPipe);
			ConnectNamedPipe (hNamedPipe, NULL);
		}

		sscanf (Request.Record, "%s", Command);

		if (strcmp(Command, ShutRqst) == 0)		/* Terminate the system. */
			ServerStats (NULL, CS_TERMTHD);
		else {
			StartInfoCh.hStdOutput = hTmpFile;
			StartInfoCh.hStdError = hTmpFile;
			StartInfoCh.dwFlags = STARTF_USESTDHANDLES;

			if (!CreateProcess (NULL, Request.Record, NULL,
					NULL, TRUE, /* Inherit handles. */
					0, NULL, NULL, &StartInfoCh, &ProcInfo)) {
				PrintMsg (hTmpFile, "Server could not create process."));
				ProcInfo.hProcess = NULL;
			}
			else { 
				CloseHandle (ProcInfo.hThread);
				WaitForSingleObject (ProcInfo.hProcess, INFINITE);
				CloseHandle (ProcInfo.hProcess);
			}
		}
		
		CloseHandle (hTmpFile);

		/* Respond a line at a time. It is convenient to use
			C library line-oriented routines at this point. */

		fp = fopen (pThArg->TmpFileName, "r");
		if (fp == NULL)
			perror ("Failure to open command output file.");
		Response.Status = 0;
		while ((fgetts (Response.Record, MAX_RQRS_LEN, fp) != NULL)) 
			WriteFile (hNamedPipe, &Response, RS_SIZE, &nXfer, NULL); 
		fclose (fp);

		/* Send an end of response indicator. */

		Response.Status = 1;
		WriteFile (hNamedPipe, &Response, RS_SIZE, &nXfer, NULL);

	}   /* End of main command loop. */

	if (!DisconnectNamedPipe (hNamedPipe) || !CloseHandle (hNamedPipe) || 
		!DeleteFile (pThArg->TmpFileName))
		ReportError ("Error shutting down server.", 0, TRUE);
	ExitThread (0);
	return 0;
}