2017-02-17 20 views
1

그래서 C에서 공유 메모리와 shm 함수를 처음 사용했습니다.forked 프로세스에서 C로 베이커리 알고리즘 구현

두 가지 프로그램이 있습니다. 주인과 노예. 가장 일반적인 의미에서 : 마스터 프로그램은 공유 메모리에 sharedNum 정수를 생성하고 슬레이브 프로그램을 실행하는 여러 프로세스를 포크합니다. 그러면 슬레이브 프로그램 프로세스는 sharedNum을 공유 메모리에서 증분하고 (아마 여러 번, 심지어는) 지정된 파일로 인쇄해야합니다. 나는 공유 메모리 조작을 제외하고 모든 것이 작동하고 있다고 100 % 확신한다. 나는 개발을 통해 테스트 해왔다.

내가 겪고있는 문제는 슬레이브 프로그램 프로세스의 경쟁 조건 때문입니다. 나는 프로세스가 핵심 섹션에 액세스하는 것을 잠 그거나 잠금 해제하기 위해 베이커리 알고리즘을 구현해야한다는 것을 알고 있습니다. 이로 인해 sharedNum 조작이 해제됩니다.

슬레이브 프로그램에서 베이커리 알고리즘을 구현하려고 시도했지만 작동하지 않는 것 같습니다 ... 테스트를 통해 choosingturnNum 개의 변수 (내가 필요로하는 변수)를 발견했습니다. 내가 아는 한 베이커리 알고리즘) 자체가 경쟁 조건을 경험하고 있습니다. 어떻게 피할 수 있습니까? 나는 그들이 공유 메모리에 있어야한다고 확신한다. 그렇지 않으면 여러 프로세스에 의해 업데이트 될 수 없다. ...

미리 감사드립니다.

프로그램 덤프가 이어집니다.

는 master.c :

#include<stdio.h> 
#include<stdlib.h> 
#include<unistd.h> 
#include<sys/types.h> 
#include<sys/wait.h> 
#include<ctype.h> 
#include<string.h> 
#include<errno.h> 
#include<signal.h> 
#include<sys/ipc.h> 
#include<sys/shm.h> 

// global variables 
pid_t *children; 
int slave_max; 

// globals relating to shared memory 
key_t shmkey; 
int shmid_sharedNum; 
int *sharedNum; 

void handle_sigalrm(int signum, siginfo_t *info, void *ptr) 
{ 
    // prevents multiple interrupts 
    signal(SIGINT, SIG_IGN); 

    fprintf(stderr, "Master ran out of time\n"); 

    // detaching and deleting shared memory 
    shmdt(sharedNum); 
    shmctl(shmid_sharedNum, IPC_RMID, NULL); 

    // creating tmp_children to replace children 
    // this way children can be freed before SIGTERM 
    pid_t tmp_children[slave_max]; 
    int i; 
    for (i = 0; i < slave_max; i++); 
    { 
     tmp_children[i] = children[i]; 
    } 

    // freeing allocated memory 
    free(children); 

    // terminate child processes 
    for (i = 0; i < slave_max; i++) 
    { 
     kill(tmp_children[i], SIGTERM); 
    } 
} 

void handle_sigint(int signum, siginfo_t *info, void *ptr) 
{ 
    // prevents multiple interrupts 
    signal(SIGINT, SIG_IGN); 
    signal(SIGALRM, SIG_IGN); 

    fprintf(stderr, " interrupt was caught by master\n"); 

    // detaching and deleting shared memory 
    shmdt(sharedNum); 
    shmctl(shmid_sharedNum, IPC_RMID, NULL); 

    // creating tmp_children to replace children 
    // this way children can be freed before SIGTERM 
    pid_t tmp_children[slave_max]; 
    int i; 
    for (i = 0; i < slave_max; i++) 
    { 
     tmp_children[i] = children[i]; 
    } 

    // freeing allocated memory 
    free(children); 

    // terminate child processes 
    for (i = 0; i < slave_max; i++) 
    { 
     kill(tmp_children[i], SIGTERM); 
    } 
} 

void catch_sigalrm() 
{ 
    static struct sigaction _sigact; 
    memset(&_sigact, 0, sizeof(_sigact)); 
    _sigact.sa_sigaction = handle_sigalrm; 
    _sigact.sa_flags = SA_SIGINFO; 
    sigaction(SIGALRM, &_sigact, NULL); 
} 

void catch_sigint() 
{ 
    static struct sigaction _sigact; 
    memset(&_sigact, 0, sizeof(_sigact)); 
    _sigact.sa_sigaction = handle_sigint; 
    _sigact.sa_flags = SA_SIGINFO; 
    sigaction(SIGINT, &_sigact, NULL); 
} 

