内核proc文件系统主要代码位于fs/proc目录内, 在内核称之为procsfs文件系统。看内核代码其实有一定的规律可言,尤其是这种与硬件无关的处理模块。查看内核模块代码首先需要先熟悉该模块API的使用,通过自己编写用例来使用,会更加对这个模块有深入的了解,其次看源代码,需要了解该模块的核心数据结构以及其设计原理,每个字段大概什么意思,后面需要根据使用的API 梳理代码就方便很多,经过长时间熟悉设计原则之后,就大概能够根据API 猜出里面一些大概流程然后根据问题看代码就会非常快。

struct proc_dir_entry

procfs文件系统的核心数据结构为proc_dir_enty,内核将proc目录下的文件或者文件夹都统一抽象成proc_dir_entry结构,按照一条条entry 组织成树形结构,该数据结构定义在fs/proc/internal.h文件中,在最新5.8.11版本中结构定义主要如下:

/** This is not completely implemented yet. The idea is to* create an in-memory tree (like the actual /proc filesystem* tree) of these proc_dir_entries, so that we can dynamically* add new files to /proc.** parent/subdir are used for the directory structure (every /proc file has a* parent, but "subdir" is empty for all non-directory entries).* subdir_node is used to build the rb tree "subdir" of the parent.*/
struct proc_dir_entry {/** number of callers into module in progress;* negative -> it's going away RSN*/atomic_t in_use;refcount_t refcnt;struct list_head pde_openers; /* who did ->open, but not ->release *//* protects ->pde_openers and all struct pde_opener instances */spinlock_t pde_unload_lock;struct completion *pde_unload_completion;const struct inode_operations *proc_iops;union {const struct proc_ops *proc_ops;const struct file_operations *proc_dir_ops;};const struct dentry_operations *proc_dops;union {const struct seq_operations *seq_ops;int (*single_show)(struct seq_file *, void *);};proc_write_t write;void *data;unsigned int state_size;unsigned int low_ino;nlink_t nlink;kuid_t uid;kgid_t gid;loff_t size;struct proc_dir_entry *parent;struct rb_root subdir;struct rb_node subdir_node;char *name;umode_t mode;u8 flags;u8 namelen;char inline_name[];
} __randomize_layout;

比较关键字段:

atomic_t in_use:该字段为原子操作数据,表明该entry正在被使用的数量,为防止多个进程同时使用造成冲突需要使用原子操作相关接口,对该数据的增加或删除分别封装在use_pde()和unuse_pde()函数,该函数位于fs\proc\node.c文件中,函数定义如下:

static inline int use_pde(struct proc_dir_entry *pde)
{return likely(atomic_inc_unless_negative(&pde->in_use));
}static void unuse_pde(struct proc_dir_entry *pde)
{if (unlikely(atomic_dec_return(&pde->in_use) == BIAS))complete(pde->pde_unload_completion);
}

每次对proc目录下的文件进行读取,打开,修改或者map操作之后都会首先增加in_use计数,当操作完毕之后再减少in_use计数。

refcount_t refcnt:引用计数, 和in_use不同,in_user侧重于有多少个用户或者进程增在操作该文件,而refcount_t refcnt为引用计数,即通过inode_operations操作中的lookup被调用此次即被查找次数。

struct list_head pde_openers:用于记录有所有打开还没有调用close的  opener,即当前文件正在被多少opener打开。

spinlock_t pde_unload_lock用于对pde_openers数据加锁,防止多个进程或者线程同时操作pde_openers数据

struct completion *pde_unload_completion:用于等待所有的pde_openers都操作完毕之后。

const struct inode_operations *proc_iops:procfs文件系统中不同的文件类型对应的不同inode_operations操作:

文件类型 const struct inode_operations *proc_iops
软链接 proc_link_inode_operations
文件夹directory proc_dir_inode_operations
文件file proc_file_inode_operations
proc根节点root proc_root_inode_operations
/proc/sys proc_sys_dir_operations

proc_ops or proc_dir_ops: 该结构为一个联合体,主要是根据文件类型,如果是一个文件则为proc_ops,如果是一个目录directory:proc_dir_ops

const struct dentry_operations *proc_dops: proc操作封装,类似于inode_operations。

