Problem with processes and memory reserve

4

I have the following exercise:

Perform a program that expands N child processes. Each child must share a variable called counter, which must be initialized to zero. This variable must be incremented by each thread 100 times. Print the variable once the threads are finished and analyze the result obtained. A predictable result for 3 processes would be 300.

I have the following code but I can not get the sum correctly, I think the problem is in the memory reserve.

#define CHILDREN    3
#define OK  7


int suma;

void adder(int);

int main()
{

    int i,j, shmid, status;
    key_t key;

    extern int suma ;
    struct shmid_ds buf;

   //  Shared memory
    key = ftok("adicional2.c", 1);
    if ((shmid = shmget(key, sizeof(int), IPC_CREAT | 0777)) == -1){

    exit(1);
    }
        suma=(int) shmat(shmid,NULL,0);
        suma=0;



    // creacion hilos
    for (i = 0; i < CHILDREN; i++) {

    pid_t pid = fork();
    if (pid==0) {//cuando fork es igual de 0

        adder(i);
        printf("%d",suma);

        exit(0);
    }

    }

    // Wait to finish
    for (i = 0; i < CHILDREN; i++) {
    pid_t pid = wait(&status);

    printf("\nHijo %d ha terminado status %d\n", pid, WEXITSTATUS(status));
    }

   // Final result
  printf("\nSuma");

    printf("[%d]",suma);



printf("\n");

}

void adder(int id)
{

int i,shmid;

extern int suma ;

    key_t key;
   //  Shared memory
    key = ftok("adicional2.c", 1);
    if ((shmid = shmget(key, sizeof(int), IPC_CREAT | 0777)) == -1)

        perror("Child");




//Creacion de hilos



            if(id==0){//proceso 1 fila 1
                suma+=100;
                printf("suma hijo 1= %d",suma);
                exit(OK);
            }


            if(id==1){//proceso 2 fila 2{

                suma+=100;
                printf("suma hijo 2=%d",suma);
                exit(OK);
            }
            if(id==2){  //proceso 3 fila 3{

                suma+=100;
                printf("suma hijo 3=%d",suma);
                exit(OK);           
            }


}

As I say, I think the failure is from the memory reserve, since each child returns 100 but does not add it to the previous one.

    
asked by juanma 18.12.2018 в 21:25
source

1 answer

3

You have several failures:

  • You do not need any global variable in your program. Both main() as adder() (and therefore each of the children) have their own variable suma .
  • Now, those variables are not int , but pointer to int and what you have to do to share the data is to have them all point to the same address of physical memory.
  • Since each process has its own virtual address space, it is impossible to make a pointer in one process point to a variable in another process. How to do it then? Well precisely thanks to shmget() , which creates a memory area in the system that can then be shared between processes. Once that zone is created, the creator receives an entire identifier ( shmid ) that must be used to obtain, through shmat() , a pointer (in the virtual space of the process that obtains it) that points to the zone shared in the system. It is like a "little window" in the virtual address space of each process that allows "lean" to the system's memory. The "little windows" of all the processes point to the same piece of memory of the system and they share it, but it must be through pointers.
  • This implies that each child must know the value of shmid to be able to map it in their own address space. A simple way is to pass that value as a parameter (although only the father creates it).
  • Therefore, the part where you call shmget() from the children remains. These should only do shmap() , on the shmid that happens to them the father.
  • Each child must increase the amount 100 times. I guess that means in a loop that is repeated 100 times, and not adding 100 as you have done.
  • The code of the three children is identical, so I do not see the need to make a if to distinguish which of the children it is. You can therefore simplify your code.

There is an additional failure related to the concurrence, but at the moment I will not enter it. With all the above said the program would be like this:

#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<stdio.h>
#define CHILDREN    3
#define OK  7

void adder(int,int);
// Desaparece la variable suma global

int main()
{
    int i,j, shmid, status;
    key_t key;

    int *suma ;       // La suma en el padre es un puntero
    struct shmid_ds buf;

    //  El padre crea la zona de memoria compartida en el sistema
    key = ftok("adicional2.c", 1);
    if ((shmid = shmget(key, sizeof(int), IPC_CREAT | 0777)) == -1){
         exit(1);
    }
    // Pero debe mapearla en su propio espacio de direcciones
    suma=(int *) shmat(shmid,NULL,0);

    // Ahora ya podemos ponerla a cero, pero recuerda que es un puntero
    *suma=0;

    // creacion hilos
    for (i = 0; i < CHILDREN; i++) {
        pid_t pid = fork();
        if (pid==0) {//cuando fork es igual de 0
            // Esto lo hará cada hijo
            adder(shmid, i);     // Le pasamos el id de la zona compartida
            exit(0);             // y termina aqui
        }
    }

    // Esperar a que los tres hijos acaben
    for (i = 0; i < CHILDREN; i++) {
        pid_t pid = wait(&status);
        printf("\nHijo %d ha terminado status %d\n", pid, WEXITSTATUS(status));
    }

    // Imprimir resultado final, recordemos que es puntero
    printf("\nSuma [%d]\n",*suma);
}

// Esta es la función que ejecutará cada hijo
void adder(int shmid, int id)
{
    // Cada hijo tiene su propio puntero a la zona compartida
    int *suma ;

    // que hay que mapear al espacio de direcciones del hijo
    suma=(int *) shmat(shmid,NULL,0);

    // Ahora el bucle que incrementa 100 veces la suma
    for (int i=0; i<100; i++ ) {
        *suma+=1;
    }
    // Este hijo imprime su resultado
    printf("suma hijo %d= %d\n",id, *suma);
    // exit(OK);  // sobraba este exit, pues ya se hace luego en main
}

If you compile and run this program you will see:

suma hijo 0= 100
suma hijo 1= 200
Hijo 19226 ha terminado status 0
Hijo 19227 ha terminado status 0
suma hijo 2= 300
Hijo 19228 ha terminado status 0
Suma [300]

So it worked.

However, if you execute it millions of times, you will find some where the result is not 300. This may happen because the three children are running at the same time and modifying at the same time the same area of the memory, but the operation:

*suma += 1

is not "atomic" (it does not take place in a single instruction, although in C it is a single line). Its execution looks more like this other:

int temporal;
temporal = *suma;
temporal = temporal + 1;
*suma = temporal;

so it could be the case that a child reads *suma , finding for example 10, and while he is adding 1 to his temporal , another child reads also *suma and find still 10. Both children will arrive to the result 11 , and they will save it (first one and then the other) in the shared zone, with which instead of increasing in two, it will have increased only in 1.

This is unlikely, but it can happen. And therefore technically the program is incorrect. Fixing it involves using traffic lights or locks, which are another type of shared object managed by the operative and which serves so that the children can coordinate. A son asks for the bolt, and while he increases his variable no other son will be able to do it because when they go to request the bolt this one will be occupied and they will remain in the wait until obtaining it. Once the increase has finished, the thread releases the bolt, thus allowing another thread to advance. That lock therefore allows the children to enter "by turns" the instruction *suma+=1 .

I leave you as an exercise this part of the locks (read about flock() ) or semaphores (read about sem_init() , sem_wait() and sem_post() .

    
answered by 18.12.2018 / 23:29
source