Linux内核PROC文件系统的初始化和主要函数分析
2017-04-01 22:57
555 查看
Linux内核PROC文件系统的初始化和主要函数分析
written by aweii
一、概述(内核版本2.4.0)
系统启动初始化阶段,调用init_proc_fs进行proc文件系统的注册。内核初始化完成后,可由初始化用户进程(如init进程之类)调用mount -t proc /dev/null /proc 进行挂接。
二、结构
(1)虚拟目录结构
struct proc_dir_entry {
unsigned short low_ino;
unsigned short namelen;
const char *name;
mode_t mode;
nlink_t nlink;
uid_t uid;
gid_t gid;
unsigned long size;
struct inode_operations * proc_iops;
struct file_operations * proc_fops;
get_info_t *get_info;
struct module *owner;
struct proc_dir_entry *next, *parent, *subdir;
void *data;
read_proc_t *read_proc;
write_proc_t *write_proc;
atomic_t count; /* use count */
int deleted; /* delete flag */
kdev_t rdev;
};
inode->generic_ip指向proc_dir_entry,proc_lookup()通过proc_dir_entry的subdir链表搜寻其子节点的proc_dir_entry。
(2)
三、主要代码分析
(1)文件系统初始化
-------------init_proc_fs
static DECLARE_FSTYPE(proc_fs_type, "proc", proc_read_super, FS_SINGLE);
static int __init init_proc_fs(void)
{
int err = register_filesystem(&proc_fs_type); //注册文件系统proc_fs_type,成员函数read_super指向proc_read_super
if (!err) {
proc_mnt = kern_mount(&proc_fs_type);//创建vfs_mount结构,挂接在proc_fs_type下,同时生成的元素包括超级块super_block和根节点root_inode
err = PTR_ERR(proc_mnt);
if (IS_ERR(proc_mnt))
unregister_filesystem(&proc_fs_type);
else
err = 0;
}
return err;
}
(2)/proc/下的主要子节点初始化
--------------proc_misc_init
void __init proc_misc_init(void)
{
struct proc_dir_entry *entry;
static struct {
char *name;
int (*read_proc)(char*,char**,off_t,int,int*,void*);
} *p, simple_ones[] = {
{"loadavg", loadavg_read_proc},
{"uptime", uptime_read_proc},
{"meminfo", meminfo_read_proc},
{"version", version_read_proc},
{"cpuinfo", cpuinfo_read_proc},
#ifdef CONFIG_PROC_HARDWARE
{"hardware", hardware_read_proc},
#endif
#ifdef CONFIG_STRAM_PROC
{"stram", stram_read_proc},
#endif
#ifdef CONFIG_DEBUG_MALLOC
{"malloc", malloc_read_proc},
#endif
#ifdef CONFIG_MODULES
{"modules", modules_read_proc},
{"ksyms", ksyms_read_proc},
#endif
{"stat", kstat_read_proc},
{"devices", devices_read_proc},
{"partitions", partitions_read_proc},
#if !defined(CONFIG_ARCH_S390)
{"interrupts", interrupts_read_proc},
#endif
{"filesystems", filesystems_read_proc},
{"dma", dma_read_proc},
{"ioports", ioports_read_proc},
{"cmdline", cmdline_read_proc},
#ifdef CONFIG_SGI_DS1286
{"rtc", ds1286_read_proc},
#endif
{"locks", locks_read_proc},
{"mounts", mounts_read_proc},
{"swaps", swaps_read_proc},
{"iomem", memory_read_proc},
{"execdomains", execdomains_read_proc},
{NULL,}
};
for (p = simple_ones; p->name; p++)
create_proc_read_entry(p->name, 0, NULL, p->read_proc, NULL);
/* And now for trickier ones */
entry = create_proc_entry("kmsg", S_IRUSR, &proc_root);
if (entry)
entry->proc_fops = &proc_kmsg_operations;
proc_root_kcore = create_proc_entry("kcore", S_IRUSR, NULL);
if (proc_root_kcore) {
proc_root_kcore->proc_fops = &proc_kcore_operations;
proc_root_kcore->size =
(size_t)high_memory - PAGE_OFFSET + PAGE_SIZE;
}
if (prof_shift) {
entry = create_proc_entry("profile", S_IWUSR | S_IRUGO, NULL);
if (entry) {
entry->proc_fops = &proc_profile_operations;
entry->size = (1+prof_len) * sizeof(unsigned int);
}
}
#ifdef __powerpc__
{
extern struct file_operations ppc_htab_operations;
entry = create_proc_entry("ppc_htab", S_IRUGO|S_IWUSR, NULL);
if (entry)
entry->proc_fops = &ppc_htab_operations;
}
#endif
entry = create_proc_read_entry("slabinfo", S_IWUSR | S_IRUGO, NULL,
slabinfo_read_proc, NULL);
if (entry)
entry->write_proc = slabinfo_write_proc;
}
(3)创建只读子节点的函数,能同时设置read_proc
-------------------------create_proc_read_entry
extern inline struct proc_dir_entry *create_proc_read_entry(const char *name,
mode_t mode, struct proc_dir_entry *base,
read_proc_t *read_proc, void * data)
{
struct proc_dir_entry *res=create_proc_entry(name,mode,base); //创建名为name的proc_dir_entry,其父节点为base
if (res) {
res->read_proc=read_proc; //设置read_proc方法
res->data=data;
}
return res;
}
(4)创建子节点的函数
-------------------------create_proc_entry
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
struct proc_dir_entry *parent)
{
struct proc_dir_entry *ent = NULL;
const char *fn = name;
int len;
if (!parent && xlate_proc_name(name, &parent, &fn) != 0)
goto out;
len = strlen(fn);
ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL); //分配proc_dir_entry,其后紧跟name+"\0",所以空间大小为sizeof(struct proc_dir_entry) + strlen(name) + 1
if (!ent)
goto out;
memset(ent, 0, sizeof(struct proc_dir_entry));
memcpy(((char *) ent) + sizeof(*ent), fn, len + 1);//拷贝name到proc_dir_entry后
ent->name = ((char *) ent) + sizeof(*ent); //name指向它
ent->namelen = len;
//目录节点设置方法
if (S_ISDIR(mode)) {
if ((mode & S_IALLUGO) == 0)
mode |= S_IRUGO | S_IXUGO;
ent->proc_fops = &proc_dir_operations;
ent->proc_iops = &proc_dir_inode_operations;
ent->nlink = 2;
} else {
if ((mode & S_IFMT) == 0)
mode |= S_IFREG;
if ((mode & S_IALLUGO) == 0)
mode |= S_IRUGO;
ent->nlink = 1;
}
ent->mode = mode;
proc_register(parent, ent); //级联到父节点
out:
return ent;
}
(5)目录搜索函数
根据级联关系进行循环搜索,和目录中搜索文件一样,都要比对名称
-------------------------proc_lookup
struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry)
{
struct inode *inode;
struct proc_dir_entry * de;
int error;
error = -ENOENT;
inode = NULL;
de = (struct proc_dir_entry *) dir->u.generic_ip;
if (de) {
for (de = de->subdir; de ; de = de->next) {
if (!de || !de->low_ino)
continue;
if (de->namelen != dentry->d_name.len)
continue;
if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {
int ino = de->low_ino;
error = -EINVAL;
inode = proc_get_inode(dir->i_sb, ino, de);
break;
}
}
}
if (inode) {
dentry->d_op = &proc_dentry_operations;
d_add(dentry, inode);
return NULL;
}
return ERR_PTR(error);
}
(6)文件通用读函数,它调用proc_dir_entry的get_info或read_proc成员函数
注意proc_file_read和具体的read_proc的搭配使用方法,ppos和其他普通文件读函数相同,
但要注意ppos、start值的交互变化。
proc_file_read循环调用read_proc,read_proc每次最多只读PROC_BLOCK_SIZE(4K页的3/4,3KB),
保证每次读入量都能容纳进page页面中。根据ppos内容在page中的偏移,调节start指针
-----------------------------proc_file_read
static struct file_operations proc_file_operations = {
llseek: proc_file_lseek,
read: proc_file_read,
write: proc_file_write,
};
#define PROC_BLOCK_SIZE (PAGE_SIZE - 1024)
static ssize_t
proc_file_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
{
struct inode * inode = file->f_dentry->d_inode;
char *page;
ssize_t retval=0;
int eof=0;
ssize_t n, count;
char *start;
struct proc_dir_entry * dp;
dp = (struct proc_dir_entry *) inode->u.generic_ip;
if (!(page = (char*) __get_free_page(GFP_KERNEL)))
return -ENOMEM;
while ((nbytes > 0) && !eof)
{
count = MIN(PROC_BLOCK_SIZE, nbytes);
start = NULL;
if (dp->get_info) {
/*
* Handle backwards compatibility with the old net
* routines.
*/
n = dp->get_info(page, &start, *ppos, count);
if (n < count)
eof = 1;
} else if (dp->read_proc) {
n = dp->read_proc(page, &start, *ppos,
count, &eof, dp->data);
} else
break;
if (!start) {
/*
* For proc files that are less than 4k
*/
start = page + *ppos;
n -= *ppos;
if (n <= 0)
break;
if (n > count)
n = count;
}
if (n == 0)
break; /* End of file */
if (n < 0) {
if (retval == 0)
retval = n;
break;
}
/* This is a hack to allow mangling of file pos independent
* of actual bytes read. Simply place the data at page,
* return the bytes, and set `start' to the desired offset
* as an unsigned int. - Paul.Russell@rustcorp.com.au
*/
n -= copy_to_user(buf, start < page ? page : start, n);
if (n == 0) {
if (retval == 0)
retval = -EFAULT;
break;
}
*ppos += start < page ? (long)start : n; /* Move down the file */
nbytes -= n;
buf += n;
retval += n;
}
free_page((unsigned long) page);
return retval;
}
(7)read_proc成员函数实例,读内核符号表
-------------------------read_proc举例
//见proc_misc_init()中的{"ksyms", ksyms_read_proc},
static int ksyms_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len = get_ksyms_list(page, start, off, count);
if (len < count) *eof = 1;
return len;
}
/*
begin始终指向buf处的内容在文件中的偏移,而
if (pos < offset) {
len = 0;
begin = pos;
}
不断调节begin的位置使offset最终落到buf缓冲页(也就是上级函数传递过来的page)中
因此buf + (offset - begin)就得到offset对应的缓存页中的位置。
因为start可能>buf,为预防offset - begin + length 超出缓冲页,所以buf中只用了3/4空间限额,后边作预留。
由(6)知,每次调用read_proc时length最大值设置为PROC_BLOCK_SIZE(即3KB),此时,若当offset - begin>0,那么本段读入数据的末尾是超出3KB的,
但是不会超出4KB(begin的不断调节作用)。
*/
/*
* Called by the /proc file system to return a current list of ksyms.
*/
int
get_ksyms_list(char *buf, char **start, off_t offset, int length)
{
struct module *mod;
char *p = buf;
int len = 0; /* code from net/ipv4/proc.c */
off_t pos = 0;
off_t begin = 0;
for (mod = module_list; mod; mod = mod->next) {
unsigned i;
struct module_symbol *sym;
if (!MOD_CAN_QUERY(mod))
continue;
for (i = mod->nsyms, sym = mod->syms; i > 0; --i, ++sym) {
p = buf + len;
if (*mod->name) {
len += sprintf(p, "%0*lx %s\t[%s]\n",
(int)(2*sizeof(void*)),
sym->value, sym->name,
mod->name);
} else {
len += sprintf(p, "%0*lx %s\n",
(int)(2*sizeof(void*)),
sym->value, sym->name);
}
pos = begin + len;
if (pos < offset) {
len = 0;
begin = pos;
}
pos = begin + len;
if (pos > offset+length)
goto leave_the_loop;
}
}
leave_the_loop:
*start = buf + (offset - begin);
len -= (offset - begin);
if (len > length)
len = length;
return len;
}
written by aweii
一、概述(内核版本2.4.0)
系统启动初始化阶段,调用init_proc_fs进行proc文件系统的注册。内核初始化完成后,可由初始化用户进程(如init进程之类)调用mount -t proc /dev/null /proc 进行挂接。
二、结构
(1)虚拟目录结构
struct proc_dir_entry {
unsigned short low_ino;
unsigned short namelen;
const char *name;
mode_t mode;
nlink_t nlink;
uid_t uid;
gid_t gid;
unsigned long size;
struct inode_operations * proc_iops;
struct file_operations * proc_fops;
get_info_t *get_info;
struct module *owner;
struct proc_dir_entry *next, *parent, *subdir;
void *data;
read_proc_t *read_proc;
write_proc_t *write_proc;
atomic_t count; /* use count */
int deleted; /* delete flag */
kdev_t rdev;
};
inode->generic_ip指向proc_dir_entry,proc_lookup()通过proc_dir_entry的subdir链表搜寻其子节点的proc_dir_entry。
(2)
三、主要代码分析
(1)文件系统初始化
-------------init_proc_fs
static DECLARE_FSTYPE(proc_fs_type, "proc", proc_read_super, FS_SINGLE);
static int __init init_proc_fs(void)
{
int err = register_filesystem(&proc_fs_type); //注册文件系统proc_fs_type,成员函数read_super指向proc_read_super
if (!err) {
proc_mnt = kern_mount(&proc_fs_type);//创建vfs_mount结构,挂接在proc_fs_type下,同时生成的元素包括超级块super_block和根节点root_inode
err = PTR_ERR(proc_mnt);
if (IS_ERR(proc_mnt))
unregister_filesystem(&proc_fs_type);
else
err = 0;
}
return err;
}
(2)/proc/下的主要子节点初始化
--------------proc_misc_init
void __init proc_misc_init(void)
{
struct proc_dir_entry *entry;
static struct {
char *name;
int (*read_proc)(char*,char**,off_t,int,int*,void*);
} *p, simple_ones[] = {
{"loadavg", loadavg_read_proc},
{"uptime", uptime_read_proc},
{"meminfo", meminfo_read_proc},
{"version", version_read_proc},
{"cpuinfo", cpuinfo_read_proc},
#ifdef CONFIG_PROC_HARDWARE
{"hardware", hardware_read_proc},
#endif
#ifdef CONFIG_STRAM_PROC
{"stram", stram_read_proc},
#endif
#ifdef CONFIG_DEBUG_MALLOC
{"malloc", malloc_read_proc},
#endif
#ifdef CONFIG_MODULES
{"modules", modules_read_proc},
{"ksyms", ksyms_read_proc},
#endif
{"stat", kstat_read_proc},
{"devices", devices_read_proc},
{"partitions", partitions_read_proc},
#if !defined(CONFIG_ARCH_S390)
{"interrupts", interrupts_read_proc},
#endif
{"filesystems", filesystems_read_proc},
{"dma", dma_read_proc},
{"ioports", ioports_read_proc},
{"cmdline", cmdline_read_proc},
#ifdef CONFIG_SGI_DS1286
{"rtc", ds1286_read_proc},
#endif
{"locks", locks_read_proc},
{"mounts", mounts_read_proc},
{"swaps", swaps_read_proc},
{"iomem", memory_read_proc},
{"execdomains", execdomains_read_proc},
{NULL,}
};
for (p = simple_ones; p->name; p++)
create_proc_read_entry(p->name, 0, NULL, p->read_proc, NULL);
/* And now for trickier ones */
entry = create_proc_entry("kmsg", S_IRUSR, &proc_root);
if (entry)
entry->proc_fops = &proc_kmsg_operations;
proc_root_kcore = create_proc_entry("kcore", S_IRUSR, NULL);
if (proc_root_kcore) {
proc_root_kcore->proc_fops = &proc_kcore_operations;
proc_root_kcore->size =
(size_t)high_memory - PAGE_OFFSET + PAGE_SIZE;
}
if (prof_shift) {
entry = create_proc_entry("profile", S_IWUSR | S_IRUGO, NULL);
if (entry) {
entry->proc_fops = &proc_profile_operations;
entry->size = (1+prof_len) * sizeof(unsigned int);
}
}
#ifdef __powerpc__
{
extern struct file_operations ppc_htab_operations;
entry = create_proc_entry("ppc_htab", S_IRUGO|S_IWUSR, NULL);
if (entry)
entry->proc_fops = &ppc_htab_operations;
}
#endif
entry = create_proc_read_entry("slabinfo", S_IWUSR | S_IRUGO, NULL,
slabinfo_read_proc, NULL);
if (entry)
entry->write_proc = slabinfo_write_proc;
}
(3)创建只读子节点的函数,能同时设置read_proc
-------------------------create_proc_read_entry
extern inline struct proc_dir_entry *create_proc_read_entry(const char *name,
mode_t mode, struct proc_dir_entry *base,
read_proc_t *read_proc, void * data)
{
struct proc_dir_entry *res=create_proc_entry(name,mode,base); //创建名为name的proc_dir_entry,其父节点为base
if (res) {
res->read_proc=read_proc; //设置read_proc方法
res->data=data;
}
return res;
}
(4)创建子节点的函数
-------------------------create_proc_entry
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
struct proc_dir_entry *parent)
{
struct proc_dir_entry *ent = NULL;
const char *fn = name;
int len;
if (!parent && xlate_proc_name(name, &parent, &fn) != 0)
goto out;
len = strlen(fn);
ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL); //分配proc_dir_entry,其后紧跟name+"\0",所以空间大小为sizeof(struct proc_dir_entry) + strlen(name) + 1
if (!ent)
goto out;
memset(ent, 0, sizeof(struct proc_dir_entry));
memcpy(((char *) ent) + sizeof(*ent), fn, len + 1);//拷贝name到proc_dir_entry后
ent->name = ((char *) ent) + sizeof(*ent); //name指向它
ent->namelen = len;
//目录节点设置方法
if (S_ISDIR(mode)) {
if ((mode & S_IALLUGO) == 0)
mode |= S_IRUGO | S_IXUGO;
ent->proc_fops = &proc_dir_operations;
ent->proc_iops = &proc_dir_inode_operations;
ent->nlink = 2;
} else {
if ((mode & S_IFMT) == 0)
mode |= S_IFREG;
if ((mode & S_IALLUGO) == 0)
mode |= S_IRUGO;
ent->nlink = 1;
}
ent->mode = mode;
proc_register(parent, ent); //级联到父节点
out:
return ent;
}
(5)目录搜索函数
根据级联关系进行循环搜索,和目录中搜索文件一样,都要比对名称
-------------------------proc_lookup
struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry)
{
struct inode *inode;
struct proc_dir_entry * de;
int error;
error = -ENOENT;
inode = NULL;
de = (struct proc_dir_entry *) dir->u.generic_ip;
if (de) {
for (de = de->subdir; de ; de = de->next) {
if (!de || !de->low_ino)
continue;
if (de->namelen != dentry->d_name.len)
continue;
if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {
int ino = de->low_ino;
error = -EINVAL;
inode = proc_get_inode(dir->i_sb, ino, de);
break;
}
}
}
if (inode) {
dentry->d_op = &proc_dentry_operations;
d_add(dentry, inode);
return NULL;
}
return ERR_PTR(error);
}
(6)文件通用读函数,它调用proc_dir_entry的get_info或read_proc成员函数
注意proc_file_read和具体的read_proc的搭配使用方法,ppos和其他普通文件读函数相同,
但要注意ppos、start值的交互变化。
proc_file_read循环调用read_proc,read_proc每次最多只读PROC_BLOCK_SIZE(4K页的3/4,3KB),
保证每次读入量都能容纳进page页面中。根据ppos内容在page中的偏移,调节start指针
-----------------------------proc_file_read
static struct file_operations proc_file_operations = {
llseek: proc_file_lseek,
read: proc_file_read,
write: proc_file_write,
};
#define PROC_BLOCK_SIZE (PAGE_SIZE - 1024)
static ssize_t
proc_file_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
{
struct inode * inode = file->f_dentry->d_inode;
char *page;
ssize_t retval=0;
int eof=0;
ssize_t n, count;
char *start;
struct proc_dir_entry * dp;
dp = (struct proc_dir_entry *) inode->u.generic_ip;
if (!(page = (char*) __get_free_page(GFP_KERNEL)))
return -ENOMEM;
while ((nbytes > 0) && !eof)
{
count = MIN(PROC_BLOCK_SIZE, nbytes);
start = NULL;
if (dp->get_info) {
/*
* Handle backwards compatibility with the old net
* routines.
*/
n = dp->get_info(page, &start, *ppos, count);
if (n < count)
eof = 1;
} else if (dp->read_proc) {
n = dp->read_proc(page, &start, *ppos,
count, &eof, dp->data);
} else
break;
if (!start) {
/*
* For proc files that are less than 4k
*/
start = page + *ppos;
n -= *ppos;
if (n <= 0)
break;
if (n > count)
n = count;
}
if (n == 0)
break; /* End of file */
if (n < 0) {
if (retval == 0)
retval = n;
break;
}
/* This is a hack to allow mangling of file pos independent
* of actual bytes read. Simply place the data at page,
* return the bytes, and set `start' to the desired offset
* as an unsigned int. - Paul.Russell@rustcorp.com.au
*/
n -= copy_to_user(buf, start < page ? page : start, n);
if (n == 0) {
if (retval == 0)
retval = -EFAULT;
break;
}
*ppos += start < page ? (long)start : n; /* Move down the file */
nbytes -= n;
buf += n;
retval += n;
}
free_page((unsigned long) page);
return retval;
}
(7)read_proc成员函数实例,读内核符号表
-------------------------read_proc举例
//见proc_misc_init()中的{"ksyms", ksyms_read_proc},
static int ksyms_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len = get_ksyms_list(page, start, off, count);
if (len < count) *eof = 1;
return len;
}
/*
begin始终指向buf处的内容在文件中的偏移,而
if (pos < offset) {
len = 0;
begin = pos;
}
不断调节begin的位置使offset最终落到buf缓冲页(也就是上级函数传递过来的page)中
因此buf + (offset - begin)就得到offset对应的缓存页中的位置。
因为start可能>buf,为预防offset - begin + length 超出缓冲页,所以buf中只用了3/4空间限额,后边作预留。
由(6)知,每次调用read_proc时length最大值设置为PROC_BLOCK_SIZE(即3KB),此时,若当offset - begin>0,那么本段读入数据的末尾是超出3KB的,
但是不会超出4KB(begin的不断调节作用)。
*/
/*
* Called by the /proc file system to return a current list of ksyms.
*/
int
get_ksyms_list(char *buf, char **start, off_t offset, int length)
{
struct module *mod;
char *p = buf;
int len = 0; /* code from net/ipv4/proc.c */
off_t pos = 0;
off_t begin = 0;
for (mod = module_list; mod; mod = mod->next) {
unsigned i;
struct module_symbol *sym;
if (!MOD_CAN_QUERY(mod))
continue;
for (i = mod->nsyms, sym = mod->syms; i > 0; --i, ++sym) {
p = buf + len;
if (*mod->name) {
len += sprintf(p, "%0*lx %s\t[%s]\n",
(int)(2*sizeof(void*)),
sym->value, sym->name,
mod->name);
} else {
len += sprintf(p, "%0*lx %s\n",
(int)(2*sizeof(void*)),
sym->value, sym->name);
}
pos = begin + len;
if (pos < offset) {
len = 0;
begin = pos;
}
pos = begin + len;
if (pos > offset+length)
goto leave_the_loop;
}
}
leave_the_loop:
*start = buf + (offset - begin);
len -= (offset - begin);
if (len > length)
len = length;
return len;
}
相关文章推荐
- Android研究-Android系统初始化程序init和初始化配置文件init.rc分析
- LARBIN源代码分析[12]GLOBAL文件中的四个INIT初始化模块函数
- linux011文件系统中缓冲区管理函数分析
- Android研究-Android系统初始化程序init和初始化配置文件init.rc分析[zz]
- linux网络协议栈分析——网络文件系统初始化
- Linux内核源码分析--系统时间初始化(kernel_mktime()函数)
- RTEMS 的文件系统初始化等的一点分析笔记
- 《第一篇 从linux 0.11系统初始化main.c的fork()函数调用分析内核源码》
- Android研究-Android系统初始化程序init和初始化配置文件init.rc分析[zz]
- Android研究-Android系统初始化程序init和初始化配置文件init.rc分析[zz]
- C++中函数中参数和返回值都是用户定义类型(这里主要是类的情况时的分析)
- windows XP系统内核文件分析精简系统很有好处
- 共享库的初始化和~初始化函数分析
- 如何调用MFC中的函数打开一个系统资源面板,获取文件
- 硬盘 FAT 文件系统原理的详细分析
- 强制把系统缓存写入文件sync和f s y n c函数
- PHP - Manual手册 - XL. Filesystem 文件系统函数 - fgetcsv从文件指针中读入一行并解析 CSV 字段
- 实例分析-在FLASH上构造JFFS2文件系统
- 最详细的硬盘 FAT 文件系统原理分析
- 嵌入式Linux的文件系统及其存储机制分析