红联Linux门户
Linux帮助

鸟哥另一力作<鸟哥 linux网络架设>

发布时间:2006-08-01 00:12:53来源:红联作者:kong343526
鸟哥另一力作
文章评论

共有 1260 条评论

  1. SUPERXIAOBON 于 2006-10-18 10:29:23发表:

    谢过楼主

  2. hnzjl 于 2006-10-18 10:23:27发表:

    again

  3. hnzjl 于 2006-10-18 10:22:45发表:

    again

  4. hnzjl 于 2006-10-18 10:21:56发表:

    again

  5. hnzjl 于 2006-10-18 10:20:51发表:

    thank

  6. goldpei 于 2006-10-16 16:41:03发表:

    都说好一定要看

  7. goldpei 于 2006-10-16 16:34:47发表:

    好东西

  8. cq771004 于 2006-10-16 15:57:43发表:

    好东西就应该一起分享。

  9. liyong123 于 2006-10-16 15:42:40发表:

    1. 前言

    在fs/seq_file.c中定义了关于seq操作的一系列顺序读取的函数,这些函数最早是在2001年就引入了,但以前内核中一直用得不多,而到了2.6内核后,许多/proc的只读文件中大量使用了seq函数处理。
    以下内核源码版本为2.6.17.11。

    2. seq相关数据结构

    2.1 seq文件结构
    struct seq_file {
    char *buf;
    size_t size;
    size_t from;
    size_t count;
    loff_t index;
    loff_t version;
    struct mutex lock;
    struct seq_operations *op;
    void *private;
    };

    struct seq_file描述了seq处理的缓冲区及处理方法,buf是动态分配的,大小不小于PAGE_SIZE,通常这个结构是通过struct file结构中的private_data来指向的。
    char *buf:seq流的缓冲区
    size_t size:缓冲区大小
    size_t from:from指向当前要显示的数据头位置
    size_t count:缓冲区中已有的数据长度
    loff_t index:数据记录索引值
    loff_t version:版本号,是struct file的版本号的拷贝
    struct mutex lock:seq锁
    struct seq_operations *op:seq操作结构,定义数据显示的操作函数
    void *private:私有数据


    2.2 seq操作结构

    seq的操作结构比较简单,就是4个操作函数,完成开始、停止、显示和取下一个操作:

    /* include/linux/seq_file.h */
    struct seq_operations {
    void * (*start) (struct seq_file *m, loff_t *pos);
    void (*stop) (struct seq_file *m, void *v);
    void * (*next) (struct seq_file *m, void *v, loff_t *pos);
    int (*show) (struct seq_file *m, void *v);
    };


    3. seq操作函数

    seq操作包括以下一系列函数:
    int seq_open(struct file *, struct seq_operations *);
    打开seq流,为struct file分配struct seq_file结构,并定义seq_file的操作;

    ssize_t seq_read(struct file *, char __user *, size_t, loff_t *);
    从seq流中读数据到用户空间,其中循环调用了struct seq_file中的各个函数来读数据;
    ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
    {
    struct seq_file *m = (struct seq_file *)file->private_data;
    size_t copied = 0;
    loff_t pos;
    size_t n;
    void *p;
    int err = 0;
    // 先加锁
    mutex_lock(&m->lock);
    /*
    * seq_file->op->..m_start/m_stop/m_next may do special actions
    * or optimisations based on the file->f_version, so we want to
    * pass the file->f_version to those methods.
    *
    * seq_file->version is just copy of f_version, and seq_file
    * methods can treat it simply as file version.
    * It is copied in first and copied out after all operations.
    * It is convenient to have it as part of structure to avoid the
    * need of passing another argument to all the seq_file methods.
    */
    m->version = file->f_version;
    /* grab buffer if we didn't have one */
    // 如果struct seq_file结构中的缓冲区没有分配的话,
    // 分配缓冲,大小为PAGE_SIZE
    if (!m->buf) {
    m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
    if (!m->buf)
    goto Enomem;
    }
    /* if not empty - flush it first */
    // count表示当时有多少数据还没有传给用户空间
    // 尽量先将这些数据传出
    if (m->count) {
    n = min(m->count, size);
    err = copy_to_user(buf, m->buf + m->from, n);
    if (err)
    goto Efault;
    m->count -= n;
    m->from += n;
    size -= n;
    buf += n;
    copied += n;
    if (!m->count)
    m->index++;
    if (!size)
    goto Done;
    }
    // 进行主要传数据过程,缓冲区中至少要有一个记录单位的数据
    /* we need at least one record in buffer */
    while (1) {
    // 数据记录的位置
    pos = m->index;
    // 初始化操作,返回值为对象相关指针
    p = m->op->start(m, &pos);
    err = PTR_ERR(p);
    if (!p || IS_ERR(p))
    break;
    // 执行具体的显示过程
    err = m->op->show(m, p);
    if (err)
    break;
    // 当前缓冲区中的实际数据小于缓冲区大小,转到填数据部分
    if (m->count < m->size)
    goto Fill;
    // 否则说明一个记录的数据量太大,原来缓冲区大小不够;
    // 先停操作,重新分配缓冲区,大小增加一倍,重新操作,
    // 要保证缓冲区大小大于一个数据记录的大小
    m->op->stop(m, p);
    kfree(m->buf);
    m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
    if (!m->buf)
    goto Enomem;
    m->count = 0;
    m->version = 0;
    }
    m->op->stop(m, p);
    m->count = 0;
    goto Done;
    Fill:
    // 继续读数据到缓冲区
    /* they want more? let's try to get some more */
    while (m->count < size) {
    size_t offs = m->count;
    loff_t next = pos;
    p = m->op->next(m, p, &next);
    if (!p || IS_ERR(p)) {
    err = PTR_ERR(p);
    break;
    }
    err = m->op->show(m, p);
    if (err || m->count == m->size) {
    m->count = offs;
    break;
    }
    pos = next;
    }
    // 停seq
    m->op->stop(m, p);
    n = min(m->count, size);
    // 将数据拷贝到用户空间
    err = copy_to_user(buf, m->buf, n);
    if (err)
    goto Efault;
    copied += n;
    m->count -= n;
    if (m->count)
    m->from = n;
    else
    pos++;
    m->index = pos;
    Done:
    if (!copied)
    copied = err;
    else
    *ppos += copied;
    file->f_version = m->version;
    mutex_unlock(&m->lock);
    return copied;
    Enomem:
    err = -ENOMEM;
    goto Done;
    Efault:
    err = -EFAULT;
    goto Done;
    }

    loff_t seq_lseek(struct file *, loff_t, int);
    定位seq流当前指针偏移;
    int seq_release(struct inode *, struct file *);
    释放seq流所分配的动态内存空间,即struct seq_file的buf及其本身;

    int seq_escape(struct seq_file *, const char *, const char *);
    将seq流中需要进行转义的字符转换为8进制数字;

    int seq_putc(struct seq_file *m, char c);
    向seq流中写一个字符

    int seq_puts(struct seq_file *m, const char *s);
    向seq流中写一个字符串

    int seq_printf(struct seq_file *, const char *, ...)
    __attribute__ ((format (printf,2,3)));
    向seq流方式写格式化信息;

    int seq_path(struct seq_file *, struct vfsmount *, struct dentry *, char *);
    在seq流中添加路径信息,路径字符都转换为8进制数。

    int seq_release_private(struct inode *, struct file *);
    释放seq_file的private然后进行seq_release

    3. 用seq流填写/proc文件

    以下使用文件/proc/net/ip_conntrack的生成代码来说明seq流的使用:

    3.1 创立文件
    以前2.4版本中使用proc_net_create()来建立/proc/net下的文件,现在使用seq流时要使用proc_net_fops_create()函数来创建,区别在于函数的最后一个参数,proc_net_create()的是一个函数指针,而proc_net_fops_create()的是一个文件操作指针:
    ......
    proc = proc_net_fops_create("ip_conntrack", 0440, &ct_file_ops);
    ......

    proc_net_fops_create()函数其实也很简单,调用create_proc_entry()函数建立/proc文件项,然后将文件项的操作结构指针指向所提供的文件操作指针:
    static inline struct proc_dir_entry *proc_net_fops_create(const char *name,
    mode_t mode, const struct file_operations *fops)
    {
    struct proc_dir_entry *res = create_proc_entry(name, mode, proc_net);
    if (res)
    res->proc_fops = fops;
    return res;
    }

    3.2 文件操作结构
    /proc/net/ip_conntrack所用的文件结构如下:
    static struct file_operations ct_file_ops = {
    .owner = THIS_MODULE,
    .open = ct_open,
    .read = seq_read,
    .llseek = seq_lseek,
    .release = seq_release_private,
    };
    可见,结构中除了open()函数是需要自定义外,其他的读、定位、释放函数都可以用seq标准函数。

    3.3 open函数定义
    open函数主要就是调用seq_open()函数将一个struct seq_operations结构和struct file链接起来,如果需要有私有数据的话,需要分配出动态空间作为struct seq_file的私有数据:
    static int ct_open(struct inode *inode, struct file *file)
    {
    struct seq_file *seq;
    struct ct_iter_state *st;
    int ret;
    st = kmalloc(sizeof(struct ct_iter_state), GFP_KERNEL);
    if (st == NULL)
    return -ENOMEM;
    ret = seq_open(file, &ct_seq_ops);
    if (ret)
    goto out_free;
    seq = file->private_data;
    seq->private = st;
    memset(st, 0, sizeof(struct ct_iter_state));
    return ret;
    out_free:
    kfree(st);
    return ret;
    }
    简单的如exp_open()函数,就只调用seq_open()函数就完了:
    static int exp_open(struct inode *inode, struct file *file)
    {
    return seq_open(file, &exp_seq_ops);
    }

    3.4 seq操作结构

    static struct seq_operations ct_seq_ops = {
    .start = ct_seq_start,
    .next = ct_seq_next,
    .stop = ct_seq_stop,
    .show = ct_seq_show
    };
    这个结构就是填写4个操作函数:

    start()函数完成读数据前的一些预先操作,通常如加锁,定位数据记录位置等,该函数返回值就是show()函数第二个参数:
    static void *ct_seq_start(struct seq_file *seq, loff_t *pos)
    {
    read_lock_bh(&ip_conntrack_lock);
    return ct_get_idx(seq, *pos);
    }

    stop()函数完成读数据后的一些恢复操作,如解锁等:
    static void ct_seq_stop(struct seq_file *s, void *v)
    {
    read_unlock_bh(&ip_conntrack_lock);
    }

    next()函数定位数据下一项:
    static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
    {
    (*pos)++;
    return ct_get_next(s, v);
    }

    show()函数实现读数据过程,将要输出的数据直接用seq_printf()函数打印到seq流缓冲区中,由seq_printf()函数输出到用户空间:
    static int ct_seq_show(struct seq_file *s, void *v)
    {
    // start()虽然返回的是struct list_head的指针,
    // 但struct ip_conntrack_tuple_hash结构的第一
    // 项参数就是struct list_head,所以可以进行直接
    // 类型转换而不用再计算偏移量
    const struct ip_conntrack_tuple_hash *hash = v;
    const struct ip_conntrack *conntrack = tuplehash_to_ctrack(hash);
    struct ip_conntrack_protocol *proto;
    ASSERT_READ_LOCK(&ip_conntrack_lock);
    IP_NF_ASSERT(conntrack);
    /* we only want to print DIR_ORIGINAL */
    if (DIRECTION(hash))
    return 0;
    proto = __ip_conntrack_proto_find(conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum);
    IP_NF_ASSERT(proto);
    // 以下打印连接和协议信息
    if (seq_printf(s, "%-8s %u %ld ",
    proto->name,
    conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum,
    timer_pending(&conntrack->timeout)
    ? (long)(conntrack->timeout.expires - jiffies)/HZ
    : 0) != 0)
    return -ENOSPC;
    ......
    if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use)))
    return -ENOSPC;
    return 0;
    }

    4. 结论

    seq流函数的使用保证了数据能顺序输出,这也就是/proc只读文件中使用它的最大原因吧。

  10. twbxhl 于 2006-10-13 18:59:32发表:

    漏了一个,在顶

  11. twbxhl 于 2006-10-13 18:52:52发表:

    还要继续顶

  12. twbxhl 于 2006-10-13 18:51:26发表:

    不顶不行啊

  13. twbxhl 于 2006-10-13 18:50:03发表:

    我又来顶啦

  14. twbxhl 于 2006-10-13 18:48:51发表:

    一定要顶

  15. 于 2006-10-13 18:41:41发表:

    kankan

  16. mathice 于 2006-10-12 09:32:29发表:

    实在是好,所以把今天能加的分全加以资鼓励

  17. hoohezi 于 2006-10-10 21:08:13发表:

    寻觅中

  18. mrhaigui 于 2006-10-10 18:00:40发表:

    我看下先

  19. yueguangm 于 2006-09-29 10:26:09发表:

    我是新来得

  20. yueguangm 于 2006-09-29 10:24:54发表:

    好好学习

  21. billy_phd 于 2006-09-28 19:35:05发表:

    刚刚下了,好精彩,感谢ing

  22. billy_phd 于 2006-09-28 19:33:44发表:

    VBrid的确很强!

  23. sskmyyq 于 2006-09-28 16:46:47发表:

  24. sky1000000 于 2006-09-28 00:01:38发表:

    学习!

  25. 82080747 于 2006-09-27 20:40:07发表:

    红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享 红联Linux论坛|LinuX技巧|LinuX教程分享

  26. superyu 于 2006-09-27 16:57:46发表:

    我还顶

  27. superyu 于 2006-09-27 16:57:29发表:

    我顶

  28. superyu 于 2006-09-27 16:56:52发表:

    看一看

  29. ForceZerg 于 2006-09-27 16:13:00发表:

    支持开源 ,谢谢楼主

  30. ForceZerg 于 2006-09-27 16:12:53发表:

    支持开源 ,谢谢楼主

  31. ForceZerg 于 2006-09-27 16:12:39发表:

    支持开源 ,谢谢楼主

  32. ForceZerg 于 2006-09-27 16:12:29发表:

    支持开源 ,谢谢楼主

  33. bobowoya 于 2006-09-26 12:06:50发表:

    bird good2 4

  34. bobowoya 于 2006-09-26 12:06:41发表:

    bird good2 3

  35. bobowoya 于 2006-09-26 12:06:25发表:

    bird good2

  36. bobowoya 于 2006-09-26 12:06:07发表:

    bird good.1

  37. luckvalley 于 2006-09-26 12:01:30发表:

    挺好的。。顶

  38. luckvalley 于 2006-09-26 11:59:00发表:

    顶你吧。。没有什么可说的。。谢谢谢谢!!

  39. zzyzzyhong 于 2006-09-26 02:59:26发表:

    再次谢谢楼主了

  40. zzyzzyhong 于 2006-09-26 02:59:06发表:

    谢谢楼主了