使用fork()在C中用餐的哲学家 [英] Dining Philosophers in C using fork()

查看:59
本文介绍了使用fork()在C中用餐的哲学家的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

前一段时间,我使用pthread为餐饮哲学家问题"编写了一个C程序,现在正尝试将其更改为使用fork().这是我已经通过的演讲的练习.但是一个朋友向我求助,我似乎无法自己解决,这让我发疯了!

I wrote a C program for the Dining Philosophers Problem using pthread some time ago and am now trying to change it to use fork() instead. This is an exercive for a lecture I already passed. But a friend asked me for help and I can't seem to get it figured out by myself, which is driving me crazy!

如果我执行"ps",则过程在那里.但是没有任何输出到stdout,所以我认为管道做错了.

If i do a "ps" the processes are there. But there isn't any output to stdout, so I think I'm doing something wrong with the pipes.

#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

#define N 5     
#define LEFT (i+4)%N
#define RIGHT (i+1)%N
#define THINKING 0
#define HUNGRY 1
#define EATING 2

sem_t spoon;
sem_t phil[N];
int state[N];
int phil_num[N]={0,1,2,3,4};
int fd[N][2]; // file descriptors for pipes
pid_t pid, pids[N]; // process ids
int i; 
int num;

void philosopher(int i);
void test(int i);
void take_spoon(int i);
void put_spoon(int i);

char buffer[100];

int main(void) 
{
  for(i=0;i<N;++i)
  {
    pipe(fd[i]);        
    pids[i] = fork();

    printf("i=%d\n",i);
    printf("pids[i]=%d\n",pids[i]);

    if(pids[i]==0)
    {
      // child
      dup2(fd[i][1],1);
      close(fd[i][0]);      
      close(fd[i][1]);
      philosopher(i);
      _exit(0);
    }

    else if(pids[i]>0)
    {
      // parent
      dup2(fd[i][0],0);
      close(fd[i][0]);      
      close(fd[i][1]);
    }
  }

  // wait for child processes to end
  for(i=0;i<N;++i) waitpid(pids[i],NULL,0);

  return 0;
}



void philosopher(int i)
{
  while(1)
  {
    sleep(1);
    take_spoon(i);
    sleep(2);
    put_spoon(i);
    sleep(1);
  }
}

void take_spoon(int i)
{
  sem_wait(&spoon);
  state[i] = HUNGRY;
  printf("philosopher %d is hungry\n",i+1);
  test(i);
  sem_post(&spoon);
  sem_wait(&phil[i]);
}

void put_spoon(int i)
{
  sem_wait(&spoon);
  state[i] = THINKING;
  printf("philosopher %d puts down spoon %d and %d hin\n",i+1,LEFT+1,i+1);
  printf("philosopher %d thinks\n",i+1);
  test(LEFT);
  test(RIGHT);
  sem_post(&spoon);
}

void test(int i)
{
  if( state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING)
  {
    state[i] = EATING;
    printf("philosopher %d takes spoon %d and %d\n",i+1,LEFT+1,i+1);
    printf("philosopher  %d eats\n",i+1);
    sem_post(&phil[i]);
  }
}

在此先感谢您的帮助.

推荐答案

几个问题.首先是在 fork()之后,子进程和父进程不共享内存.这是线程和进程之间的主要区别之一.每个进程都有其自己的虚拟地址空间.无论您想让哲学家共享什么,都必须通过创建共享内存来明确地做到这一点.似乎您希望全局变量在所有过程之间共享.(请注意,有一些共享的东西,例如打开的文件描述符,并且子级确实从父级那里获得了变量的副本,并被初始化为在 fork()<时分配给它们的值/code>调用.)

Several issues. First is that after fork(), the child process and parent process do not share memory. This is one of the primary differences between a thread and a process. Each process has its own virtual address space. Whatever you want the philosophers to share, you have to explicitly do that by creating shared memory. It seems you intended your global variables to be shared among all the processes. (Note that there are some things shared, such as open file descriptors, and the child does get a copy of the variables from the parent, initialized to the values that were assigned to them at the time of the fork() call.)

第二,您有一些令人困惑的不必要变量.特别是,这些管道没有任何实际用途.每个进程的 stdout 将已经进入控制台屏幕,而无需尝试将其通过管道传递回父进程.这是因为子进程已经继承了父进程的打开文件描述符,所以子进程将已经使用与父进程相同的 stdout .此外,未使用 phil_num num 变量,以及 i . pid pids 变量似乎被不必要地设置为全局变量.