int main(int argc, char *argv[]) 
{ 
    // default variables 
    int i = 0; // to be used as a counter variable 
    slave_max = 5; 
    char slave_max_str[25]; // arbitrary size 
    char *log_filename = NULL; 
    int slave_increment = 3; 
    char slave_increment_str[25]; // arbitrary size 
    int master_time = 20; 

    // shared memory initialization 
    shmkey = ftok("./master", 118371); // arbitrary key 
    shmid_sharedNum = shmget(shmkey, sizeof(sharedNum), 0600 | IPC_CREAT); 
    sharedNum = (int *)shmat(shmid_sharedNum, NULL, 0); 
    sharedNum[0] = 0; 

    // handling command line args with getopt 
    int c; 
    while((c = getopt(argc, argv, "hs:l:i:t:")) != -1) 
    { 
     switch(c) 
     { 
     // -h : program help 
     case 'h': 
      // the following if-else block makes sure 
      // that -h will be used by itself 
      if (argc == 2) 
      { 
       printf("%s -h : program help\n", argv[0]); 
       printf("%s -s [integer] : set max number of slave processes\n", argv[0]); 
       printf("%s -l [filename] : set log filename\n", argv[0]); 
       printf("%s -i [integer] : set slave process incrementer\n", argv[0]); 
       printf("%s -t [integer] : set number of seconds master will terminate\n", argv[0]); 
       exit(0); 
      } 
      else 
      { 
       fprintf(stderr, "%s: option must be used by itself -- 'h'\n", argv[0]); 
       exit(1); 
      } 
     // -s [integer] : set max number of slave processes 
     case 's': 
      slave_max = atoi(optarg); 
      break; 
     // -l [filename] : set log filename 
     case 'l': 
      log_filename = optarg; 
      break; 
     // -i [integer] : set slave process incrementer 
     case 'i': 
      slave_increment = atoi(optarg); 
      break; 
     // -t [integer] : set number of seconds master will terminate 
     case 't': 
      master_time = atoi(optarg); 
      break; 
     // the following case takes care of user input errors 
     case '?': 
      if (optopt == 's') 
       fprintf(stderr, "Error: -s requires an integer\n"); 
      else if (optopt == 'l') 
       fprintf(stderr, "Error: -l requires a filename\n"); 
      else if (optopt == 'i') 
       fprintf(stderr, "Error: -i requires an integer\n"); 
      else if (optopt == 't') 
       fprintf(stderr, "Error: -t requires an integer\n"); 
      else if (isprint(optopt)) 
       fprintf(stderr, "Error: input can't be printed\n"); 
      else 
       fprintf(stderr, "Error: invalid syntax\n"); 
      exit(1); 
     default: 
      abort(); 
     } 
    } 

    catch_sigint(); 
    catch_sigalrm(); 
    alarm(master_time); 

    // if log_filename wasn't passed in by -l, 
    // its default value is set here... 
    if (!log_filename) 
     log_filename = "test.out"; 

    // setting slave_increment_str and slave_max_str 
    // for use in future execl 
    snprintf(slave_increment_str, 25, "%i", slave_increment); 
    snprintf(slave_max_str, 25, "%i", slave_max); 

    // initializing pids 
    if ((children = (pid_t *)(malloc(slave_max * sizeof(pid_t)))) == NULL) 
    { 
     errno = ENOMEM; 
     perror("children malloc"); 
     exit(1); 
    } 
    pid_t p; 

    // forking off child processes 
    for (i = 0; i < slave_max; i++) 
    { 
     p = fork(); 
     if (p < 0) 
     { 
     fprintf(stderr,"Error: fork failed\n"); 
     continue; 
     } 
     if (p == 0) 
     { 
     children[i] = p; 
     execl("./slave", "slave", "-l", log_filename, "-s", slave_max_str, "-i", slave_increment_str, (char *) NULL); 
     exit(0); 
     } 
    } 

    // waiting for all child processes to finish 
    for (i = 0; i < slave_max; i++) 
    { 
     int status; 
     waitpid(children[i], &status, 0); 
    } 

    // clean up and finish 
    free(children); 
    shmdt(sharedNum); 
    shmctl(shmid_sharedNum, IPC_RMID, NULL); 
    return 0; 
} 

는 slave.c : slave.c의

#include<stdio.h> 
#include<stdlib.h> 
#include<unistd.h> 
#include<string.h> 
#include<ctype.h> 
#include<sys/types.h> 
#include<time.h> 
#include<signal.h> 
#include<sys/ipc.h> 
#include<sys/shm.h> 

