Home > Software engineering >  Writing into shared memory while semaphore value is 0 causes program to freeze in C, ubuntu
Writing into shared memory while semaphore value is 0 causes program to freeze in C, ubuntu

Time:12-03

I'm writing a multi-process project for a university assignment in operating systems and I've come across a strange issue. Writing into a specific part of shared memory while the semaphore value is 0 causes my program to freeze.

More specifically, the first time I run the executable of parent, the first child process writes into the part of the memory attached to shm_segment_pointer->content once, then the next child process to reach that part of the program freezes the moment before it writes into the same part. Then in all subsequent runs, not even the first child is able to write into that memory segment, freezing up before doing so.

commands used to run: gcc parent.c -o parent gcc child.c -o child ./parent

parent.c

#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <sys/shm.h>
#include <time.h>

#include "utilities.h"


#define _OPEN_SYS_ITOA_EXT
#define N 4
#define REQUEST_HANDLING_REQ (unsigned long)1000
#define CLIENT_PATH_BUFSIZE 255

#define INITIAL_VALUE 1

void path_to_child(char* buffer){
    unsigned long dir_len;
    getcwd(buffer, CLIENT_PATH_BUFSIZE);
    dir_len = strlen(buffer);
    strcpy(buffer   dir_len, "/child");
}


int main(int argc, char* argv[]){
    char* newargv[2];

    char child_exe[CLIENT_PATH_BUFSIZE];
    unsigned int file_line_size, seg_amount;
    sem_t *main_sem, *rw_sem;
    int i = 0;
    pid_t pid = 1;
    int shmid_struct, shmid_text;
    unsigned int segment_size = 50;

    srand(time(NULL));
    sem_unlink(SEM_NAME);
    sem_unlink(RW_SEM_NAME);

   
    /*Initialize shared memory*/
    shmid_struct = shmget((key_t)SHM_KEY_SEG_STRUCT, sizeof(shm_seg), SHM_SEGMENT_PERM | IPC_CREAT);
    if (shmid_struct == -1) {
        fprintf(stderr, "shmget failed\n");
        exit(EXIT_FAILURE);
    }
    shmid_text = shmget((key_t)SHM_KEY_SEG_TEXT, SHM_LINE_SIZE * segment_size * sizeof(char), SHM_SEGMENT_PERM | IPC_CREAT);
    if (shmid_struct == -1) {
        fprintf(stderr, "shmget failed\n");
        exit(EXIT_FAILURE);
    }

    /*Init semaphores*/
    main_sem = sem_open(SEM_NAME, O_CREAT | O_EXCL, SEM_PERMS, INITIAL_VALUE);
    if(SEM_FAILED == main_sem){
        perror("sem_open error on main sem");
        exit(EXIT_FAILURE);
    }
    rw_sem = sem_open(RW_SEM_NAME, O_CREAT | O_EXCL, SEM_PERMS, INITIAL_VALUE);
    if(SEM_FAILED == rw_sem){
        perror("sem_open error on rw_sem");
        exit(EXIT_FAILURE);
    }

    /* Initialize the info needed for child functions*/

    newargv[0] = "child";                           //The name of the child process

    newargv[1] = NULL;
    path_to_child(child_exe);
    fflush(stdout);
    for( i = 0; i < N; i   ){
        if( 0 > (pid = fork())){
            perror("Could not fork, ending process...");
            exit(1);
        } else if(0 == pid){
            //Initiate child process
            execv(child_exe,newargv);
            //If exec failed:
            perror("exec2: execv failed");
            exit(2);
        }
    }

        
    /**************************************Cleanup Section******************************************/
    /*Close file descriptors*/

    /*Wait for child processes to finish*/
    for( i = 0; i < N; i   ){
        pid = wait(NULL);
        if(pid == -1){
            perror("wait failed");
        }

    }
    /*Clean up shared memory*/
    if (shmctl(shmid_text, IPC_RMID, 0) == -1) {
        fprintf(stderr, "shmctl(IPC_RMID) failed\n");
        exit(EXIT_FAILURE);
    }
    if (shmctl(shmid_struct, IPC_RMID, 0) == -1) {
        fprintf(stderr, "shmctl(IPC_RMID) failed\n");
        exit(EXIT_FAILURE);
    }

    /*Unlink semaphore, removing it from the file system only after the child processes are done*/
    if(sem_unlink(SEM_NAME) < 0){
        perror("sem_unlink failed");
    }
    if(sem_unlink(RW_SEM_NAME) < 0){
        perror("sem_unlink failed");
    }
    return 0;
}


child.c

