赵岳 14231027 此版本为待修订版!
很简单,调换case0和default的代码即可。此时就读写者互换了。
Lab4中已经写好了,就不再赘述了。
static int
piperead(struct Fd *fd, void *vbuf, u_int n, u_int offset)
{
int i;
struct Pipe *p;
char *rbuf;
/*Step 1: Get the pipe p according to fd. And vbuf is the reading buffer. */
p = (struct Pipe*)fd2data(fd);
rbuf = vbuf;
/*Step 2: If pointer of reading is ahead of writing,then yield. */
while(p->p_rpos>=p->p_wpos){
if(_pipeisclosed(fd,p)) // _pipeisclosed to check whether the pipe is closed.
return 0;
syscall_yield(); //syscall
}
/*Step 3: p_buf's size is BY2PIPE, and you should use it to fill rbuf. */
for(i=0 ; (p->p_rpos<p->p_wpos)&&(n--) ; i++ , p->p_rpos++)
rbuf[i] = p->p_buf[p->p_rpos%BY2PIPE];
return i;
}
static int
pipewrite(struct Fd *fd, const void *vbuf, u_int n, u_int offset)
{
int i;
struct Pipe *p;
char *wbuf;
/*Step 1: Get the pipe p according to fd. And vbuf is the writing buffer. */
p = (struct Pipe*)fd2data(fd);
wbuf = vbuf;
/*Step 2: If the difference between the pointer of writing and reading is larger than BY2PIPE, then yield. */
while(p->p_wpos-p->p_rpos>=BY2PIPE){
if(_pipeisclosed(fd,p)) // _pipeisclosed to check whether the pipe is closed.
return 0;
syscall_yield(); //syscall
}
/*Step 3: p_buf's size is BY2PIPE, and you should use it to fill rbuf. */
for(i=0 ; i<n ; i++ , p->p_wpos++){
p->p_buf[p->p_wpos%BY2PIPE] = wbuf[i];
while((i<n)&&(p->p_wpos-p->p_rpos>=BY2PIPE))
syscall_yield();
}
return n;
}
static int
_pipeisclosed(struct Fd *fd, struct Pipe *p)
{
int pfd, pfp, runs;
/*Step 1: Get reference of fd and p, and check if they are the same. */
pfd = pageref(fd);
runs = env->env_runs;
pfp = pageref(p);
while(runs!=env->env_runs){
pfd = pageref(fd);
runs = env->env_runs;
pfp = pageref(p);
} //again and again
/*Step 2: If they are the same, return 1; otherwise return 0. */
if(pfd == pfp)
return 1;
return 0;
}
dup函数的作用是将旧的文件描述符和对应的数据映射到新文件描述符上,类似于我们Lab4中的duppage。dup原来的机制是先映射文件描述符本身再映射其中的内容,通俗地讲,也就是先增加了fd_ref,再增加了pipe_ref,类似于close的竞争,我们应该先增加pipe_ref,否则在某一时刻会出现fd_ref=prpe_ref的情况从而关闭端口。
Linux中并不是所有的系统调用都是原子操作,例如write系统调用就不是。两个进程同时写一个文件,就会出现各自写的数据中相互穿插叠加的情况。我们可以设置一个“专职打字员”负责写,其他进程均以IPC的形式给它发送消息,然后打字员负责将待写入内容排成队列有序地写入。这样可以实现write的原子性。
1.不能。dup还有竞争存在,还需要解决。 2.照猫画虎,先处理fd,再处理pipe。
调换两个if语句即可,先处理fd,再处理pipe。
static int
_pipeisclosed(struct Fd *fd, struct Pipe *p)
{
int pfd, pfp, runs;
/*Step 1: Get reference of fd and p, and check if they are the same. */
pfd = pageref(fd);
runs = env->env_runs;
pfp = pageref(p);
while(runs!=env->env_runs){
pfd = pageref(fd);
runs = env->env_runs;
pfp = pageref(p);
} //again and again
/*Step 2: If they are the same, return 1; otherwise return 0. */
if(pfd == pfp)
return 1;
return 0;
}
至此,pipe和竞争测试已经成功完成,下来完成shell部分。
int
syscall_cgetc()
{
return msyscall(SYS_cgetc, 0, 0, 0, 0, 0);
}
case '|':
pipe(p);
if((r = fork())<0)
writef("fork error %d",r);
else if(r == 0) {
dup(p[0],0);
close(p[0]);
close(p[1]);
goto again;
}
else{
dup(p[1],1);
close(p[1]);
close(p[0]);
rightpipe = r;
goto runit;
}