- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我不久前使用 pthread 为哲学家就餐问题编写了一个 C 程序,现在尝试将其更改为使用 fork()。这是我已经通过的讲座的练习。但是一个 friend 向我寻求帮助,而我似乎无法自己解决这个问题,这让我发疯!
如果我执行“ps”,进程就在那里。但没有任何输出到标准输出,所以我认为我在管道上做错了什么。
#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()
调用时分配给它们的值。)
其次,你有一些令人困惑的不必要的变量。特别是,这些管道没有任何实际用途。 stdout
每个进程都将进入控制台屏幕,无需尝试将它们通过管道传输回父进程。这是因为子进程已经继承了父进程的打开文件描述符,因此子进程将已经使用相同的 stdout
作为家长。此外,phil_num
,和num
变量未使用,并且 i
。 pid
和pids
变量似乎不必要地变得全局化。
第三,您未能初始化信号量。作为全局变量的默认初始化可能会使信号量“可用”,但初始值为 0,这意味着 sem_wait()
它只会阻塞。在您的情况下,您需要共享内存中的这些信号量,因此调用 sem_init()
无论如何都是强制性的(以表明它将在多个进程之间共享),并且该调用使您有机会使用值 1
正确初始化信号量这样初始sem_wait()
调用有机会返回。
将全局变量调整为真正需要共享的内容后,可以将它们捆绑在一起形成一个结构。然后,可以为共享数据创建一个全局指针。
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()
来释放。这些是在进程退出时为您完成的,但为了完整性而提供。 (您应该始终检查您进行的所有操作系统调用的返回值,但为了简洁起见,我省略了它们。)
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()
相关的所有代码.
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()
作为无限循环实现。
关于c - C 中的哲学家就餐使用 fork(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17092121/
我是一名优秀的程序员,十分优秀!