const struct seq_operations *seq_ops or int (*single_show)(struct seq_file *, void *): 该结构为一个联合体,主要是针对用户读取/proc 相应文件的内核数据时,该数据是一个单项,还是需要通过一个迭代器循环遍历。

proc_write_t write:用户空间程序 往proc文件写数据时对应的操作方法。

void *data: 对应私有数据。

unsigned int state_size:私有数据大小

unsigned int low_ino: inode numerber 每个proc文件或者目录都有唯一一个,范围从PROC_DYNAMIC_FIRST(0xF0000000U) ~ 0xffffffff,

nlink_t nlink:链接文件,如果是文件夹则说明该文件夹下有多少个文件,从2开始,因为文件夹下面默认有"."和“..”,如果是一个文件创建链接则为1.

kuid_t uid: 该文件所属user id

kgid_t gid: 该文件所属group id.

loff_t size:  文件大小 或者偏移。

struct proc_dir_entry *parent: 该文件所属的parent entry.

struct rb_root subdir: 该文件夹内所有的文件

struct rb_node subdir_node: 该文件夹内所有的子文件夹

char *name: 该entry 名称

umode_t mode: 该文件模式, 支持模式主要有如下:

#define S_IFMT  00170000
#define S_IFSOCK 0140000
#define S_IFLNK  0120000
#define S_IFREG  0100000
#define S_IFBLK  0060000
#define S_IFDIR  0040000
#define S_IFCHR  0020000
#define S_IFIFO  0010000
#define S_ISUID  0004000
#define S_ISGID  0002000
#define S_ISVTX  0001000

u8 flags: 该entry flags

u8 namelen;: 该entry 名称长度

char inline_name[]: 该文件内部名称 一半和 name相同。

API列表

proc文件系统可以按照其文件类型:file,directory和symlink三种类型,下面按照上述三种类型进行说明

directory API

该类型文件主要是在proc文件系统内创建或者删除一个目录。

API名称 Description说明
struct proc_dir_entry *proc_mkdir(const char *name,  struct proc_dir_entry *parent) 在proc文件系统内创建一个directory, name为该directory 名称, parent为其归属的哪个parent directory,如果为NULL,则创建的directory位于/proc根目录内 
struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,  struct proc_dir_entry *parent, void *data) 在proc文件系统内创建一个directory, 与上述API相比多了一个参数data,代表的是传入到entry中的私有数据,后面处理需要用到
struct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode,     struct proc_dir_entry *parent) 在proc文件系统内创建一个directory, 与上述API相比多了一个参数mode, 代表由驱动程序指定该enntry 权限模式 不采用默认权限
void proc_remove(struct proc_dir_entry *de) 删除传入的entry
void remove_proc_entry(const char *name, struct proc_dir_entry *parent) 根据名称删除entry, 其中paren为要产出的entry的父entry,parent一定要传对 否则就会因找不到而无法删除。如果parent为NULL,则位于/proc根目录

directory API 用例

创建一个directory最简单的一个用例:  /proc 根目录下创建一个my_proc 目录,代码如下:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/proc_fs.h>
MODULE_LICENSE("GPL v2");#define MY_PROC "my_proc"static struct proc_dir_entry * my_proc_entry=NULL;static int __init my_proc_init(void)
{my_proc_entry = proc_mkdir(MY_PROC,NULL);if (NULL == my_proc_entry){return -ENODEV;}return 0;
}static void __exit my_proc_exit(void)
{remove_proc_entry(MY_PROC, my_proc_entry);
}module_init(my_proc_init);
module_exit(my_proc_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for my proc");
MODULE_VERSION("V1.0");

file API

file 类别API主要是用于在proc目录内创建fie,用于读取或者修改内核数据,主要API类型如下:

API名称 Description说明
struct proc_dir_entry *proc_create(const char *name, umode_t mode,    struct proc_dir_entry *parent,      const struct proc_ops *proc_ops) 创建名称为name的 proc文件,mode为文件模式包括权限等, parent为该文件所属的目录, proc_ops为对该文件操作封装,主要包括读写等功能
struct proc_dir_entry *proc_create_single_data(const char *name, umode_t mode,    struct proc_dir_entry *parent,  int (*show)(struct seq_file *, void *), void *data) 创建名称为name的 proc文件,mode为文件,parent为该文件所属的目录,一般使用该方法创建的proc文件是只读,所以仅仅提供了读show操作 , data为要读取的数据
struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
        struct proc_dir_entry *parent,    const struct proc_ops *proc_ops, void *data)
