Bonjour,

tout d'abord je m'excuse pour l'absence d'accents dans ce message, j'utilise un clavier qwerty

Je developpe une bibliotheque destinee a Linux noyau 2.6, utilisant des futex.

Je stock mes futex dans un segment de memoire partagee, cree avec les IPC SYSV
(shmget, shmat).

Je souhaite controler le nombre de processus attaches au segment, par l'appel a shmctl(shmid,IPC_STAT,&shds), shds est une struct shmid_ds qui contiendra toutes les informations relatives au segment.
Mon probleme est que le comportement de l'appel a shmctl(shmid,IPC_STAT,&shds)
varie si je l'appelle dans une bibliotheque ou dans un programme (de test).

Si je l'appelle dans le programme, tout est correct, les champs de la
structure sont bien renseignes.

Si je l'appelle dans la bibliotheque, les champs contiennent des valeurs
incorrectes.

Initialiser (avec memset) ou non la structure shmid_ds avant l'appel ne regle
pas le probleme, juste que la structure contient toujours, apres l'appel a
shmctl, les valeurs de l'initialisation.

shmctl(...,IPC_STAT,...) n'arrive pas a recuperer les donnees
concernant le segment de memoire partage, mais l'appel a shmctl(...,IPC_RMID,...)
fonctionne correctement et marque bien le segment comme etant a liberer prochainement

J'en conclus que shmctl n'ecrit rien dans la variable locale a la fonction de
la bibliotheque ... mais je ne vois pas pourquoi ... un probleme de droits?
[edit] en deboguant davantage j'ai realise (de nouveau) que 3 champs sont renseignes incorrectement : la taille, la date de dernier attachement, et un unused),
donc shmctl ecrit bien dans la variable locale, mais des informations peu coherentes, et j'ai toujours plus de mal a comprendre ...

Quelqu'un sait il pourquoi cet appel systeme se comporte comme ca,
differemment en fonction de "l'endroit" ou on l'appelle? Et j'aimerai comprendre comment
regler le probleme?


Voici le programme de test, il appelle une fonction de la bibliotheque pour
demander un futex, celle ci cree le segment de memoire, initialise le futex et
renvoit un pointeur.
Ensuite le programme fait quelques operations idiotes car il est sense
controler que tout se passe bien niveau synchronisation de differents
processus.
Enfin, lorsque le programme se termine, la fonction _free_shm() est appelee
automatiquement (grace a on_exit(...)), elle marque le segment comme etant a liberer si le processus courant est le dernier a l'utiliser.

Resoudre mon probleme avec shmctl me permettrai de reduire la taille du futex.


Programme de test :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include "exclu.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>

#include <sys/ipc.h>
#include <sys/shm.h>



