提交 46e86676 编写于 作者: 鸿蒙内核源码分析's avatar 鸿蒙内核源码分析

为支持doxygen,改变注释表达方式.

    百万汉字注解 + 百篇博客分析 => 挖透鸿蒙内核源码
    博客输出站点(国内):http://weharmonyos.com
    博客输出站点(国外):https://weharmony.github.io
    注解文件系统:https://gitee.com/weharmony/third_party_NuttX
    注解协议栈:https://gitee.com/weharmony/third_party_lwip
    注解编译子系统:https://gitee.com/weharmony/build_lite
上级 bfb86fda
......@@ -31,12 +31,19 @@
#include "los_typedef.h"
#pragma once
//汇编代码实现 见于..\third_party\FreeBSD\lib\libc\arm\string\hw_user_copy.S
/****************************************
用户空间 <---> 内核空间的拷贝实现函数,由上层封装从哪到哪的拷贝
根据参数的不同来实现相互的拷贝
dst:可以是 用户空间/内核空间地址
src:可以是 用户空间/内核空间地址
****************************************/
/**
* @file arm_user_copy.h
* @brief 汇编代码实现 见于..\third_party\FreeBSD\lib\libc\arm\string\hw_user_copy.S
* 用户空间 <---> 内核空间的拷贝实现函数,由上层封装从哪到哪的拷贝
* 根据参数的不同来实现相互的拷贝
* dst:可以是 用户空间/内核空间地址
* src:可以是 用户空间/内核空间地址
* @param dst
* @param src
* @param len
* @return size_t
*/
size_t _arm_user_copy(void *dst, const void *src, size_t len);
......@@ -400,10 +400,14 @@ BOOL OsArchMmuInit(LosArchMmu *archMmu, VADDR_T *virtTtb)
return TRUE;
}
/******************************************************************************
本函数是内核高频函数,通过MMU查询虚拟地址是否映射过,
带走映射的物理地址和权限
******************************************************************************/
/**
* @brief 本函数是内核高频函数,通过MMU查询虚拟地址是否映射过,带走映射的物理地址和权限
* @param archMmu
* @param vaddr
* @param paddr
* @param flags
* @return STATUS_T
*/
STATUS_T LOS_ArchMmuQuery(const LosArchMmu *archMmu, VADDR_T vaddr, PADDR_T *paddr, UINT32 *flags)
{//archMmu->virtTtb:转换表基地址
PTE_T l1Entry = OsGetPte1(archMmu->virtTtb, vaddr);//获取PTE vaddr右移20位 得到L1描述子地址
......
......@@ -37,11 +37,19 @@
#include "los_vm_map.h"
/*******************************************************
从用户空间拷贝到内核空间
请思考个问题,系统调用时为什么一定要copy_from_user ? 为什么不直接用memcpy?
https://mp.weixin.qq.com/s/H3nXlOpP_XyF7M-1B4qreQ
*******************************************************/
/**
* @brief
* @verbatim
从用户空间拷贝到内核空间
请思考个问题,系统调用时为什么一定要copy_from_user ? 为什么不直接用memcpy?
https://mp.weixin.qq.com/s/H3nXlOpP_XyF7M-1B4qreQ
* @endverbatim
* @param dst
* @param src
* @param len
* @return size_t
*/
size_t arch_copy_from_user(void *dst, const void *src, size_t len)
{
return LOS_ArchCopyFromUser(dst, src, len);
......@@ -60,11 +68,14 @@ size_t arch_copy_to_user(void *dst, const void *src, size_t len)
{
return LOS_ArchCopyToUser(dst, src, len);//
}
/********************************************
从内核空间拷贝到用户空间
dst:必须在用户空间
src:必须在内核空间
********************************************/
/**
* @brief 从内核空间拷贝到用户空间
* @param dst 必须在用户空间
* @param src 必须在内核空间
* @param len
* @return size_t
*/
size_t LOS_ArchCopyToUser(void *dst, const void *src, size_t len)
{//先判断地址是不是在用户空间
if (!LOS_IsUserAddressRange((VADDR_T)(UINTPTR)dst, len)) {//[dest,dest+count] 不在用户空间
......
......@@ -35,7 +35,11 @@
#include "time_posix.h"
#include "los_atomic.h"
#include "los_event_pri.h"
/************************************************************
/**
* @file
* @brief
* @verbatim
条件变量属性
使用条件变量可以以原子方式阻塞线程,直到某个特定条件为真为止。条件变量始终与互斥锁一起使用。
......@@ -52,7 +56,8 @@
https://docs.oracle.com/cd/E19253-01/819-7051/sync-13528/index.html
https://docs.oracle.com/cd/E19253-01/819-7051/6n919hpai/index.html#sync-59145
************************************************************/
* @endverbatim
*/
#define BROADCAST_EVENT 1
#define COND_COUNTER_STEP 0x0004U
......
......@@ -44,37 +44,53 @@ https://www.cnblogs.com/sparkdev/p/8341134.html
#if !LWIP_COMPAT_SOCKETS
#define CHECK_NULL_PTR(ptr) do { if (ptr == NULL) { set_errno(EFAULT); return -1; } } while (0)
/***************************************************************
TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。
TCP客户端依次调用socket()、connect()之后就向TCP服务器发送了一个连接请求。
TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。
之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。
accept函数
第一个参数为服务器的socket描述字,
第二个参数为指向struct sockaddr *的指针,用于返回客户端的协议地址,
第三个参数为客户端协议地址的长度。
如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。
注意:accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,称为监听socket描述字;
而accept函数返回的是已连接的socket描述字。一个服务器通常通常仅仅只创建一个监听socket描述字,
它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字,
当服务器完成了对某个客户的服务,相应的已连接socket描述字就被关闭。
***************************************************************/
/**
* @brief
* @verbatim
TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。
TCP客户端依次调用socket()、connect()之后就向TCP服务器发送了一个连接请求。
TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。
之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。
accept函数
第一个参数为服务器的socket描述字,
第二个参数为指向struct sockaddr *的指针,用于返回客户端的协议地址,
第三个参数为客户端协议地址的长度。
如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。
注意:accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,称为监听socket描述字;
而accept函数返回的是已连接的socket描述字。一个服务器通常通常仅仅只创建一个监听socket描述字,
它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字,
当服务器完成了对某个客户的服务,相应的已连接socket描述字就被关闭。
* @endverbatim
* @param s
* @param addr
* @param addrlen
* @return int
*/
int accept(int s, struct sockaddr *addr, socklen_t *addrlen)
{
return lwip_accept(s, addr, addrlen);
}
/***************************************************************
bind()函数把一个地址族中的特定地址赋给socket。
例如对应AF_INET、AF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket。
函数的三个参数分别为:
•sockfd:即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。
bind()函数就是将给这个描述字绑定一个名字。
•addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。
•addrlen:对应的是地址的长度。
通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,
客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。
这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。
***************************************************************/
/**
* @brief
* @verbatim
bind()函数把一个地址族中的特定地址赋给socket。
例如对应AF_INET、AF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket。
函数的三个参数分别为:
•sockfd:即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。
bind()函数就是将给这个描述字绑定一个名字。
•addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。
•addrlen:对应的是地址的长度。
通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,
客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。
这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。
* @endverbatim
* @param s
* @param name
* @param namelen
* @return int
*/
int bind(int s, const struct sockaddr *name, socklen_t namelen)
{
CHECK_NULL_PTR(name);
......@@ -84,7 +100,10 @@ int bind(int s, const struct sockaddr *name, socklen_t namelen)
}
return lwip_bind(s, name, namelen);
}
/***************************************************************
/**
* @brief
* @verbatim
该函数的行为依赖于how的值
SHUT_RD:值为0,关闭连接的读这一半。
SHUT_WR:值为1,关闭连接的写这一半。
......@@ -97,7 +116,11 @@ closesocket 函数会关闭套接字ID,如果有其他的进程共享着这个
而shutdown会切断进程共享的套接字的所有连接,不管这个套接字的引用计数是否为零,
那些试图读得进程将会接收到EOF标识,那些试图写的进程将会检测到SIGPIPE信号,
同时可利用shutdown的第二个参数选择断连的方式。
***************************************************************/
* @endverbatim
* @param s
* @param how
* @return int
*/
int shutdown(int s, int how)
{
return lwip_shutdown(s, how);
......@@ -126,25 +149,39 @@ int setsockopt(int s, int level, int optname, const void *optval, socklen_t optl
{
return lwip_setsockopt(s, level, optname, optval, optlen);
}
/***************************************************************
/**
* @brief
* @verbatim
closesocket 一个套接字的默认行为是把套接字标记为已关闭,然后立即返回到调用进程,
该套接字描述符不能再由调用进程使用,也就是说它不能再作为read或write的第一个参数,
然而TCP将尝试发送已排队等待发送到对端,发送完毕后发生的是正常的TCP连接终止序列。
在多进程并发服务器中,父子进程共享着套接字,套接字描述符引用计数记录着共享着的进程个数,
当父进程或某一子进程close掉套接字时,描述符引用计数会相应的减一,
当引用计数仍大于零时,这个close调用就不会引发TCP的四路握手断连过程。
***************************************************************/
* @endverbatim
* @param s
* @return int
*/
int closesocket(int s)
{
return lwip_close(s);
}
/***************************************************************
/**
* @brief
* @verbatim
connect函数由客户端使用,发出连接请求,服务器端就会接收到这个请求
第一个参数即为客户端的socket描述字,
第二参数为服务器的socket地址,
第三个参数为socket地址的长度。
客户端通过调用connect函数来建立与TCP服务器的连接。
***************************************************************/
* @endverbatim
* @param s
* @param name
* @param namelen
* @return int
*/
int connect(int s, const struct sockaddr *name, socklen_t namelen)
{
CHECK_NULL_PTR(name);
......@@ -154,19 +191,29 @@ int connect(int s, const struct sockaddr *name, socklen_t namelen)
}
return lwip_connect(s, name, namelen);
}
/***************************************************************
/**
* @brief
* @verbatim
如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket,
如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。
listen函数的
第一个参数即为要监听的socket描述字,
第二个参数为相应socket可以排队的最大连接个数。
socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。
***************************************************************/
* @endverbatim
* @param s
* @param backlog
* @return int
*/
int listen(int s, int backlog)
{
return lwip_listen(s, backlog);
}
/***************************************************************
/**
* @brief
* @verbatim
相当于文件操作的 read 功能,区别是第四个参数
MSG_DONTROUTE:不查找表,是send函数使用的标志,这个标志告诉IP,目的主机在本地网络上,
......@@ -177,7 +224,13 @@ MSG_PEEK:查看数据,并不从系统缓冲区移走数据。是recv函数
依然是一样的内容,一般在有个进程读写数据的时候使用这个标志。
MSG_WAITALL:等待所有数据,是recv函数的使用标志,表示等到所有的信息到达时才返回,
使用这个标志的时候,recv返回一直阻塞,直到指定的条件满足时,或者是发生了错误。
***************************************************************/
* @endverbatim
* @param s
* @param mem
* @param len
* @param flags
* @return ssize_t
*/
ssize_t recv(int s, void *mem, size_t len, int flags)
{
CHECK_NULL_PTR(mem);
......@@ -199,9 +252,8 @@ ssize_t recvmsg(int s, struct msghdr *message, int flags)
}
return lwip_recvmsg(s, message, flags);
}
/***************************************************************
相当于文件操作的 write 功能,区别是第四个参数 同 recv
***************************************************************/
/// 相当于文件操作的 write 功能,区别是第四个参数 同 recv
ssize_t send(int s, const void *dataptr, size_t size, int flags)
{
CHECK_NULL_PTR(dataptr);
......@@ -223,7 +275,10 @@ ssize_t sendto(int s, const void *dataptr, size_t size, int flags,
}
return lwip_sendto(s, dataptr, size, flags, to, tolen);
}
/***************************************************************
/**
* @brief
* @verbatim
用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。
这个socket描述符跟文件描述符一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。
正如可以给fopen的传入不同参数值,以打开不同的文件。创建socket的时候,也可以指定不同的参数
......@@ -250,16 +305,21 @@ ssize_t sendto(int s, const void *dataptr, size_t size, int flags,
它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。
注意:并不是上面的type和protocol可以随意组合的,如SOCK_STREAM不可以跟IPPROTO_UDP组合。
当protocol为0时,会自动选择type类型对应的默认协议。
***************************************************************/
* @endverbatim
* @param domain
* @param type
* @param protocol
* @return int
*/
int socket(int domain, int type, int protocol)
{
return lwip_socket(domain, type, protocol);
}
/***************************************************************
/*!
inet_ntop 函数转换网络二进制结构到ASCII类型的地址,参数的作用和上面相同,
只是多了一个参数socklen_t cnt,他是所指向缓存区dst的大小,避免溢出,
如果缓存区太小无法存储地址的值,则返回一个空指针,并将errno置为ENOSPC
***************************************************************/
*/
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size)
{
return lwip_inet_ntop(af, src, dst, size);
......
......@@ -47,7 +47,9 @@ extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
/***************************************************************
/**
* @brief
* @verbatim
* flash按照内部存储结构不同,分为两种:nor flash和nand flash
* Nor FLASH使用方便,易于连接,可以在芯片上直接运行代码,稳定性出色,传输速率高,
在小容量时有很高的性价比,这使其很适合应于嵌入式系统中作为 FLASH ROM。
......@@ -78,16 +80,19 @@ Nand Flash:芯片操作是以“块”为基本单位.NAND闪存的块比较
页大小一般是512字节.要修改NandFlash芯片中一个字节,必须重写整个数据块,读和写都是按照扇区进行的。
参考:https://blog.csdn.net/zhejfl/article/details/78544796
***************************************************************/
* @endverbatim
*/
#define SPIBLK_NAME "/dev/spinorblk" ///< nor spi flash | 块设备
#define SPICHR_NAME "/dev/spinorchr" ///< nor spi flash | 字符设备
#define SPIBLK_NAME "/dev/spinorblk" //nor spi flash 块设备
#define SPICHR_NAME "/dev/spinorchr" //nor spi flash 字符设备
#define NANDBLK_NAME "/dev/nandblk" ///< nand flash | 块设备
#define NANDCHR_NAME "/dev/nandchr" ///< nand flash | 字符设备
#define NANDBLK_NAME "/dev/nandblk" //nand flash 块设备
#define NANDCHR_NAME "/dev/nandchr" //nand flash 字符设备
/***************************************************************
/**
* @brief 通过mknod在/dev子目录下建立MTD块设备节点(主设备号为31)和MTD字符设备节点(主设备号为90)
* @verbatim
存储器技术设备(英语:Memory Technology Device,缩写为 MTD),是Linux系统中设备文件系统的一个类别,
主要用于闪存的应用,是一种闪存转换层(Flash Translation Layer,FTL)。创造MTD子系统的主要目的是提供
一个介于闪存硬件驱动程序与高端应用程序之间的抽象层。
......@@ -121,33 +126,36 @@ MTD设备既非块设备也不是字符设备,但可以同时提供字符设
MTD设备通常可分为四层
这四层从上到下依次是:设备节点、MTD设备层、MTD原始设备层和硬件驱动层。
参考: https://blog.csdn.net/lwj103862095/article/details/21545791
***************************************************************/
typedef struct mtd_node {//通过mknod在/dev子目录下建立MTD块设备节点(主设备号为31)和MTD字符设备节点(主设备号为90)
UINT32 start_block; //开始块索引
UINT32 end_block; //结束块索引
UINT32 patitionnum; //分区编号
CHAR *blockdriver_name; //块设备驱动名称 例如: /dev/spinorblk0p0
CHAR *chardriver_name; //字符设备驱动名称 例如: /dev/nandchr0p2
CHAR *mountpoint_name; //挂载点名称 例如: /
* @endverbatim
*/
typedef struct mtd_node {
UINT32 start_block; ///< 开始块索引
UINT32 end_block; ///< 结束块索引
UINT32 patitionnum; ///< 分区编号
CHAR *blockdriver_name; ///< 块设备驱动名称 例如: /dev/spinorblk0p0
CHAR *chardriver_name; ///< 字符设备驱动名称 例如: /dev/nandchr0p2
CHAR *mountpoint_name; ///< 挂载点名称 例如: /
VOID *mtd_info; /* Driver used by a partition *///分区使用的驱动程序
LOS_DL_LIST node_info;//双循环节点,挂在首个分区节点上
LosMux lock; //每个分区都有自己的互斥锁
UINT32 user_num; //使用数量
LOS_DL_LIST node_info;///< 双循环节点,挂在首个分区节点上
LosMux lock; ///< 每个分区都有自己的互斥锁
UINT32 user_num; ///< 使用数量
} mtd_partition;
typedef struct par_param {//分区参数描述符,一个分区既可支持按块访问也可以支持按字符访问,只要有驱动程序就阔以
mtd_partition *partition_head; //首个分区,其他分区都挂在.node_info节点上
struct MtdDev *flash_mtd; //flash设备描述符,属于硬件驱动层
const struct block_operations *flash_ops; //块方式的操作数据
const struct file_operations_vfs *char_ops; //字符方式的操作数据
CHAR *blockname; //块设备名称
CHAR *charname; //字符设备名称
UINT32 block_size; //块单位(4K),对文件系统而言是按块读取数据,方便和内存页置换
/**
* @brief 分区参数描述符,一个分区既可支持按块访问也可以支持按字符访问,只要有驱动程序就阔以
*/
typedef struct par_param {
mtd_partition *partition_head; ///< 首个分区,其他分区都挂在.node_info节点上
struct MtdDev *flash_mtd; ///< flash设备描述符,属于硬件驱动层
const struct block_operations *flash_ops; ///< 块方式的操作数据
const struct file_operations_vfs *char_ops; ///< 字符方式的操作数据
CHAR *blockname; ///< 块设备名称
CHAR *charname; ///< 字符设备名称
UINT32 block_size; ///< 块单位(4K),对文件系统而言是按块读取数据,方便和内存页置换
} partition_param;
#define CONFIG_MTD_PATTITION_NUM 20 //分区数量的上限
#define CONFIG_MTD_PATTITION_NUM 20 ///< 分区数量的上限
#define ALIGN_ASSIGN(len, startAddr, startBlk, endBlk, blkSize) do { \
(len) = (((len) + ((blkSize) - 1)) & ~((blkSize) - 1)); \
......
......@@ -58,7 +58,9 @@
#include <sys/types.h>
#include <fcntl.h>
/***************************************************
/**
* @brief
* @verbatim
FAT文件系统是File Allocation Table(文件配置表)的简称,主要包括DBR区、FAT区、DATA区三个区域。
其中,FAT区各个表项记录存储设备中对应簇的信息,包括簇是否被使用、文件下一个簇的编号、是否文件结尾等。
FAT文件系统有FAT12、FAT16、FAT32等多种格式,其中,12、16、32表示对应格式中FAT表项的字节数。
......@@ -73,9 +75,8 @@ OpenHarmony内核支持FAT12、FAT16与FAT32三种格式的FAT文件系统,
FAT文件系统的使用需要底层MMC相关驱动的支持。在一个带MMC存储设备的板子上运行FATFS,需要:
1、适配板端EMMC驱动,实现disk_status、disk_initialize、disk_read、disk_write、disk_ioctl接口;
2、新增fs_config.h文件,配置FS_MAX_SS(存储设备最大sector大小)、FF_VOLUME_STRS(分区名)等信息,
***************************************************/
* @endverbatim
*/
struct VnodeOps fatfs_vops; /* forward define */
struct file_operations_vfs fatfs_fops;
......@@ -84,13 +85,13 @@ struct file_operations_vfs fatfs_fops;
#define BITMASK5 0x1F
#define BITMASK6 0x3F
#define BITMASK7 0x7F
#define FTIME_MIN_OFFSET 6 /* minute offset in word */
#define FTIME_HR_OFFSET 11 /* hour offset in word */
#define FTIME_MTH_OFFSET 5 /* month offset in word */
#define FTIME_YEAR_OFFSET 9 /* year offset in word */
#define FTIME_DATE_OFFSET 16 /* date offset in dword */
#define FTIME_MIN_OFFSET 6 /*! minute offset in word */
#define FTIME_HR_OFFSET 11 /*! hour offset in word */
#define FTIME_MTH_OFFSET 5 /*! month offset in word */
#define FTIME_YEAR_OFFSET 9 /*! year offset in word */
#define FTIME_DATE_OFFSET 16 /*! date offset in dword */
#define SEC_MULTIPLIER 2
#define YEAR_OFFSET 80 /* Year start from 1980 in FATFS, while start from 1900 in struct tm */
#define YEAR_OFFSET 80 /*! Year start from 1980 in FATFS, while start from 1900 in struct tm */
// 结果转化 fat 转 vfs
int fatfs_2_vfs(int result)
{
......
......@@ -39,7 +39,10 @@
#define MS_NOSYNC 2
struct MountOps;
/******************************************************************
/**
* @brief
* @verbatim
将一个设备(通常是存储设备)挂接到一个已存在的目录上,操作系统将所有的设备都看作文件,
它将整个计算机的资源都整合成一个大的文件目录。我们要访问存储设备中的文件,必须将文件所在的分区挂载到一个已存在的目录上,
然后通过访问这个目录来访问存储设备。挂载就是把设备放在一个目录下,让系统知道怎么管理这个设备里的文件,
......@@ -55,31 +58,39 @@ linux下面所有的文件、目录、设备都有一个路径,这个路径永
如果没有其他挂载点那么就是rootfs上的目录或者文件了。
https://www.zhihu.com/question/266907637/answer/315386532
*******************************************************************/
//举例: mount /dev/mmcblk0p0 /bin1/vs/sd vfat 将/dev/mmcblk0p0 挂载到/bin1/vs/sd目录
//注意: 同时可以 mount /dev/mmcblk0p0 /home/vs vfat 也就是说一个文件系统可以有多个挂载点
* @endverbatim
*/
/**
* @brief 举例: mount /dev/mmcblk0p0 /bin1/vs/sd vfat 将/dev/mmcblk0p0 挂载到/bin1/vs/sd目录
* @attention 注意: 同时可以 mount /dev/mmcblk0p0 /home/vs vfat 也就是说一个文件系统可以有多个挂载点
*/
struct Mount {
LIST_ENTRY mountList; /* mount list */ //通过本节点将Mount挂到全局Mount链表上
const struct MountOps *ops; /* operations of mount */ //挂载操作函数
struct Vnode *vnodeBeCovered; /* vnode we mounted on */ //要被挂载的节点 即 /bin1/vs/sd 对应的 vnode节点
struct Vnode *vnodeCovered; /* syncer vnode */ //要挂载的节点 即/dev/mmcblk0p0 对应的 vnode节点
struct Vnode *vnodeDev; /* dev vnode */
LIST_HEAD vnodeList; /* list of vnodes */ //链表表头
int vnodeSize; /* size of vnode list */ //节点数量
LIST_HEAD activeVnodeList; /* list of active vnodes */ //激活的节点链表
int activeVnodeSize; /* szie of active vnodes list *///激活的节点数量
void *data; /* private data */ //私有数据,可使用这个成员作为一个指向它们自己内部数据的指针
uint32_t hashseed; /* Random seed for vfs hash */ //vfs 哈希随机种子
unsigned long mountFlags; /* Flags for mount */ //挂载标签
char pathName[PATH_MAX]; /* path name of mount point */ //挂载点路径名称 /bin1/vs/sd
char devName[PATH_MAX]; /* path name of dev point */ //设备名称 /dev/mmcblk0p0
LIST_ENTRY mountList; /*! mount list | 通过本节点将Mount挂到全局Mount链表上*/
const struct MountOps *ops; /*! operations of mount | 挂载操作函数*/
struct Vnode *vnodeBeCovered; /*! vnode we mounted on | 要被挂载的节点 即 /bin1/vs/sd 对应的 vnode节点*/
struct Vnode *vnodeCovered; /*! syncer vnode | 要挂载的节点 即/dev/mmcblk0p0 对应的 vnode节点*/
struct Vnode *vnodeDev; /*! dev vnode */
LIST_HEAD vnodeList; /*! list of vnodes | 链表表头*/
int vnodeSize; /*! size of vnode list | 节点数量*/
LIST_HEAD activeVnodeList; /*! list of active vnodes | 激活的节点链表*/
int activeVnodeSize; /*! szie of active vnodes list | 激活的节点数量*/
void *data; /*! private data | 私有数据,可使用这个成员作为一个指向它们自己内部数据的指针*/
uint32_t hashseed; /*! Random seed for vfs hash | vfs 哈希随机种子*/
unsigned long mountFlags; /*! Flags for mount | 挂载标签*/
char pathName[PATH_MAX]; /*! path name of mount point | 挂载点路径名称 /bin1/vs/sd*/
char devName[PATH_MAX]; /*! path name of dev point | 设备名称 /dev/mmcblk0p0*/
};
//挂载操作
/**
* @brief 挂载操作
*/
struct MountOps {
int (*Mount)(struct Mount *mount, struct Vnode *vnode, const void *data);//挂载
int (*Unmount)(struct Mount *mount, struct Vnode **blkdriver);//卸载
int (*Statfs)(struct Mount *mount, struct statfs *sbp);//统计文件系统的信息,如该文件系统类型、总大小、可用大小等信息
int (*Sync)(struct Mount *mount);
int (*Mount)(struct Mount *mount, struct Vnode *vnode, const void *data); ///< 挂载
int (*Unmount)(struct Mount *mount, struct Vnode **blkdriver); ///< 卸载
int (*Statfs)(struct Mount *mount, struct statfs *sbp); ///< 统计文件系统的信息,如该文件系统类型、总大小、可用大小等信息
int (*Sync)(struct Mount *mount); ///< 同步挂载
};
typedef int (*foreach_mountpoint_t)(const char *devpoint,
......
......@@ -41,7 +41,9 @@
#include "los_tables.h"
#include "internal.h"
/******************************************************************
/**
* @brief
* @verbatim
鸿蒙的/proc目录是一种文件系统,即proc文件系统。与其它常见的文件系统不同的是,/proc是一种伪文件系统(也即虚拟文件系统),
存储的是当前内核运行状态的一系列特殊文件,用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,
甚至可以通过更改其中某些文件来改变内核的运行状态。
......@@ -54,17 +56,18 @@
如/proc/mounts 目录中存储的就是当前系统上所有装载点的相关信息,
大多数虚拟文件可以使用文件查看命令如cat、more或者less进行查看,有些文件信息表述的内容可以一目了然,
*******************************************************************/
#ifdef LOSCFG_FS_PROC //使能 /proc 功能
static struct VnodeOps g_procfsVops; // proc 文件系统
* @endverbatim
*/
#ifdef LOSCFG_FS_PROC
static struct VnodeOps g_procfsVops; /// proc 文件系统
static struct file_operations_vfs g_procfsFops;
//通过节点获取私有内存对象,注意要充分理解 node->data 的作用,那是个可以通天的神奇变量.
/// 通过节点获取私有内存对象,注意要充分理解 node->data 的作用,那是个可以通天的神奇变量.
static struct ProcDirEntry *VnodeToEntry(struct Vnode *node)
{
return (struct ProcDirEntry *)(node->data);
}
///创建节点,通过实体对象转成vnode节点,如此达到统一管理的目的.
/// 创建节点,通过实体对象转成vnode节点,如此达到统一管理的目的.
static struct Vnode *EntryToVnode(struct ProcDirEntry *entry)
{
struct Vnode *node = NULL;
......
......@@ -31,7 +31,10 @@
#ifndef _LOS_ROOTFS_H
#define _LOS_ROOTFS_H
/**********************************************
/**
* @brief
* @verbatim
rootfs之所以存在,是因为需要在VFS机制下给系统提供最原始的挂载点
https://blog.csdn.net/tankai19880619/article/details/12093239
......@@ -51,7 +54,9 @@ rootfs的特点:
3.该文件系统仅仅存在于内存中。
VFS是一种机制、是每一种文件系统都必须按照这个机制去实现的一种规范;
而rootfs仅仅是符合VFS规范的而且又具有如上3个特点的一个文件系统。
**********************************************/
* @endverbatim
*/
#include "los_typedef.h"
#define ROOT_DIR_NAME "/"
......@@ -66,15 +71,15 @@ VFS是一种机制、是每一种文件系统都必须按照这个机制去实
#define DEFAULT_MOUNT_DATA NULL
#ifdef LOSCFG_STORAGE_SPINOR //外部开关定 使用哪种flash
#define FLASH_TYPE "spinor" //flash类型
#define ROOT_DEV_NAME "/dev/spinorblk0" //根设备名称,将挂载到 `/` 目录下
#define FLASH_TYPE "spinor" ///< flash类型
#define ROOT_DEV_NAME "/dev/spinorblk0" ///< 根设备名称,将挂载到 `/` 目录下
#define USER_DEV_NAME "/dev/spinorblk2"
#define ROOTFS_ADDR 0x600000
#define ROOTFS_SIZE 0x800000
#define USERFS_SIZE 0x80000
#elif defined(LOSCFG_STORAGE_SPINAND)
#define FLASH_TYPE "nand"
#define ROOT_DEV_NAME "/dev/nandblk0" //设备名称
#define ROOT_DEV_NAME "/dev/nandblk0" ///< 设备名称
#define USER_DEV_NAME "/dev/nandblk2"
#define ROOTFS_ADDR 0x600000
#define ROOTFS_SIZE 0x800000
......
......@@ -75,24 +75,26 @@ typedef LOS_DL_LIST LIST_ENTRY;
#define CHG_ATIME 16
#define CHG_MTIME 32
#define CHG_CTIME 64
struct IATTR { //此结构用于记录 vnode 的属性
/**
* @brief 此结构用于记录 vnode 的属性
*/
struct IATTR {
/* This structure is used for record vnode attr. */
unsigned int attr_chg_valid;//节点改变有效性 (CHG_MODE | CHG_UID | ... )
unsigned int attr_chg_flags;//额外的系统与用户标志(flag),用来保护该文件
unsigned attr_chg_mode; //确定了文件的类型,以及它的所有者、它的group、其它用户访问此文件的权限 (S_IWUSR | ...)
unsigned attr_chg_uid; //用户ID
unsigned attr_chg_gid; //组ID
unsigned attr_chg_size; //节点大小
unsigned attr_chg_atime; //节点最近访问时间
unsigned attr_chg_mtime; //节点对应的文件内容被修改时间
unsigned attr_chg_ctime; //节点自身被修改时间
unsigned int attr_chg_valid; ///< 节点改变有效性 (CHG_MODE | CHG_UID | ... )
unsigned int attr_chg_flags; ///< 额外的系统与用户标志(flag),用来保护该文件
unsigned attr_chg_mode; ///< 确定了文件的类型,以及它的所有者、它的group、其它用户访问此文件的权限 (S_IWUSR | ...)
unsigned attr_chg_uid; ///< 用户ID
unsigned attr_chg_gid; ///< 组ID
unsigned attr_chg_size; ///< 节点大小
unsigned attr_chg_atime; ///< 节点最近访问时间
unsigned attr_chg_mtime; ///< 节点对应的文件内容被修改时间
unsigned attr_chg_ctime; ///<节点自身被修改时间
};
/******************************************************************
/**
* @brief
* @verbatim
Linux系统使用struct inode作为数据结构名称。BSD派生的系统,使用vnode名称,其中v表示“virtual file system”
Linux 链接分两种,一种被称为硬链接(Hard Link),另一种被称为符号链接(Symbolic Link)。默认情况下,ln 命令产生硬链接。
硬连接
......@@ -123,28 +125,33 @@ inode的特殊作用
第3点使得软件更新变得简单,可以在不关闭软件的情况下进行更新,不需要重启。因为系统通过inode号码,识别运行中的文件,不通过文件名。
更新的时候,新版文件以同样的文件名,生成一个新的inode,不会影响到运行中的文件。等到下一次运行这个软件的时候,文件名就自动指向新版文件,
旧版文件的inode则被回收。
*******************************************************************/
/*
* Vnode types. VNODE_TYPE_UNKNOWN means no type.
@endverbatim
*/
/*!
* Vnode types. VNODE_TYPE_UNKNOWN means no type. | 节点类型
*/
enum VnodeType {//节点类型
VNODE_TYPE_UNKNOWN, /* unknown type */ //未知类型
VNODE_TYPE_REG, /* regular file */ //正则文件(普通文件)
VNODE_TYPE_DIR, /* directory */ //目录
VNODE_TYPE_BLK, /* block device */ //块设备
VNODE_TYPE_CHR, /* char device */ //字符设备
VNODE_TYPE_BCHR, /* block char mix device *///块和字符设备混合
VNODE_TYPE_FIFO, /* pipe */ //管道文件
VNODE_TYPE_LNK, /* link */ //链接,这里的链接指的是上层硬链接概念
enum VnodeType {
VNODE_TYPE_UNKNOWN, /*! unknown type | 未知类型*/
VNODE_TYPE_REG, /*! regular file | 正则文件(普通文件)*/
VNODE_TYPE_DIR, /*! directory | 目录*/
VNODE_TYPE_BLK, /*! block device | 块设备*/
VNODE_TYPE_CHR, /*! char device | 字符设备*/
VNODE_TYPE_BCHR, /*! block char mix device | 块和字符设备混合*/
VNODE_TYPE_FIFO, /*! pipe | 管道文件*/
VNODE_TYPE_LNK, /*! link | 链接,这里的链接指的是上层硬链接概念*/
};
struct fs_dirent_s;
struct VnodeOps;
struct IATTR;
/*
* linux下有多种权限控制的机制,常见的有:DAC(Discretionary Access Control)自主式权限控制和MAC(Mandatory Access Control)强制访问控制。
* linux 下使用 inode 中文意思是索引节点(index node),从概念层面鸿蒙 Vnode是对标 inode
* 这里顺便说一下目录文件的"链接数"。创建目录时,默认会生成两个目录项:"."和".."。前者的inode号码就是当前目录的inode号码,
/*!
* @brief vnode并不包含文件名,因为 vnode和文件名是 1:N 的关系
@verbatim
linux下有多种权限控制的机制,常见的有:DAC(Discretionary Access Control)自主式权限控制和MAC(Mandatory Access Control)强制访问控制。
linux 下使用 inode 中文意思是索引节点(index node),从概念层面鸿蒙 Vnode是对标 inode
这里顺便说一下目录文件的"链接数"。创建目录时,默认会生成两个目录项:"."和".."。前者的inode号码就是当前目录的inode号码,
等同于当前目录的"硬链接";后者的inode号码就是当前目录的父目录的inode号码,等同于父目录的"硬链接"。
所以,任何一个目录的"硬链接"总数,总是等于2加上它的子目录总数(含隐藏目录)
......@@ -152,67 +159,68 @@ struct IATTR;
因此 vop ,fop 都是接口, data 因设备不同而不同.
如果底层是磁盘存储,Inode结构会保存到磁盘。当需要时从磁盘读取到内存中进行缓存。
@endverbatim
*/
struct Vnode {//vnode并不包含文件名,因为 vnode和文件名是 1:N 的关系
enum VnodeType type; /* vnode type */ //节点类型 (文件|目录|链接...)
int useCount; /* ref count of users *///节点引用(链接)数,即有多少文件名指向这个vnode,即上层理解的硬链接数
uint32_t hash; /* vnode hash */ //节点哈希值
uint uid; /* uid for dac */ //文件拥有者的User ID
uint gid; /* gid for dac */ //文件的Group ID
mode_t mode; /* mode for dac */ //chmod 文件的读、写、执行权限
LIST_HEAD parentPathCaches; /* pathCaches point to parents */ //指向父级路径缓存,上面的都是当了爸爸节点
LIST_HEAD childPathCaches; /* pathCaches point to children */ //指向子级路径缓存,上面都是当了别人儿子的节点
struct Vnode *parent; /* parent vnode */ //父节点
struct VnodeOps *vop; /* vnode operations */ //相当于指定操作Vnode方式 (接口实现|驱动程序)
struct file_operations_vfs *fop; /* file operations */ //相当于指定文件系统
void *data; /* private data */ //文件数据block的位置,指向每种具体设备私有的成员,例如 ( drv_data | nfsnode | ....)
uint32_t flag; /* vnode flag */ //节点标签
LIST_ENTRY hashEntry; /* list entry for bucket in hash table */ //通过它挂入哈希表 g_vnodeHashEntrys[i], i:[0,g_vnodeHashMask]
LIST_ENTRY actFreeEntry; /* vnode active/free list entry */ //通过本节点挂到空闲链表和使用链表上
struct Mount *originMount; /* fs info about this vnode */ //自己所在的文件系统挂载信息
struct Mount *newMount; /* fs info about who mount on this vnode */ //其他挂载在这个节点上文件系统信息
struct Vnode {
enum VnodeType type; /* vnode type | 节点类型 (文件|目录|链接...)*/
int useCount; /* ref count of users | 节点引用(链接)数,即有多少文件名指向这个vnode,即上层理解的硬链接数*/
uint32_t hash; /* vnode hash | 节点哈希值*/
uint uid; /* uid for dac | 文件拥有者的User ID*/
uint gid; /* gid for dac | 文件的Group ID*/
mode_t mode; /* mode for dac | chmod 文件的读、写、执行权限*/
LIST_HEAD parentPathCaches; /* pathCaches point to parents | 指向父级路径缓存,上面的都是当了爸爸节点*/
LIST_HEAD childPathCaches; /* pathCaches point to children | 指向子级路径缓存,上面都是当了别人儿子的节点*/
struct Vnode *parent; /* parent vnode | 父节点*/
struct VnodeOps *vop; /* vnode operations | 相当于指定操作Vnode方式 (接口实现|驱动程序)*/
struct file_operations_vfs *fop; /* file operations | 相当于指定文件系统*/
void *data; /* private data | 文件数据block的位置,指向每种具体设备私有的成员,例如 ( drv_data | nfsnode | ....)*/
uint32_t flag; /* vnode flag | 节点标签*/
LIST_ENTRY hashEntry; /* list entry for bucket in hash table | 通过它挂入哈希表 g_vnodeHashEntrys[i], i:[0,g_vnodeHashMask]*/
LIST_ENTRY actFreeEntry; /* vnode active/free list entry | 通过本节点挂到空闲链表和使用链表上*/
struct Mount *originMount; /* fs info about this vnode | 自己所在的文件系统挂载信息*/
struct Mount *newMount; /* fs info about who mount on this vnode | 其他挂载在这个节点上文件系统信息*/
char *filePath; /* file path of the vnode */
struct page_mapping mapping; /* page mapping of the vnode */
};
/*
/*!
虚拟节点操作接口,具体的文件系统只需实现这些接口函数来操作vnode.
VnodeOps 系列函数是对节点本身的操作.
*/
struct VnodeOps {
int (*Create)(struct Vnode *parent, const char *name, int mode, struct Vnode **vnode);//创建节点
int (*Lookup)(struct Vnode *parent, const char *name, int len, struct Vnode **vnode);//查询节点
int (*Create)(struct Vnode *parent, const char *name, int mode, struct Vnode **vnode);///< 创建节点
int (*Lookup)(struct Vnode *parent, const char *name, int len, struct Vnode **vnode);///<查询节点
//Lookup向底层文件系统查找获取inode信息
int (*Open)(struct Vnode *vnode, int fd, int mode, int flags);//打开节点
int (*Open)(struct Vnode *vnode, int fd, int mode, int flags);///< 打开节点
ssize_t (*ReadPage)(struct Vnode *vnode, char *buffer, off_t pos);
ssize_t (*WritePage)(struct Vnode *vnode, char *buffer, off_t pos, size_t buflen);
int (*Close)(struct Vnode *vnode);//关闭节点
int (*Reclaim)(struct Vnode *vnode);//收节点
int (*Unlink)(struct Vnode *parent, struct Vnode *vnode, const char *fileName);//取消硬链接
int (*Rmdir)(struct Vnode *parent, struct Vnode *vnode, const char *dirName);//删除目录节点
int (*Mkdir)(struct Vnode *parent, const char *dirName, mode_t mode, struct Vnode **vnode);//创建目录节点
/*
int (*Close)(struct Vnode *vnode);///< 关闭节点
int (*Reclaim)(struct Vnode *vnode);///<回 收节点
int (*Unlink)(struct Vnode *parent, struct Vnode *vnode, const char *fileName);///< 取消硬链接
int (*Rmdir)(struct Vnode *parent, struct Vnode *vnode, const char *dirName);///< 删除目录节点
int (*Mkdir)(struct Vnode *parent, const char *dirName, mode_t mode, struct Vnode **vnode);///< 创建目录节点
/*!
创建一个目录时,实际做了3件事:在其“父目录文件”中增加一个条目;分配一个inode;再分配一个存储块,
用来保存当前被创建目录包含的文件与子目录。被创建的“目录文件”中自动生成两个子目录的条目,名称分别是:“.”和“..”。
前者与该目录具有相同的inode号码,因此是该目录的一个“硬链接”。后者的inode号码就是该目录的父目录的inode号码。
所以,任何一个目录的"硬链接"总数,总是等于它的子目录总数(含隐藏目录)加2。即每个“子目录文件”中的“..”条目,
加上它自身的“目录文件”中的“.”条目,再加上“父目录文件”中的对应该目录的条目。
*/
int (*Readdir)(struct Vnode *vnode, struct fs_dirent_s *dir);//读目录节点
int (*Opendir)(struct Vnode *vnode, struct fs_dirent_s *dir);//打开目录节点
int (*Rewinddir)(struct Vnode *vnode, struct fs_dirent_s *dir);//定位目录节点
int (*Closedir)(struct Vnode *vnode, struct fs_dirent_s *dir);//关闭目录节点
int (*Getattr)(struct Vnode *vnode, struct stat *st);//获取节点属性
int (*Setattr)(struct Vnode *vnode, struct stat *st);//设置节点属性
int (*Chattr)(struct Vnode *vnode, struct IATTR *attr);//改变节点属性(change attr)
int (*Rename)(struct Vnode *src, struct Vnode *dstParent, const char *srcName, const char *dstName);//重命名
int (*Truncate)(struct Vnode *vnode, off_t len);//缩减或扩展大小
int (*Truncate64)(struct Vnode *vnode, off64_t len);//缩减或扩展大小
int (*Fscheck)(struct Vnode *vnode, struct fs_dirent_s *dir);//检查功能
int (*Readdir)(struct Vnode *vnode, struct fs_dirent_s *dir);///< 读目录节点
int (*Opendir)(struct Vnode *vnode, struct fs_dirent_s *dir);///< 打开目录节点
int (*Rewinddir)(struct Vnode *vnode, struct fs_dirent_s *dir);///< 定位目录节点
int (*Closedir)(struct Vnode *vnode, struct fs_dirent_s *dir);///< 关闭目录节点
int (*Getattr)(struct Vnode *vnode, struct stat *st);///< 获取节点属性
int (*Setattr)(struct Vnode *vnode, struct stat *st);///< 设置节点属性
int (*Chattr)(struct Vnode *vnode, struct IATTR *attr);///< 改变节点属性(change attr)
int (*Rename)(struct Vnode *src, struct Vnode *dstParent, const char *srcName, const char *dstName);///< 重命名
int (*Truncate)(struct Vnode *vnode, off_t len);///< 缩减或扩展大小
int (*Truncate64)(struct Vnode *vnode, off64_t len);///< 缩减或扩展大小
int (*Fscheck)(struct Vnode *vnode, struct fs_dirent_s *dir);///< 检查功能
int (*Link)(struct Vnode *src, struct Vnode *dstParent, struct Vnode **dst, const char *dstName);
int (*Symlink)(struct Vnode *parentVnode, struct Vnode **newVnode, const char *path, const char *target);
ssize_t (*Readlink)(struct Vnode *vnode, char *buffer, size_t bufLen);
};
/*哈希比较指针函数,使用方法,例如:
/*! 哈希比较指针函数,使用方法,例如:
* int VfsHashGet(const struct Mount *mount, uint32_t hash, struct Vnode **vnode, VfsHashCmp *fn, void *arg)
* VfsHashCmp *fn 等同于 int *fn, 此时 fn是个指针,指向了一个函数地址
* fn(vnode,arg)就是调用这个函数,返回一个int类型的值
......
......@@ -104,6 +104,7 @@ STATUS_T OsAnonMMap(LosVmMapRegion *region)
/**
* @brief
@verbatim
mmap基础概念:
一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系.
实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,
......@@ -136,6 +137,7 @@ STATUS_T OsAnonMMap(LosVmMapRegion *region)
offset 文件映射的偏移量,通常设置为0,代表从文件最前方开始对应,offset必须是PAGE_SIZE的整数倍。
成功返回:虚拟内存地址,这地址是页对齐。
失败返回:(void *)-1。
@endverbatim
* @param vaddr
* @param len
* @param prot
......
git add -A
git commit -m ' 为支持doxygen ,将// 变成 ///
git commit -m ' 为支持doxygen,改变注释表达方式.
百万汉字注解 + 百篇博客分析 => 挖透鸿蒙内核源码
博客输出站点(国内):http://weharmonyos.com
博客输出站点(国外):https://weharmony.github.io
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册