创建名称为name的 proc文件,mode为文件,parent为该文件所属的目录,与proc_create函数相比较 多个data参数,指明传入的私有数据即要操作的数据
int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) 删除parent目录内名称为name的子目录内的所有文件
void proc_remove(struct proc_dir_entry *de) 删除传入的entry
void remove_proc_entry(const char *name, struct proc_dir_entry *parent) 根据名称删除entry, 其中paren为要产出的entry的父entry,parent一定要传对 否则就会因找不到而无法删除。如果parent为NULL,则位于/proc根目录

file API用例一

使用proc_create_single_dataAPI创建两个文件分别为proc_1和proc_2,各种的私有数据分别为my_proc_1_data和my_proc_2_data,用例代码如下:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
MODULE_LICENSE("GPL v2");#define MY_PROC "my_proc"static struct proc_dir_entry * my_proc_entry=NULL;
static DEFINE_MUTEX(my_proc_lock);
static int my_proc_1_data=1;
static int my_proc_2_data=2;static int my_proc_1_show(struct seq_file *s, void *v)
{int * value = s->private;seq_printf(s, "proc_1 value:%d\n", *value);return 0;
}static int my_proc_2_show(struct seq_file *s, void *v)
{int * value = s->private;seq_printf(s, "proc_2 value:%d\n", *value);return 0;
}static int __init my_proc_init(void)
{my_proc_entry = proc_mkdir(MY_PROC, NULL);if (NULL == my_proc_entry){return -ENODEV;}proc_create_single_data("proc_1", 0, my_proc_entry, my_proc_1_show, &my_proc_1_data);proc_create_single_data("proc_2", 0, my_proc_entry, my_proc_2_show, &my_proc_2_data);return 0;
}static void __exit my_proc_exit(void)
{remove_proc_entry(MY_PROC, NULL);
}module_init(my_proc_init);
module_exit(my_proc_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for my proc");
MODULE_VERSION("V1.0");

proc_ops接口

proc_ops结构是对proc文件操作的封装,主要包括文件打开,读写等常用操作,该文件定义在include\linux\proc_fs.h文件中

struct proc_ops {unsigned int proc_flags;int (*proc_open)(struct inode *, struct file *);ssize_t (*proc_read)(struct file *, char __user *, size_t, loff_t *);ssize_t    (*proc_write)(struct file *, const char __user *, size_t, loff_t *);loff_t  (*proc_lseek)(struct file *, loff_t, int);int   (*proc_release)(struct inode *, struct file *);__poll_t (*proc_poll)(struct file *, struct poll_table_struct *);long    (*proc_ioctl)(struct file *, unsigned int, unsigned long);
#ifdef CONFIG_COMPATlong    (*proc_compat_ioctl)(struct file *, unsigned int, unsigned long);
#endifint   (*proc_mmap)(struct file *, struct vm_area_struct *);unsigned long (*proc_get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
} __randomize_layout;

seq_file接口

在早期的/proc文件系统的实现中,都是由驱动开发人员在创建proc文件时直接提供read,write方法,驱动开发人员直接操纵buf,但是存在这样一个问题当读取改文件时 要显示很多或者里面很很多项目是,buf经常不够,很容易造成越界现象,因此后来经过改进seq_file 接口,该接口通过创建一个简单的迭代器对象,该对象用来表示项目序列中的位置,每前进一步,该对象输出序列中的一个项目,这样每此迭代只显示一个迭代。

接口API description描述
ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) 读取文件
int seq_open(struct file *file, const struct seq_operations *op) 打开文件
loff_t seq_lseek(struct file *file, loff_t offset, int whence) lseek操作
int single_release(struct inode *inode, struct file *file) 单次迭代释放
int seq_release(struct inode *inode, struct file *file) 多次迭代释放
int single_open(struct file *file, int (*show)(struct seq_file *, void *),    void *data) 单次迭代打开
int single_open_size(struct file *file, int (*show)(struct seq_file *, void *),   void *data, size_t size) 代词迭代打开,包括数据大小
int single_open_size(struct file *file, int (*show)(struct seq_file *, void *),     void *data, size_t size) 写操作
struct list_head *seq_list_start(struct list_head *head, loff_t pos) seq list迭代开始
struct list_head *seq_list_start_head(struct list_head *head, loff_t pos) seq list头
struct list_head *seq_list_next(void *v, struct list_head *head, loff_t *ppos) seq 下次迭代