int
main (int argc, char *argv[])
{
  struct futex_j *sem;
  unsigned int i;
  int ret;

  struct shmid_ds shds;

  struct timeval tp;

  fprintf (stderr, "GO");

  if (argc != 2)
    {
      fprintf (stderr, "Usage: testshmid_ds <iterations>\n");
      exit (1);
    }

  sem = lib_get_futex (IDBS);



  if (shmctl (sem->shmid, IPC_STAT, &shds))
    {
      perror ("IPC error : shmctl");
      exit (1);
    }

  fprintf (stderr, "DEBUG :: IPC_STAT : %u \n", shds.shm_nattch);

  fprintf (stderr,
           "DEBUG %d : shm_nattch = %d, size = %d, pid last shmop = %d, pid
creator = %d\n",
           getpid (), shds.shm_nattch, shds.shm_segsz, shds.shm_lpid,
           shds.shm_cpid);


  for (i = 0; i < atoi (argv[1]); i++)
    {
      int j;
      struct shmid_ds shds;

      if (shmctl (sem->shmid, IPC_STAT, &shds))
	{
	  perror ("IPC error : shmctl");
	  exit (1);
	}

      fprintf (stderr, "DEBUG :: IPC_STAT : %u \n", shds.shm_nattch);

      for (j = random () % 1024; j > 0; j--);


      fprintf (stderr, "BEFORE SEM DOWN %iv, sem->count = %d\n", getpid (),
	       sem->count);
      if((ret=lib_futex_lock (sem)))
	  {
	  	switch(ret) {
			case -1 : fprintf(stderr,"DEBUG %d : woken by a
signal, we continue\n",getpid());
					  break;
			case 1 : fprintf(stderr,"DEBUG %d : timed out, we
continue\n",getpid());
					 break;
			case 2 : fprintf(stderr,"DEBUG %d : Would have been
blocked, we continue\n",getpid());
					 break;
		}
	  }
      fprintf (stderr, "AFTER SEM DOWN %iv, sem->count = %d\n", getpid (),
	       sem->count);
      usleep (3);


      fprintf (stderr, "DEBUG %d : who got the futex ? %d\n", getpid (),
	       sem->pid);


      gettimeofday (&tp, NULL);
      fprintf (stderr, "DEBUG :: %d, I have the resource at %d.%d\n",
	       getpid (), tp.tv_sec % 60, tp.tv_usec % 1000);
      fprintf (stdout, "READ \n");
      sleep (5);


      fprintf (stderr, "BEFORE SEM UP %i^, sem->count = %d\n", getpid (),
	       sem->count);
      if(lib_futex_unlock (sem)) 
		  fprintf(stderr,"ERROR %d : we should wake up processes\n");


      fprintf (stderr, "AFTER SEM UP %i^, sem->count = %d\n", getpid (),
	       sem->count);
    }

  if (shmctl (sem->shmid, IPC_STAT, &shds))
    {
      perror ("IPC error : shmctl");
      exit (1);
    }

   fprintf (stderr,
           "DEBUG %d : In Main Function : shm_nattch = %d, size = %d, pid last
shmop = %d, pid creator = %d\n",
           getpid (), shds.shm_nattch, shds.shm_segsz, shds.shm_lpid,
           shds.shm_cpid);


  exit (0);
}

Header de la bibliotheque :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#ifndef __MY_LIB_FUTEX_H__
#define __MY_LIB_FUTEX_H__



#include <linux/futex.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include "i386.h"

#include <linux/unistd.h>

#include <asm/unistd.h>
#include <sys/syscall.h>

/** same values as the enable flags in glue.h */
#define IDBS 0x00
#define IDJTAG 0x20

/** number of futexes wished */
#define NBLIB 8

/** time out in seconds */
#define TOS		1
/** time out in nanoseconds */
#define TONS	0

#define __NR_sys_futex __NR_futex

/**
 * the futex, it needs some informations for my implementation 
 */
struct futex_j
{
  /** 
   * the most important variable, it is the value of the futex, 
   * the one we increment and decrement and test
   */
  int count;     
  
  /**
   * boolean to know if the futex has already been initialized
   */
  int init_done; // a boolean to know if the futex has already been
initialized

  /**
   * the shared memory identifier, it is needed to free the shm 
   * once we don't need it anymore, and it's useless in 
   * the other parts of the implementation
   */
  int shmid;     


  /**
   * the pid of the process who has locked the futex
   * may be used to prevent deadlocks
   */
  int pid; 		 
				 
				 
  /**
   * the number of process attached to the shared memory, should 
   * be useless has with the shmid and shmctl(...,IPC_STAT,...)
   * we can get this information, 
   * however, it seems shmctl does not work correctly when it is
   * used in a shared library, I do not know why
   */				 
  int nattch; 	 
};


/******************************************************************************/
/*                                                                                               */
/*                       Functions for the user                                        */
/*                                                                                               */
/******************************************************************************/

/* 
 * Get you a futex, shared in a shared memory, 
 * associated with an identifier id, with the same id you get the same futex 
 */
/* XXX 
   Notice that this function make a new attachment to the shared memory,
   I do not find this elegant but it is the only way I found to get rid of 
   global variables. However, we can attach 2^32 process to a shared memory
...
 */
struct futex_j *lib_get_futex (int id);

/* Unlocks the futex */
int lib_futex_unlock (struct futex_j *futx);
	
/* Locks the futex */
int lib_futex_lock (struct futex_j *futx);




/******************************************************************************/
/*                                                                                               */
/*                    Internal functions, should be static                         */
/*                                                                                               */
/******************************************************************************/


/* the system call, used only internally */
static int sys_futex (int *, int, int, struct timespec *);
	

/* the function which init the futex, used only internally 
   but the keyword static give me some troubles
 */
void lib_init_futex (struct futex_j *futx);


/* Get a shared memory according to the identifier, and return
   an uninitialized futex
 */
struct futex_j *get_shm (int id);
	
/* Destroy the shared memory */
int free_shm (void *shm);

/* The way to destroy correctly the shared memory on exit of a process*/
void _free_shm (int status, void *arg);

#endif /*__MY_LIB_FUTEX_H__*/
Source de la bibliotheque :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
#include "include/exclu.h"

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#include <limits.h>




/**
 *
 * Library to share IPCs between other libraries to disallow
 * their users to write at the same place of another one
 * 
 * @author jc
 *
 */

/**
 * 
 * Get you a futex, shared in a shared memory, 
 * associated with an identifier id, with the same id you get the same futex 
 * \return 0 : everything OK
 * \return -1 : error occured
 *
 */
struct futex_j *
lib_get_futex (int id)
{
  struct futex_j *ftx;
  struct shmid_ds shds;
  ftx = get_shm (id);
  if (ftx < 0)
    {
      perror ("libexclu : get_shm");
      return (struct futex_j *) -1;
    }
  lib_init_futex (ftx);

    memset(&shds,0,sizeof(struct shmid_ds));
  if (shmctl (ftx->shmid, IPC_STAT, &shds))     {
      perror ("IPC error : shmctl");
      exit (errno);
    }

  fprintf (stderr,
           "DEBUG %d : In Library : shm_nattch = %d, size = %d, pid last shmop
= %d, pid creator = %d\n",
           getpid (), shds.shm_nattch, shds.shm_segsz, shds.shm_lpid,
shds.shm_cpid);



  return ftx;
}


/**
 * 
 * Get a shared memory according to the identifier, and return
 * an uninitialized futex
 * \return 0 : everything OK 
 * \return -1 : error occured
 *
 */
struct futex_j *
get_shm (int id)
{
  key_t cle;
  void *shm;
  int shmid;

  char cond = 0;

  struct futex_j *futx;

  struct shmid_ds shds;

  cle = ftok ("/tmp", id);
  if (cle == -1)		//error
    {
      perror ("IPC error : ftok");
      return (struct futex_j *) -1;
    }

  // try to create a shm
  shmid =
    shmget (cle, NBLIB * sizeof (struct futex_j),
	    IPC_CREAT | IPC_EXCL | 0666);
  if (shmid == -1)
    {
      perror ("IPC error : shmget");
      //exit(errno);

      // if shm already exists, we just open it
      shmid = shmget (cle, 4 * sizeof (struct futex_j), 0666);
      if (shmid == -1)
	{
	  perror ("IPC error : shmget");
	  return (struct futex_j *) -1;
	}
    }
  else
    cond = 1;





  // get attached to the shm
  if ((shm = shmat (shmid, NULL, 0666)) == (void *) -1)
    {
      perror ("IPC error : shmat");
      return (struct futex_j *) -1;
    }

  fprintf (stderr, "DEBUG %d : shmid %d, shm %x\n ", getpid (), shmid, shm);

  
  futx = shm;
  
  // can cause errors, as the futex is shared and no control is done when we
  // write ...
  futx->shmid = shmid;
  if (cond)
    {
      futx->init_done = 0;
      futx->nattch = 0;
    }
  ++(futx->nattch);

    memset(&shds,0,sizeof(struct shmid_ds));
  if (shmctl (shmid, IPC_STAT, &shds))     {
      perror ("IPC error : shmctl");
      exit (errno);
    }

  fprintf (stderr,
           "DEBUG %d : In Library : shm_nattch = %d, size = %d, pid last shmop
= %d, pid creator = %d\n",
           getpid (), shds.shm_nattch, shds.shm_segsz, shds.shm_lpid,
shds.shm_cpid);




  if (on_exit (_free_shm, (void *) futx))
    perror ("ERROR : on exit shall not free the shm :");

  return futx;
}

/**
 * Free the shared memory
 * \return 0 : everything OK
 * \param shmid : shared memory identifier (redundant, it is also in the shm
 * ...
 * \param shm : shared memory
 */
int
free_shm ( void *shm)
{

  struct shmid_ds shds;
  struct futex_j *futx = shm;
  fprintf (stderr, "DEBUG %d : shmid = %d, shm = %x \n", getpid (),
futx->shmid,
	   shm);


  fprintf (stderr, "DEBUG %d : futx->nattch = %d\n", getpid (), futx->nattch);

/** XXX 
 * A matter is that the shm_nattch contains strange values
 * ipcs shell command returns the good value however
 */

/**
 *	IPC_RMID allows the segment to be destroyed once the last process is
 *	distached, i.e. once shm_nattch is 0
 *	therefore, no need to check by myself
 *  the caller should be the creator of the segment
 *	so if everybody calls it, the caller will do
 *
 *  once it has been called, no new process can get attached to the shm
 *  if so, they make another segment, .... this is not ... cool
 */

  memset(&shds,0,sizeof(struct shmid_ds));
  if (shmctl (futx->shmid, IPC_STAT, &shds))
    {
      perror ("IPC error : shmctl");
      exit (errno);
    }

  fprintf (stderr,
	   "DEBUG %d : In Library : shm_nattch = %d, size = %d, pid last shmop
= %d, pid creator = %d\n",
	   getpid (), shds.shm_nattch, shds.shm_segsz, shds.shm_lpid,
	   shds.shm_cpid);

  // can cause errors, as the futex is shared and no control is done when we
  // write ...
  /* check if some process are already attached to the segment */
  if (--(futx->nattch) == 0)
    {
      // mark the shm as destroyable and then distach the shm
      if (shmctl (futx->shmid, IPC_RMID, NULL))
	{
	  perror ("IPC error : shmctl");
	  exit (errno);
	}
    }

  
  if (shmdt (shm))
    {
      perror ("IPC error : shmdt");
      exit (errno);
    }

  return 0;
}

/**
 * Destroy the shared memory on exit (called at a normal end of the program) 
 * \param status : the status of the exit
 * \param arg : here, the shared memory
 */
void
_free_shm (int status, void *arg)
{

  if (free_shm (arg))
    {
      perror ("free_shm");
      exit (errno);
    }
}


_syscall4 (int, sys_futex,
	   int *, futex, int, op, int, val, struct timespec *, rel);


/**
 * Initialize the futex
 *
 * \param futx : the futex to initialize		  
 * \return 0 : everything OK 
 * \return 1 : error occured
 *
 */
void
lib_init_futex (struct futex_j *futx)
{
  fprintf (stderr, "DEBUG %d : init_done = %d\n", getpid (), futx->init_done);
  if (futx->init_done)
    {
      fprintf (stderr, "DEBUG %d : %d made the init\n", getpid (),
	       futx->init_done);
      return;
    }
  futx->init_done = getpid ();
  fprintf (stderr, "DEBUG %d : I made the init\n", getpid ());
  futx->count = 1;
  futx->pid = -1;

}

/**
 * Lock the futex
 *
 * \param futx : the futex to lock
 * \return 0 : everything OK
 * \return 1 : lock timed out
 * \return 2 : process would have been blocked
 * \return -1 : process woken by a signal
 */
int
lib_futex_lock (struct futex_j *futx)
{
  struct timespec timeout={1,0}; //arbitrary, study the best time
	 
  fprintf (stderr, "DEBUG %d :: Before atomic dec : count = %d\n", getpid (),
	   futx->count);
//      if(futx->pid!=getpid())
//        {
  while (__futex_down (&(futx->count)))	//count!=0 after decrementation
    //so count was already 0 or -1
    {
      futx->count = -1;		//count should always be -1 if we're
      //going to wait
      __futex_commit ();
      fprintf (stderr, "DEBUG %d :: futex : %d, Process waiting\n", getpid (),
	       futx->count);
      if (sys_futex (&(futx->count), FUTEX_WAIT, -1, &timeout) != 0)	// not
woken
	{
	  switch (errno)
	    {
	    case ETIMEDOUT:	//time out occured
		  fprintf(stderr,"DEBUG %d : locked timed out\n",getpid());
	      return 1;
	    case EWOULDBLOCK:	//count != val (-1)
	      perror ("ERROR : sys_futex");
		  fprintf(stderr,"DEBUG %d : process would have been
blocked\n",getpid());
	      return 2;
	    case EINTR:	//woken by a signal
	      perror ("ERROR : sys_futex");
		  fprintf(stderr,"DEBUG %d : process woken by a
signal\n",getpid());
	      return -1;

	    }
	}
      else			// woken
	{
	  fprintf (stderr, "DEBUG %d :: futex : %d, I'm woken\n", getpid (),
		   futx->count);

	}

    }				//__futex_down
  //here, we locked the futex
  futx->pid = getpid ();

  fprintf (stderr, "DEBUG %d :: After atomic dec : count = %d\n", getpid (),
	   futx->count);
//        }
//      else
//        {
//          fprintf(stderr,"DEBUG %d :: We already had the futex so we did
//          nothing\n",getpid());
//        }
  return 0;
}


/**
 * Unlock the futex, only the locker can do that, yet
 *
 * \param futx : the futex to unlock		  
 * \return 0 : everything OK
 *
 */
int
lib_futex_unlock (struct futex_j *futx)
{
  if (futx->pid == getpid ()) // only the locker can unlock
    {
      if (!__futex_up (&(futx->count)))	// not from 0 to 1, process are
waiting
	{
	  futx->count = 1;
	  __futex_commit ();

	  // process are waiting, wake them all up
	  if (sys_futex (&(futx->count), FUTEX_WAKE, INT_MAX, NULL) == 0)
	    {
	      fprintf (stderr,
		       "ERROR %d : we should wake up at least one process\n",
		       getpid ());
	      return -1;
	    }
	  return 0;
	}
    }
  return 0;
}
J'espere avoir ete clair
Merci d'avance pour toute l'aide que vous pouvez m'apporter.