// global variables 
pid_t parent; 
pid_t child; 
int childProc; 

// globals for shared memory 
key_t shmkey; 
int shmid_sharedNum, shmid_choosing, shmid_turnNum; 
int *sharedNum; int *choosing; int *turnNum; 

void handle_sigterm(int signum, siginfo_t *info, void *ptr) 
{ 
    // detaching and deleting shared memory 
    shmdt(sharedNum); 
    shmdt(choosing); 
    shmdt(turnNum); 
    shmctl(shmid_sharedNum, IPC_RMID, NULL); 
    shmctl(shmid_choosing, IPC_RMID, NULL); 
    shmctl(shmid_turnNum, IPC_RMID, NULL); 

    fprintf(stderr, "Process #%i was terminated by master\n", childProc); 
    exit(0); 
} 

void catch_sigterm() 
{ 
    static struct sigaction _sigact; 
    memset(&_sigact, 0, sizeof(_sigact)); 
    _sigact.sa_sigaction = handle_sigterm; 
    _sigact.sa_flags = SA_SIGINFO; 
    sigaction(SIGTERM, &_sigact, NULL); 
} 

int main(int argc, char *argv[]) 
{ 
    // default variables 
    parent = getppid(); 
    child = getpid(); 
    childProc = (int)(child - parent); 
    int i, j, maxCounter; // to be used as a counter variables 
    int slave_max = 1; 
    char *log_filename = NULL; 
    int slave_incrementer = 3; 
    srand(time(NULL)); 
    int napTime; 

    // shared memory initialization 
    shmkey = ftok("./master", 118371); // arbitrary key 
    shmid_sharedNum = shmget(shmkey, sizeof(sharedNum), 0600 | IPC_CREAT); 
    sharedNum = (int *)shmat(shmid_sharedNum, NULL, 0); 
    shmid_choosing = shmget(shmkey, sizeof(choosing), 0600 | IPC_CREAT); 
    choosing = (int *)shmat(shmid_choosing, NULL, 0); 
    shmid_turnNum = shmget(shmkey, sizeof(turnNum), 0600 | IPC_CREAT); 
    turnNum = (int *)shmat(shmid_turnNum, NULL, 0); 

    catch_sigterm(); 
    signal(SIGINT, SIG_IGN); 

    // implementing getopt to handle command line args 
    int c; 
    while((c = getopt(argc, argv, "s:l:i:")) != -1) 
    { 
     switch(c) 
     { 
     // -s [integer] : number of slave processes 
     case 's': 
      slave_max = atoi(optarg); 
     // -l [filename] : set log filename 
     case 'l': 
      log_filename = optarg; 
      break; 
     // -i [integer] : set slave process incrementer 
     case 'i': 
     slave_incrementer = atoi(optarg); 
      break; 
     // this case takes care of user input errors 
     case '?': 
      if (optopt == 's') 
       fprintf(stderr, "Error: -s requires an integer\n"); 
      else if (optopt == 'l') 
       fprintf(stderr, "Error: -l requires a filename\n"); 
      else if (optopt == 'i') 
       fprintf(stderr, "Error: -i requires an integer\n"); 
      else if (isprint(optopt)) 
       fprintf(stderr, "Error: input can't be printed\n"); 
      else 
       fprintf(stderr, "Error: invalid syntax\n"); 
      exit(1); 
     default: 
      abort(); 
     } 
    } 

    // if log_filename wasn't passed in by -l, 
    // its default value is set here... 
    if (!log_filename) 
     log_filename = "test.out"; 

    struct timespec now; 
    long curTime; 
    int max = 0; 
    for (i = 0; i < slave_incrementer; i++) 
    { 
     // execute code to enter critical section 
     choosing[(childProc-1)] = 1; 
     for (maxCounter = 0; maxCounter < slave_max; maxCounter++) 
     { 
      if((turnNum[maxCounter]) > max) 
      max = (turnNum[maxCounter]); 
     } 
     turnNum[(childProc-1)] = 1 + max; 
     printf("turnNum for process #%i = %i\n", childProc, turnNum[(childProc-1)]); 
     choosing[(childProc-1)] = 0; 
     for (j = 0; j < slave_max; j++) 
     { 
    while (choosing[j] == 1) {} 
     while ((turnNum[j] != 0) && (turnNum[j] < turnNum[(childProc-1)])) {} 
     } 

     // critical section 
     napTime = rand() % 3; 
     sleep(napTime); 
     sharedNum[0]++; 
     clock_gettime(CLOCK_REALTIME, &now); 
     curTime = ((((long)now.tv_sec) * 1000000000) + (long)now.tv_nsec); 
     // write message to log file here 
     // for testing purposes: 
     printf("File modified by process #%i (increment %i) at time %ld with sharedNum = %i\n", childProc, (i+1), curTime, sharedNum[0]); 
     napTime = rand() % 3; 
     sleep(napTime); 

     // exit from critical section 
     turnNum[(childProc-1)] = 0; 
    } 

    // clean up and finish 
    shmdt(sharedNum); 
    shmdt(choosing); 
    shmdt(turnNum); 
    shmctl(shmid_sharedNum, IPC_RMID, NULL); 
    shmctl(shmid_choosing, IPC_RMID, NULL); 
    shmctl(shmid_turnNum, IPC_RMID, NULL); 
    return 0; 
} 