int main(int argc, char *argv[]){

    struct timespec sleep_time;
    sem_t *semaphore, *rw_semaphore;
    shm_seg *shm_segment_pointer;
    int ifd, ofd;
    FILE* fptr;
    int shmid_struct, shmid_text;

    /********************************Init section*********************************************/
    srand(time(NULL));

    /*Open semaphore from the semaphore file.*/
    semaphore = sem_open(SEM_NAME, 0);
    if (semaphore == SEM_FAILED) {
        perror("sem_open failed");
        exit(EXIT_FAILURE);
    }
    rw_semaphore = sem_open(RW_SEM_NAME, 0);
    if (rw_semaphore == SEM_FAILED) {
        perror("sem_open failed");
        exit(EXIT_FAILURE);
    }
    setbuf(stdout,NULL);
    /* Make shared memory segment for the . */
    shmid_struct = shmget(SHM_KEY_SEG_STRUCT, sizeof(shm_seg), SHM_SEGMENT_PERM);
    if (shmid_struct == -1) {
        perror("Acquisition");
    }
    shmid_text = shmget(SHM_KEY_SEG_TEXT, seg_size * SHM_LINE_SIZE * sizeof(char), SHM_SEGMENT_PERM);
    if (shmid_text == -1) {
        perror("Acquisition");
    }
    /* Attach the segments. */
    shm_segment_pointer = (shm_seg*)shmat(shmid_struct, NULL, 0);
    if ( shm_segment_pointer == (void *) -1) { 
        perror("Attachment of segment"); 
        exit(2);
    }
    shm_segment_pointer->content = (char*)shmat(shmid_text, NULL, 0);
    if ( shm_segment_pointer->content == (void *) -1) { 
        perror("Attachment of text"); 
        exit(2);
    }

    /************************************Working Segment************************************************/

    sem_wait(semaphore);
    printf("Testing assignement to seg number...");
    shm_segment_pointer->seg_num = 1;
    printf("Successful.\nTesting assignment to string...");
    strcpy( shm_segment_pointer->content, "ABCD");
    printf("shm contents are: %s\n",shm_segment_pointer->content);
    sem_post(semaphore);
    sleep(10);



    if (sem_close(semaphore) < 0)
        perror("sem_close(1) failed");
    if( sem_close(rw_semaphore) < 0)
        perror("sem_close(1) failed");
    /* detach from the shared memory segment: */
    if(shmdt(shm_segment_pointer->content) == -1) {
        perror("shmdt on text point failed");
        exit(1);
    }
    if (shmdt(shm_segment_pointer) == -1) {
    perror("shmdt on segment pointer failed");
    exit(1);
 

    return 0;
}

utilities.h

#ifndef _UTILITIES_H_
#define _UTILITIES_H_

#include <stdlib.h>

#define SEM_NAME "SM"
#define RW_SEM_NAME "RW_SM"
#define OUTPUT_FILE "testoutput.txt"

#define SHM_LINE_SIZE 1024
#define SHM_SEGMENT_PERM 0666
#define SHM_KEY_SEG_STRUCT 01234
#define SHM_KEY_SEG_TEXT 02345
#define SEM_PERMS (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)



typedef struct shared_memory_seg_tag{
    int seg_num;
    char* content;
} shm_seg;






#endif /* _UTILITIES_H_ */

Output: 51200 seg size:50 Testing assignement to seg number...Successful. Testing assignment to string...

As stated, the program doesn't end, it freezes there and won't continue without forcing it to end.

I tried removing the semaphores, which fixed the freezing issue but was not the desired behaviour. I also tried delaying the detachment of the segments, which didn't change anything. The only thing that work was completely restructuring my project, which no longer uses a struct to store the segment information.

CodePudding user response:

You have a couple of errors in form that may or may not actually be causing you trouble, as described in comments on the question. But your main issue has nothing in particular to do with the semaphores. It is that this ...

typedef struct shared_memory_seg_tag{
    int seg_num;
    char* content;
} shm_seg;

... is nonsense for an object stored in shared memory.

Specifically, it pretty much never makes sense to store pointers in memory shared between processes. Putting a pointer in shared memory allows other processes to see the pointer, but not whatever, if anything, it points to. Not even if the pointed-to object is also in the shared memory segment, because the segment is not guaranteed to be attached at the same address in each process.

What's more, your particular pointer isn't even initialized to point to anything in the first place. Your child processes are attempting to copy data to whatever random place it points.

You probably want to declare an array instead of a pointer:

typedef struct shared_memory_seg_tag{
    int seg_num;
    char content[MAX_CONTENT_SIZE];
} shm_seg;

That makes space for the content available in the shared memory segment, and gives you a valid pointer to it in each child. Of course, you do have to choose a MAX_CONTENT_SIZE that suits all your needs.

  • Related