您的位置:华清远见教育科技集团 >> Android资料 >> Android RPC管道文件系统  
 
Android RPC管道文件系统
分享到:

在Linux中,由于没有对应的物理设备,RPC管道文件系统是作为一种虚拟的文件系统存在的,其在系统中的注册和注销等操作都必须遵循Linux文件系统规范。关于RPC管道的实现主要分布在rpc_pipe.c文件中。下面是RPC管道文件系统在sunrpc_syms.c文件中的初始化过程:

代码1-1 RPC管道文件系统的初始化过程

static int __init init_sunrpc(void) //__init表示该函数只在初始化过程中调用
    {
        int err=register_rpc_pipefs(); //注册RPC管道文件系统"rpc_pipefs"
        if (err)
        goto out;
        err=rpc_init_mempool(); //初始化内存池
        if (err) {
        unregister_rpc_pipefs(); //注销RPC管道文件系统"rpc_pipefs"
        goto out;
        }
        #ifdef RPC_DEBUG
        rpc_register_sysctl(); //注册SYSCTL,方便用户读取或调整系统设置
    #endif
    #ifdef CONFIG_PROC_FS
        rpc_proc_init(); //初始化PROC,方便用户获取运行时信息
    #endif
        cache_register(&ip_map_cache); //注册缓冲
        cache_register(&unix_gid_cache);
        svc_init_xprt_sock();
    init_socket_xprt(); //初始化套接字
        rpcauth_init_module(); //初始化RPC鉴权模块
    out:
        return err;
    }

和其他文件系统一样,一个RPC管道是作为RPC管道文件系统下的一个文件存在的,下面是RPC管道的接口定义:

static const struct file_operations rpc_pipe_fops={
    .owner=THIS_MODULE,
    .llseek=no_llseek,
    .read=rpc_pipe_read, //读取管道数据
    .write=rpc_pipe_write, //写入管道数据
    .poll=rpc_pipe_poll, //轮询管道
    .ioctl=rpc_pipe_ioctl, //管道的ioctl
    .open=rpc pipe_open, //打开管道
    .release=rpc_pipe_release, //释放管道
    };

一个RPC管道就是RPC管道文件系统的一个节点,下面是RPC节点的定义:

struct rpc_inode {
    struct inode vfs_inode; //继承VFS节点
    void *private;
    struct list_head pipe;
    struct list_head in_upcall; //通知上层节点
    struct list_head in_downcall; //通知下层节点
    int pipelen;
    int nreaders; //读数据线程数
    int nwriters; //写数据线程数
    int nkern_readwriters;
    wait_queue_head_t waitq; //等待队列
    #define RPC_PIPE_WAIT_FOR_OPEN 1
    int flags;
    struct rpc_pipe_ops *ops; //管道选项
    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 *); //打开管道
    void (*destroy_msg)(struct rpc_pipe_msg *); //销毁管道消息
    };

为了利用RPC在Linux内核和用户空间之间进行通信,需要创建一个RPC管道,下面是创建一个RPC管道的实现过程:

代码1-2 创建RPC管道的过程

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); //创建目录
    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); //配置权限
    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;
    }

通过RPC管道文件系统,调用者可以像操作其他文件那样进行RPC管道的操作,常见的操作有读取数据、写入数据、查询管道信息、创建路径、删除路径、创建RPC管道等。

需要说明的是,当管道数据可读时,需要调用rpc_queue_upcall()函数通知用户空间,当管道可写入数据时,则不需要通知用户空间。在通知用户空间RPC管道的状态时,需要将消息封装在rpc_pipe_msg结构体中。下面是rpc_queue_upcall()函数的实现过程:

代码1-3 RPC通知用户空间的过程

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); //添加到消息队列
            rpci->pipelen+=msg->len;
            res=0;
        } else if (rpci->flags & RPC_PIPE_WAIT_FOR_OPEN) { //如果RPC管道等待打开
        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); //添加到消息队列
        rpci->pipelen += msg->len;
        res=0;
        }
        out:
        spin_unlock(&inode->i_lock);
        wake_up(&rpci->waitq); //唤醒等待队列
        return res;
    }

在auth_gss.c文件中,有RPC管道的运用实例,感兴趣的朋友可以自行研读。

 更多相关文章

·Android RPC远程调用
·SMSM状态通信处理过程
·SMD 数据通信的实现
·Android SMD数据通信概述
·浅析Android多媒体元数据