seq file将操作将整个迭代进行封装 每次迭代顺序分为 start->show->next->stop一个完整过程,开发人员只需要实现open 和show方法即可,其他proc_ops结构只需要挂载seq即可。

其中进行show方法实现时,不能使用printk进行打印,而是使用seq_file提供的方法进行打印:

接口API description描述
void seq_printf(struct seq_file *m, const char *f, ...) 等价与printf函数,需要将show函数传入的seq_file结构传递给这个函数,如果seq_printf返回一个非零值,则意味着缓冲区已满
void seq_putc(struct seq_file *m, char c) 打印一个char
void seq_puts(struct seq_file *m, const char *s) 打印一个字符串
void seq_escape(struct seq_file *m, const char *s, const char *esc) 等价于seq_puts函数,若s中的某个字符也存在于esc参数,则该字符以八进制形式打印。传递给esc参数的常见值“\t\n\\”,可以避免要输出的空白字符弄乱屏幕或者迷惑shell脚本

使用seq接口时,一般proc_open函数需要开发自己实现,主要时调用seq_open函数将 seq_operations复制给file接口,以从proc_op便来接管整个操作

seq按照迭代次数分为单次或者多次迭代,两者在proc_ops挂载时,使用pro_relase接口不同,下面分别展示单次和多次迭代用例

seq单次用例

