open函数原理的疑问

爱吃糖的猫
🔖︎ 3 订阅
👍︎ 5 点赞
❌︎ 已关闭

在做Exercise 5.9中的open函数时,虽然根据注释是可以写出来代码,但是对其中原理很不理解。
open函数中,首先用fd_alloc分配一个新的Fd。
然后fsipc_open函数就不太理解了。1、传入的path和mode强制转换为虚拟地址是什么原理?fsipc_open中的参数path和mode最后是要存放到req中,然后调用fsipc(FSREQ_OPEN, req, fd, &perm);而fsipc这个函数又用了ipc_send,在ipc_send中req又做为srcva传入syscall_ipc_try_send(whom, val, srcva, perm)中。综上,经过一番转换,path和mode存入一段空间后被作为一个虚拟地址传到了lab4完成的sys_ipc_try_send中。想问一下这是什么原理?把const char *path和u_int omode放入结构体强制转换为虚拟地址就能正确使用这个地址了?
2、在fsipc函数中调用的ipc_send(envs[1].env_id, type, fsreq, PTE_D);这里的envs[1].env_id是一个常量,envs[1]是指导书中提到的文件系统进程吗?
3、fsipc函数中先调用ipc_send(envs[1].env_id, type, fsreq, PTE_D);,然后return ipc_recv(……)。我记得如果不先对envs[1]的env_ipc_recving进行设置,会返回-E_IPC_NOT_RECV。所以这里是不是应该先用sys_ipc_recv设置一下envs[1]的env_ipc_recving?

回复主题帖

戴波(助教)
👍︎ 0 点赞
  1. fsipc_open(user/lib/fsipc.c)函数定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int fsipc_open(const char *path, u_int omode, struct Fd *fd) {
u_int perm;
struct Fsreq_open *req;

req = (struct Fsreq_open *)fsipcbuf;

// The path is too long.
if (strlen(path) >= MAXPATHLEN) {
return -E_BAD_PATH;
}

strcpy((char *)req->req_path, path);
req->req_omode = omode;
return fsipc(FSREQ_OPEN, req, fd, &perm);
}

该函数将预先定义好的fsipcbuf(user/lib/fsipc.c)转化为需要使用的struct Fsreq_openuser/include/fsipc.h)便于存放数据。

fsipcbuf定义如下:

1
u_char fsipcbuf[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));

注:任何包含了该源文件的用户程序都会在bss段中定义fsipcbuf,长度为一页大小,attribute属性规定其地址与页大小对齐,便于后续使用。

然后,将path参数使用strcpy拷贝到req结构体中的req_path成员变量中。将mode的值赋值给req结构体的req_mode成员变量。

1
2
3
4
struct Fsreq_open {
char req_path[MAXPATHLEN];
u_int req_omode;
};

fsreqbuf自此存放了需要使用的数据,对于ipc_send,其srcva若不为0,则表示需要从源进程共享一页空间给目标空间。

这里的共享指的是将进程地址空间中对应srcva的那一页物理页框同样映射到到目标空间的对应页表中,映射到的虚拟地址由ipc_recv决定。具体内容参考kern/syscall_all.c中的sys_ipc_try_send对于srcva的处理。

这样目标进程就能够通过ipc_recv传递的dstva使用源进程传入的Fsreq_open结构体。

  1. 是的。
  2. 请参考fs/serv.cserv函数的实现,fs进程通过该函数实现其逻辑业务,该函数会一直使用ipc_recv等待请求并处理,所以不需要再对env[1]进行额外的处理。

爱吃糖的猫 回复 戴波(助教)
👍︎ 0 点赞

我应该明白了。fsipc_open函数中req是一个结构体指针,这个指针是虚拟地址,而且对应着某一个物理页框,req指向的这个结构体就存放在这个物理页框中。
我们将想要传递的数据写入这个结构体(也就是写入这个物理页框)后,把req作为srcva传给ipc_send,之后再通过这个srcva这个虚拟地址找到其对应的物理页框(也就是找到了其中存放那个结构体),然后完成映射后,就可以让目标进程也读取到这个结构体的内容,从而实现共享。

0%