在Linux中,由于沒(méi)有對(duì)應(yīng)的物理設(shè)備,RPC管道文件系統(tǒng)是作為一種虛擬的文件系統(tǒng)存在的,其在系統(tǒng)中的注冊(cè)和注銷(xiāo)等操作都必須遵循Linux文件系統(tǒng)規(guī)范。關(guān)于RPC管道的實(shí)現(xiàn)主要分布在rpc_pipe.c文件中。下面是RPC管道文件系統(tǒng)在sunrpc_syms.c文件中的初始化過(guò)程:
代碼1-1 RPC管道文件系統(tǒng)的初始化過(guò)程
static int __init init_sunrpc(void) //__init表示該函數(shù)只在初始化過(guò)程中調(diào)用
{
int err=register_rpc_pipefs(); //注冊(cè)RPC管道文件系統(tǒng)"rpc_pipefs"
if (err)
goto out;
err=rpc_init_mempool(); //初始化內(nèi)存池
if (err) {
unregister_rpc_pipefs(); //注銷(xiāo)RPC管道文件系統(tǒng)"rpc_pipefs"
goto out;
}
#ifdef RPC_DEBUG
rpc_register_sysctl(); //注冊(cè)SYSCTL,方便用戶讀取或調(diào)整系統(tǒng)設(shè)置
#endif
#ifdef CONFIG_PROC_FS
rpc_proc_init(); //初始化PROC,方便用戶獲取運(yùn)行時(shí)信息
#endif
cache_register(&ip_map_cache); //注冊(cè)緩沖
cache_register(&unix_gid_cache);
svc_init_xprt_sock();
init_socket_xprt(); //初始化套接字
rpcauth_init_module(); //初始化RPC鑒權(quán)模塊
out:
return err;
}
和其他文件系統(tǒng)一樣,一個(gè)RPC管道是作為RPC管道文件系統(tǒng)下的一個(gè)文件存在的,下面是RPC管道的接口定義:
static const struct file_operations rpc_pipe_fops={
.owner=THIS_MODULE,
.llseek=no_llseek,
.read=rpc_pipe_read, //讀取管道數(shù)據(jù)
.write=rpc_pipe_write, //寫(xiě)入管道數(shù)據(jù)
.poll=rpc_pipe_poll, //輪詢管道
.ioctl=rpc_pipe_ioctl, //管道的ioctl
.open=rpc pipe_open, //打開(kāi)管道
.release=rpc_pipe_release, //釋放管道
};
一個(gè)RPC管道就是RPC管道文件系統(tǒng)的一個(gè)節(jié)點(diǎn),下面是RPC節(jié)點(diǎn)的定義:
struct rpc_inode {
struct inode vfs_inode; //繼承VFS節(jié)點(diǎn)
void *private;
struct list_head pipe;
struct list_head in_upcall; //通知上層節(jié)點(diǎn)
struct list_head in_downcall; //通知下層節(jié)點(diǎn)
int pipelen;
int nreaders; //讀數(shù)據(jù)線程數(shù)
int nwriters; //寫(xiě)數(shù)據(jù)線程數(shù)
int nkern_readwriters;
wait_queue_head_t waitq; //等待隊(duì)列
#define RPC_PIPE_WAIT_FOR_OPEN 1
int flags;
struct rpc_pipe_ops *ops; //管道選項(xiàng)
struct delayed_work queue_timeout;
};
struct rpc_pipe_ops {
ssize_t (*upcall)(struct file *, struct rpc_pipe_msg *, char __user *, size_t);
ssize_t (*downcall)(struct file *, const char __user *, size_t);
void (*release_pipe)(struct inode *); //釋放管道
int (*open_pipe)(struct inode *); //打開(kāi)管道
void (*destroy_msg)(struct rpc_pipe_msg *); //銷(xiāo)毀管道消息
};
為了利用RPC在Linux內(nèi)核和用戶空間之間進(jìn)行通信,需要?jiǎng)?chuàng)建一個(gè)RPC管道,下面是創(chuàng)建一個(gè)RPC管道的實(shí)現(xiàn)過(guò)程:
代碼1-2 創(chuàng)建RPC管道的過(guò)程
struct dentry * rpc_mkpipe(struct dentry *parent, const char *name, void *private, struct rpc_pipe_ops *ops, int flags)
{
struct dentry *dentry;
struct inode *dir, *inode;
struct rpc_inode *rpci;
dentry=rpc_lookup_create(parent, name, strlen(name), 0); //創(chuàng)建目錄
if (IS_ERR(dentry))
return dentry;
dir=parent->d_inode;
if (dentry->d_inode) {
rpci=RPC_I(dentry->d_inode);
if (rpci->private !=private ||
rpci->ops !=ops ||
rpci->flags !=flags) {
dput (dentry);
dentry=ERR_PTR(-EBUSY);
}
rpci->nkern_readwriters++;
goto out;
}
inode=rpc_get_inode(dir->i_sb, S_IFIFO | S_IRUSR | S_IWUSR); //配置權(quán)限
if (!inode)
goto err_dput;
inode->i_ino=iunique(dir->i_sb, 100);
inode->i_fop=&rpc_pipe_fops;
d_instantiate(dentry, inode);
rpci=RPC_I(inode);
rpci->private=private;
rpci->flags=flags;
rpci->ops=ops;
rpci->nkern_readwriters=1;
fsnotify_create(dir, dentry);
dget(dentry);
out:
mutex_unlock(&dir->i_mutex);
return dentry;
err_dput:
dput(dentry);
dentry=ERR_PTR(-ENOMEM);
printk(KERN_WARNING "%s: %s() failed to create pipe %s/%s (errno=%d)\n",__FILE__, __func__, parent->d_name.name, name,-ENOMEM);
goto out;
}
通過(guò)RPC管道文件系統(tǒng),調(diào)用者可以像操作其他文件那樣進(jìn)行RPC管道的操作,常見(jiàn)的操作有讀取數(shù)據(jù)、寫(xiě)入數(shù)據(jù)、查詢管道信息、創(chuàng)建路徑、刪除路徑、創(chuàng)建RPC管道等。
需要說(shuō)明的是,當(dāng)管道數(shù)據(jù)可讀時(shí),需要調(diào)用rpc_queue_upcall()函數(shù)通知用戶空間,當(dāng)管道可寫(xiě)入數(shù)據(jù)時(shí),則不需要通知用戶空間。在通知用戶空間RPC管道的狀態(tài)時(shí),需要將消息封裝在rpc_pipe_msg結(jié)構(gòu)體中。下面是rpc_queue_upcall()函數(shù)的實(shí)現(xiàn)過(guò)程:
代碼1-3 RPC通知用戶空間的過(guò)程
int rpc_queue_upcall(struct inode *inode, struct rpc_pipe_msg *msg)
{
struct rpc_inode *rpci=RPC_I(inode);
int res=-EPIPE;
spin_lock(&inode->i_lock); //自旋鎖
if (rpci->ops==NULL)
goto out;
if (rpci->nreaders) {
list_add_tail(&msg->list, &rpci->pipe); //添加到消息隊(duì)列
rpci->pipelen+=msg->len;
res=0;
} else if (rpci->flags & RPC_PIPE_WAIT_FOR_OPEN) { //如果RPC管道等待打開(kāi)
if (list_empty(&rpci->pipe))
queue_delayed_work(rpciod_workqueue, //添加到rpciod_workqueue
&rpci->queue_timeout,
RPC_UPCALL_TIMEOUT);
list_add_tail(&msg->list, &rpci->pipe); //添加到消息隊(duì)列
rpci->pipelen += msg->len;
res=0;
}
out:
spin_unlock(&inode->i_lock);
wake_up(&rpci->waitq); //喚醒等待隊(duì)列
return res;
}
在auth_gss.c文件中,有RPC管道的運(yùn)用實(shí)例,感興趣的朋友可以自行研讀。