Second, you have some confusingly unnecessary variables. In particular, the pipes do not serve any real purpose. The stdout for each of the processes will go to the console screen already, without the need for trying to pipe them back to the parent. This is because the child process already inherits the open file descriptors of the parent, so the child will already be using the same stdout as the parent. In addition, the phil_num, and num variables were unused, and the i. pid and pids variables seemed to be needlessly made global.

第三,您无法初始化信号量.默认初始化为全局变量可能会使信号量可用",但初始值为0,这意味着 sem_wait()只会被阻塞.在您的情况下,您需要在共享内存中使用这些信号量,因此无论如何都必须调用 sem_init()(以表明它将在多个进程之间共享),并且该调用为您提供了机会正确初始化值为 1 的信号量,以便最初的 sem_wait()调用有机会返回.

Third, you failed to initialize your semaphores. The default initialization as a global variable probably leaves the semaphore "useable" but with a 0 initial value, meaning sem_wait() on it will just block. In your case, you need those semaphores in shared memory, so a call to sem_init() is mandatory anyway (to indicate it is going to be shared between multiple processes), and the call gives you a chance to properly initialize the semaphore with a value of 1 so that the initial sem_wait() call has a chance to return.

在将全局变量调整为真正需要共享的内容之后,可以将它们捆绑在一起成为一个结构.然后,可以为共享数据创建一个全局指针.

After adjusting the globals down to what really needs to be shared, they can be bundled together into a structure. Then, a global pointer can be created for the shared data.

struct shared_data {
  sem_t spoon;
  sem_t phil[N];
  int state[N];
};

struct shared_data *shared;

void initialize_shared(); /* at program start */
void finalize_shared();   /* at program end */

创建共享内存的一种方法是使用 mmap().创建内存后,应正确初始化数据.这包括对信号量上的 sem_init()的调用. sem_destroy()用于清除信号量,并且可以使用 munmap()释放映射的内存.这些在流程退出时为您完成,但出于完整性考虑而提供.(您应该始终检查您进行的所有操作系统调用的返回值,但是为了简洁起见,我已将它们省略了.)

One way to create shared memory is to use mmap(). After the memory is created, the data should be initialized properly. This includes a call to sem_init() on the semaphores. sem_destroy() is used to clean up a semaphore, and the mapped memory can be released with munmap(). These are done for you when the process exits, but provided for completeness. (You should always check the return values of all the operating system calls you make, but I have elided them for the sake of brevity.)

void initialize_shared()
{
  int i;
  int prot=(PROT_READ|PROT_WRITE);
  int flags=(MAP_SHARED|MAP_ANONYMOUS);
  shared=mmap(0,sizeof(*shared),prot,flags,-1,0);
  memset(shared,'\0',sizeof(*shared));
  sem_init(&shared->spoon,1,1);
  for(i=0;i<N;++i) sem_init(&shared->phil[i],1,1);
}

void finalize_shared()
{
  int i;
  for(i=0;i<N;++i) sem_destroy(&shared->phil[i]);
  munmap(shared, sizeof(*shared));
}

您的 main()实现实际上并没有改变,除了您需要为不必要的全局变量添加局部变量,并调用 initialize_shared()和(可选) finalize_shared().另外,删除所有与 pipe()相关的代码.

Your main() implementation does not really change, except you need to add local variables for the ones that were needlessly global, as well as call initialize_shared() and optionally finalize_shared(). Also, remove all the code related to pipe().

int main(void)
{
  int i;
  pid_t pid, pids[N]; // process ids
  initialize_shared();
  for(i=0;i<N;++i)
  {
    pid = fork();
    if(pid==0)
    {
      // child
      philosopher(i);
      _exit(0);
    }
    else if(pid>0)
    {
      // parent
      pids[i] = pid;
      printf("pids[%d]=%d\n",i,pids[i]);
    }
    else
    {
      perror("fork");
      _exit(0);
    }
  }
  // wait for child processes to end
  for(i=0;i<N;++i) waitpid(pids[i],NULL,0);

  finalize_shared();
  return 0;
}

请注意,由于 philosopher()被实现为无限循环,因此您的程序永远不会真正退出.

Note that your program never really exits on its own, since philosopher() is implemented as an infinite loop.

这篇关于使用fork()在C中用餐的哲学家的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