使用seq接口展示单次show用例,读取my_proc文件时,显示其中内核中的my_proc_data值:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
MODULE_LICENSE("GPL v2");#define MY_PROC "my_proc"static struct proc_dir_entry * my_proc_entry=NULL;static int my_proc_show(struct seq_file *m, void * v)
{seq_printf(m,"my proc test\n");return 0;
}static int my_proc_open(struct inode *inode, struct file *file)
{return single_open(file, my_proc_show, NULL);
}static const struct proc_ops my_proc_ops = {.proc_open = my_proc_open,.proc_read = seq_read,.proc_lseek= seq_lseek,.proc_release = single_release,
};
static int __init my_proc_init(void)
{my_proc_entry = proc_create(MY_PROC,S_IRUGO, NULL, &my_proc_ops);if (NULL == my_proc_entry){return -ENOMEM;}return 0;
}static void __exit my_proc_exit(void)
{remove_proc_entry(MY_PROC, NULL);
}module_init(my_proc_init);
module_exit(my_proc_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for my proc");
MODULE_VERSION("V1.0");

seq多次迭代用例

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
MODULE_LICENSE("GPL v2");#define MY_PROC "my_proc"static struct proc_dir_entry * my_proc_entry=NULL;
static DEFINE_MUTEX(my_proc_lock);
static int my_proc_data=0;
static void * my_proc_start(struct seq_file *s, loff_t *pos)
{mutex_lock(&my_proc_lock);printk(KERN_INFO "my_proc start:%d\n", *pos);if (*pos>5){return NULL;}return (void *)&my_proc_data;}static void *my_proc_next(struct seq_file *s, void *v, loff_t *pos)
{if (*pos>5){return NULL;}my_proc_data++;(*pos)++;return (void *)&my_proc_data;
}static int my_proc_show(struct seq_file *s, void *v)
{int * value = v;seq_printf(s, "my_proc value:%d\n", *value);return 0;
}
static void my_proc_stop(struct seq_file *s, void *v)
{my_proc_data = 0;mutex_unlock(&my_proc_lock);
}static const struct seq_operations my_seq_ops = {.start = my_proc_start,.next  = my_proc_next,.stop  = my_proc_stop,.show  = my_proc_show,
};
static int my_proc_open(struct inode *inode, struct file *file)
{return seq_open(file, &my_seq_ops);
}static const struct proc_ops my_proc_ops = {.proc_open = my_proc_open,.proc_read = seq_read,.proc_lseek= seq_lseek,.proc_release = seq_release,
};
static int __init my_proc_init(void)
{my_proc_entry = proc_create(MY_PROC,S_IRUGO, NULL, &my_proc_ops);if (NULL == my_proc_entry){return -ENOMEM;}return 0;
}static void __exit my_proc_exit(void)
{remove_proc_entry(MY_PROC, NULL);
}module_init(my_proc_init);
module_exit(my_proc_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for my proc");
MODULE_VERSION("V1.0");

symlink接口

symlink接口主要提供一个软链接功能:

API名称 Description说明
struct proc_dir_entry *proc_symlink(const char *name,
        struct proc_dir_entry *parent, const char *dest)
创建名称为name的 proc 链接文件, parent为该文件所属的目录, dest为要链接为文件名
void proc_remove(struct proc_dir_entry *de) 删除传入的entry
void remove_proc_entry(const char *name, struct proc_dir_entry *parent) 根据名称删除entry, 其中paren为要产出的entry的父entry,parent一定要传对 否则就会因找不到而无法删除。如果parent为NULL,则位于/proc根目录

symlink用例

symlink用例相对比较简单:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
MODULE_LICENSE("GPL v2");#define MY_PROC "my_proc"static struct proc_dir_entry * my_proc_entry=NULL;
static struct proc_dir_entry * my_proc_link =NULL;
static DEFINE_MUTEX(my_proc_lock);
static int my_proc_data=0;
static void * my_proc_start(struct seq_file *s, loff_t *pos)
{mutex_lock(&my_proc_lock);printk(KERN_INFO "my_proc start:%d\n", *pos);if (*pos>5){return NULL;}return (void *)&my_proc_data;}static void *my_proc_next(struct seq_file *s, void *v, loff_t *pos)
{if (*pos>5){return NULL;}my_proc_data++;(*pos)++;return (void *)&my_proc_data;
}static int my_proc_show(struct seq_file *s, void *v)
{int * value = v;seq_printf(s, "my_proc value:%d\n", *value);return 0;
}
static void my_proc_stop(struct seq_file *s, void *v)
{my_proc_data = 0;mutex_unlock(&my_proc_lock);
}static const struct seq_operations my_seq_ops = {.start = my_proc_start,.next  = my_proc_next,.stop  = my_proc_stop,.show  = my_proc_show,
};
static int my_proc_open(struct inode *inode, struct file *file)
{return seq_open(file, &my_seq_ops);
}static const struct proc_ops my_proc_ops = {.proc_open = my_proc_open,.proc_read = seq_read,.proc_lseek= seq_lseek,.proc_release = seq_release,
};
static int __init my_proc_init(void)
{my_proc_entry = proc_create(MY_PROC,S_IRUGO, NULL, &my_proc_ops);if (NULL == my_proc_entry){return -ENOMEM;}my_proc_link =proc_symlink("my_link", NULL, MY_PROC);if (NULL == my_proc_link){remove_proc_entry(MY_PROC, NULL);return -ENOMEM;}return 0;
}static void __exit my_proc_exit(void)
{remove_proc_entry("my_link", NULL);remove_proc_entry(MY_PROC, NULL);
}module_init(my_proc_init);
module_exit(my_proc_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for my proc");
MODULE_VERSION("V1.0");

总结

现在内核发展要求一般不建议在proc文件再新添加文件,因为proc使用起来显得特别臃肿,由于没有THIS_MODULE,有可能会发生删除正在被使用的文件。另外一个问题就是同一个名字的注册问题。

随着sysfs文件系统完善,一般如果添加新的配置项建议加入到sysfs文件系统中。

linux procfs文件系统(2)相关推荐

  1. 查看linux文件系统结构,linux的文件系统架构

    linux的文件系统是由若干个树形以及链表的结构组成的,其中众所周知,所有的目录和文件组成了视觉上"一棵"树,在这"一棵"树背后有一条挂载链表,如果说每一个文件 ...

  2. linux的文件系统架构

    linux的文件系统是由若干个树形以及链表的结构组成的,其中众所周知,所有的目录和文件组成了视觉上"一棵"树,在这"一棵"树背后有一条挂载链表,如果说每一个文件 ...

  3. linux改文件系统类型,linux文件系统类型及mount

    1. 查看系统所支持的文件系统的方法 1.1 通过命令查看 mount df fdisk file parted fstab 1.2 通过系统文件查看 cat /proc/filesystems ls ...

  4. Linux _ 文件系统的类型和结构

    Linux _ 文件系统的类型和结构 一.Linux文件系统的类型概念 分类 SCSI与IDE设备命令 交换分区 二.Linux文件系统的结构 文件系统逻辑结构 文件系统结构 基本目录 Linux文件 ...

  5. 文件系统管理 之 Linux 创建文件系统及挂载文件系统流程详解

    作者:北南南北 来自: LinuxSir.Org 摘要:本文对新增硬盘,切割硬盘,创建硬盘分区,为硬盘分区创建文件系统,以及加载文件系统的流程做总结性论述:主要是为初学者弄清楚这一操作过程:本文涉及f ...

  6. linux的文件系统及节点表

    linux的文件系统及节点表 一  linux的文件系统 1 我们都知道当我们安装linux时会首先给系统分区,然后我们会把分区格式化成EXT3格式的文件系统.那么在linux系统中还有没有其他的文件 ...

  7. 嵌入式linux文件系统格式,嵌入式Linux的文件系统分区及数据读写方法与流程

    本发明涉及Linux系统的数据存储管理领域,特别是涉及一种基于NANDFlash存储器和UBIFS文件系统的嵌入式Linux的文件系统分区及数据读写方法. 背景技术: ::目前Linux操作系统由于源 ...

  8. Linux虚拟文件系统解析

    概述 虚拟文件系统(Virtual Filesystem Switch,简称VFS)所有的数据结构都是在运行以后建立的,并在卸载时删除,在磁盘中并没有存储这些数据结构.虚拟文件系统只有和实际文件系统( ...

  9. linux虚拟文件系统浅析

    linux虚拟文件系统浅析 虚拟文件系统(VFS) 在我看来, "虚拟"二字主要有两层含义: 1, 在同一个目录结构中, 可以挂载着若干种不同的文件系统. VFS隐藏了它们的实现细 ...

最新文章

  1. c语言输入姓名比较是否同性,C语言基础--选择题
  2. linux安装 ”NTFS“ 文件系统
  3. java缓冲流,BufferedReader,BufferedWriter 详解
  4. 实用代码-C#之IP地址和整数的互转
  5. NYOJ 658 字符串右移
  6. 15个实用的grep示例
  7. JavaWeb的web.xml标签元素(一)
  8. 划分数算法概述及习题
  9. dos命令行设置网络优先级_实用批处理:不用下软件就能 批量改名,修复网络,删除小文件...
  10. 山丽防水墙客户端的卸载
  11. python取下标_python获取下标
  12. 【干货分享】硬件测试工程师必备基本技能,看这一篇就够!
  13. 护眼灯频闪是什么意思?如何消除led灯频闪
  14. ubuntu 图形化桌面
  15. Tcl/Tk入门(上)
  16. 多彩的产品之年——产品经理一席谈
  17. JAVA垃圾回收-可达性分析算法
  18. Grapher如何显示与绘图线成角度的线图标签丨使用教程
  19. 数据太大,记事本打不开怎么办?
  20. 工控服务器什么作用,服务器与工控机的用处和区别在哪?

热门文章

  1. 快速实现手势解锁功能
  2. 20170124L07-03-01老男孩linux运维实战培训-生产环境大于254台机器网段划分及路由解决方案案例...
  3. 大数据Java基础第十二天作业
  4. 二分查找:在有序数组中搜索大于等于x的数的最小下标
  5. hadoop hive hbase 入门学习 (二)
  6. 发布代码小助手V2.1发布了——Code2HTML工具
  7. 修改系统Documents and Settings目录的位置
  8. 在界面中显示文本内容
  9. 程序员常见保健方法【转贴】
  10. C语言三个数排序,普通方法及进阶(不引入第三变量交换数值法)