(브로큰) 빵집 알고리즘 섹션 :

// execute code to enter critical section 
    choosing[(childProc-1)] = 1; 
    for (maxCounter = 0; maxCounter < slave_max; maxCounter++) 
    { 
     if((turnNum[maxCounter]) > max) 
     max = (turnNum[maxCounter]); 
    } 
    turnNum[(childProc-1)] = 1 + max; 
    printf("turnNum for process #%i = %i\n", childProc, turnNum[(childProc-1)]); 
    choosing[(childProc-1)] = 0; 
    for (j = 0; j < slave_max; j++) 
    { 
while (choosing[j] == 1) {} 
    while ((turnNum[j] != 0) && (turnNum[j] < turnNum[(childProc-1)])) {} 
    } 

    // critical section 
    napTime = rand() % 3; 
    sleep(napTime); 
    sharedNum[0]++; 
    clock_gettime(CLOCK_REALTIME, &now); 
    curTime = ((((long)now.tv_sec) * 1000000000) + (long)now.tv_nsec); 
    // write message to log file here 
    // for testing purposes: 
    printf("File modified by process #%i (increment %i) at time %ld with sharedNum = %i\n", childProc, (i+1), curTime, sharedNum[0]); 
    napTime = rand() % 3; 
    sleep(napTime); 

    // exit from critical section 
    turnNum[(childProc-1)] = 0; 
+0

나는 원래 알고리즘, 그것은 중요하지 않습니다; 이것이 그 알고리즘의 아이디어입니다. 이것이 당신의 문제 일 수 있다는 생각을 어떻게 얻습니까? – Matthias

+0

@ TonyTannous : 귀하의 힌트가 잘못되었습니다. 'fork()'는 새로운 스레드가 아닌 새로운 프로세스를 생성합니다. 따라서 자체 주소 공간을 가지며 첫 번째 쓰기 액세스시 메모리가 복사되고 두 개의 변수 인스턴스가 있습니다. – Matthias

+0

@Matthias : 테스트 할 때,'printf''의'turnNum [(childProc-1)]'에 한 줄을 포함 시켰고, 프로세스마다 중복되는 경우가 종종있었습니다. 에서처럼 여러 프로세스가 동일한 'turnNum'값을 인쇄합니다. – Anthony

답변

0

나는 얼마 전에이 발견 , 그러나 해결책을 제공하는 것을 잊었다. 내 베이커리 알고리즘 구현은 괜찮 았던 것으로 나타났습니다. 실제 문제는 내가 공유 메모리 세그먼트를 초기화하는 방법에서 나온 것입니다. slave.c. 각 세그먼트마다 다른 키를 사용하여 프로그램을 실행할 때 예상 결과와 함께 master을 실행할 수있었습니다.

slave.c의

업데이트 "공유 메모리 초기화"섹션`choosing`와`turnNum`에 대한 경쟁 조건이있는 경우

// shared memory initialization 
shmkey = ftok("./master", 118371); // arbitrary key #1 
shmid_sharedNum = shmget(shmkey, sizeof(sharedNum), 0600 | IPC_CREAT); 
sharedNum = (int *)shmat(shmid_sharedNum, NULL, 0); 
shmkey = ftok("./slave", 118372); // arbitrary key #2 
shmid_choosing = shmget(shmkey, sizeof(choosing), 0600 | IPC_CREAT); 
choosing = (int *)shmat(shmid_choosing, NULL, 0); 
shmkey = ftok("./slave", 118373); // arbitrary key #3 
shmid_turnNum = shmget(shmkey, sizeof(turnNum), 0600 | IPC_CREAT); 
turnNum = (int *)shmat(shmid_turnNum, NULL, 0);