因为使用到了金士顿的32G的TF卡,所以我们要知道它的寿命,可是官网上并没有明确的指出擦写次数的最小值。所以我们只能自己测。
我是设想直接打开文件然后写入数据最后关闭反复一秒一次直到写不进。可有 同事提出来,linux文件系统会每次写的地址不一样,可能测不出来。别人都是把TF卡写满来测试的,可是创建32G的数据来回写很费时间啊,我们还有15天结项能测试出来吗?所以我去论证下我这种简单的方法可行吗?
首先我们得确定我们文件的物理地址是否不变呢?但怎么查看我们的物理地址,这个就难倒我了。只能去源码找找看了。
首先我们要知道大体的linux文件系统的设计思路。
linux系统为了能支持多种多样的文件系统,所以弄了一层虚拟文件系统,它规定一套所有文件系统需要遵守的标准接口。用户只需要操作虚拟文件系统即可操作所有的文件系统。如下:
进程怎么和文件连接以及文件系统连接,在include/linux/sched.h有task_struct结构体
struct task_struct {
......
/* filesystem information */
struct fs_struct *fs;
/* open file information */
struct files_struct *files;
......
}
结构体中有fs_struct结构体和files_struct结构体。分别描述的是文件系统和文件
首先先看看fs_struct结构体
struct fs_struct {
int users;
spinlock_t lock;
seqcount_t seq;
int umask;
int in_exec;
struct path root, pwd;
};
主要关系的是root和pwd,分别代表当前进程的根目录和当前目录。那我们继续看看path结构体,在include/linux/path.h里
struct path {
struct vfsmount *mnt;
struct dentry *dentry;
};
原来文件系统结构体中有代表"目录项"的dentry结构指针,和代表"安装"的vfsmount结构指针。dentry结构体并没有找到,只能百度得到的是里面有inode结点指针。inode是什么?通过百度得到其实inode里面记录着文件在存储介质上的位置与分布等信息(这里让我眼前一亮,是否能在文件中也找到这个inode,让我知道文件在磁盘的具体位置呢?)。
我们再来看看files_struct结构体,也是因为自己没能在源码中找到所以就去百度
struct files_struct
{
atomic_t count; 使用该表的进程数
struct fdtable *fdt;
struct fdtable fdtab;
spinlock_t file_lock ____cacheline_aligned_in_smp;
int next_fd; 数值最小的最近关闭文件的文件描述符,下一个可用的文件描述符
struct embedded_fd_set close_on_exec_init; 执行exec时需要关闭的文件描述符初值集合
struct embedded_fd_set open_fds_init; 文件描述符的屏蔽字初值集合
struct file * fd_array[NR_OPEN_DEFAULT]; 默认打开的fd队列
};
files_struct里面有关于文件结构的数组,那我们接下来看看file结构
struct file {
union {
struct llist_node fu_llist;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
#define f_dentry f_path.dentry
struct inode *f_inode; /* cached value */
const struct file_operations *f_op;
/*
* Protects f_ep_links, f_flags.
* Must not be taken from IRQ context.
*/
spinlock_t f_lock;
atomic_long_t f_count;
unsigned int f_flags;
fmode_t f_mode;
struct mutex f_pos_lock;
loff_t f_pos;
struct fown_struct f_owner;
const struct cred *f_cred;
struct file_ra_state f_ra;
u64 f_version;
#ifdef CONFIG_SECURITY
void *f_security;
#endif
/* needed for tty driver, and maybe others */
void *private_data;
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
struct list_head f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
#ifdef CONFIG_FILE_TABLE_DEBUG
struct hlist_node f_hash;
#endif /* #ifdef CONFIG_FILE_TABLE_DEBUG */
} __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
虽然也有*f_inode但是后面注释告诉我们不是这个。又看到了path,刚刚分析了里面不是也有dentry,dentry里面有inode。再来捋一捋我们的文件系统的逻辑图
这样我们只要获取到inode信息就可以测试文件系统是怎么存放文件的。但是我们怎么获取inode信息呢?发现shell指令
ls -i
能获取到inode number,那inode number怎么获取到实际地址呢?
block_group_number = (inode_number-1) / inodes_per_group
inode_offset = (inode_number-1) % inodes_per_group
inode_location_in_byte = inode_talbe_location+ inode_offset*inode_size = inode_talbe_location+ ((inode_number-1) % inodes_per_group)*inode_size
通过上面这三个公式,其实我们只要确定inode number不变就可以确定块地址没有大变化,则我所想的方法是可以检测的。
那么下面来看看是否会发生变化。
1.测试创建写入文件然后删除文件再创建写入文件
发现块号从337变到338,所以该方式无法进行检测某一块区域的擦写次数。因为它的区域会变。
2.测试创建写入文件然后关闭,之后打开继续写入
哇,可以,没有改变inode number,也就是所在块的区域没有改变。
好,我们就用这一种方式,快速的测试TF卡的寿命。理论上是这样,赶紧和我的另一个同事沟通。
他说不可能,那只是内存中虚拟映射的。然后安装上面的操作执行并在创建结束和开机时都用单片机去读出地址值。惊人的是居然是同一个地址。那就是说我们测TF卡寿命的测试程序只要创建一个文件,然后一直修改即可对同一地址擦写。