内核链表list.h文件剖析

 

内核链表list.h文件剖析

一、内核链表的结构【双向循环链表】

    内核链表的好主要体现为两点,1是可扩展性,2是封装。可以将内核链表复用到用户态编程中,以后在用户态下编程就不需要写一些关于链表的代码了,直接将内核中list.h中的代码拷贝过来用。

struct list_head
{struct list_head *next, *prev;
}; // 包含了两个指向list_head结构体的指针next,prev[后驱和前驱]

 

二、内核链表常用接口

1、INIT_LIST_HEAD:创建链表

2、list_add:在prev和next之间插入结点

3、list_add_tail:在链表尾插入结点

4、list_del:删除结点

5、list_entry:取出结点

6、list_for_each:遍历链表

【推荐看使用sourceInsight查看代码】

 

三、深入分析list.h

1offsetof【在已知某一个成员变量的名字和结构体类型的情况下,计算该成员相对于结构体的起始地址的偏移量】

#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER)__compiler_offsetof(TYPE,MEMBER)
#else
#ifndef offsetof
#define offsetof(type, member) ((size_t) &((type*)0)->member)
#endif

 

2container_of【已知某一个成员变量的名字、指针和结构体类型的情况下,计算结构体的指针,也就是计算结构体的起始地址】

#define container_of(ptr, type, member) ({ \const typeof(((type *)0)->member ) *__mptr = (ptr); \(type *)((char *)__mptr - offsetof(type,member) );})

 

3LIST_HEAD_INIT【初始化一个结点名字为name的双向循环链表的头结点】

#define LIST_HEAD_INIT(name) { &(name),&(name) }

 

4LIST_HEAD【初始化一个结点名字为name的双向循环链表的头结点】

#define LIST_HEAD(name) \struct list_head name = LIST_HEAD_INIT(name)

 

5INIT_LIST_HEAD【初始化链表节点,将next和prev指针都指向其自身,我们就构造了一个空的双循环链表。】

static __INLINE__ void INIT_LIST_HEAD(struct list_head *list)
{list->next= list;list->prev= list;
}

 

6list_add【在头结点后加一个新结点】

#ifndef CONFIG_DEBUG_LIST
static __INLINE__ void __list_add(struct list_head *new,struct list_head *prev,struct list_head *next)
{next->prev= new;new->next =next;new->prev =prev;prev->next= new;
}
#else
extern void __list_add(struct list_head *new,struct list_head *prev,struct list_head *next);
#endifstatic __INLINE__ void list_add(struct list_head *new,struct list_head *head)
{__list_add(new, head, head->next);
}

 

7list_add_tail【添加结点new到链表尾】

static __INLINE__ void list_add_tail(struct list_head *new, struct list_head *head)
{__list_add(new, head->prev, head);
} // 在head->prev和head之间加入new,即在链表尾加入new结点

 

8list_del【list_del将会将该节点与外界的“联系”切断,然后就可以使用free释放了(如果是内核态就用kfree或vfree)】

#define LIST_POISON1 0
#define LIST_POISON2 0static __INLINE__ void __list_del(struct list_head *prev, struct list_head * next)
{next->prev= prev;prev->next= next;
}#ifndef CONFIG_DEBUG_LIST
static __INLINE__ void list_del(struct list_head *entry)
{__list_del(entry->prev, entry->next);entry->next= LIST_POISON1;entry->prev= LIST_POISON2;
}
#else
extern void list_del(struct list_head *entry);
#endif

 

9list_replace【用结点new替换结点old

static __INLINE__ void list_replace(struct list_head *old,struct list_head *new)
{new->next =old->next;new->next->prev = new;new->prev =old->prev;new->prev->next = new;
}

 

10 list_replace_init【用结点new替换结点old,并初始化old

static __INLINE__ void list_replace_init(struct list_head *old,struct list_head *new)
{list_replace(old, new);INIT_LIST_HEAD(old);
}

 

11list_del_init【删除结点entry,并初始化entry

static __INLINE__ void list_del_init(struct list_head*entry)
{__list_del(entry->prev, entry->next);INIT_LIST_HEAD(entry);
}

 

12list_move【先将list节点从原链表中删除,然后将其添加到head链表的表头】

static __INLINE__ void list_move(struct list_head *list, struct list_head *head)
{__list_del(list->prev, list->next);list_add(list,head);
}

 

13list_move_tail【先将list节点从原链表中删除,然后将其添加到head链表的表尾】

static __INLINE__ void list_move_tail(struct list_head *list,struct list_head *head)
{__list_del(list->prev, list->next);list_add_tail(list, head);
}

 

14list_is_last【测试list节点是否为head链表的表尾节点。是返回1,否则返回0

static __INLINE__ int list_is_last(const struct list_head *list,const struct list_head *head)
{return list->next == head;
}

 

15list_empty【判断head链表是否为空链表,是返回1,否则返回为0

static __INLINE__ int list_empty(const struct list_head *head)
{return head->next == head;
}


16list_empty_careful【判断节点head的前驱和后驱是否都指向head。是返回1,否则返回0

static __INLINE__ int list_empty_careful(const structlist_head *head)
{struct list_head *next = head->next;return (next== head) && (next == head->prev);
}


17list_rotate_left【函数每次将头结点后的一个结点放到head链表的末尾,直到head结点后没有其他结点】

static __INLINE__ void list_rotate_left(struct list_head *head)
{struct list_head *first;if(!list_empty(head)){first =head->next;list_move_tail(first, head);}
}

 

18list_is_singular【判断head链表是否为单节点链表。是返回1,否为0

static __INLINE__ int list_is_singular(const struct list_head *head)
{return !list_empty(head) && (head->next == head->prev);
}

 

19__list_cut_position【这个函数是将head链表的头结点至entry节点之间的节点连在list节点后面,即组成以list节点为头结点的新链表】

static __INLINE__ void __list_cut_position(struct list_head *list,struct list_head *head, struct list_head *entry)
{struct list_head *new_first = entry->next;list->next= head->next;list->next->prev = list;list->prev= entry;entry->next= list;head->next= new_first;new_first->prev = head;
}<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>


20list_cut_position【与__list_cut_position功能相似,不过要先判断链表head是否为空链表,再判断是否为了单链表而且节点不是entry的情况,如果head==entry,直接初始化list

static __INLINE__ void list_cut_position(struct list_head *list,struct list_head *head, struct list_head *entry)
{if(list_empty(head)){return;}if(list_is_singular(head) &&(head->next != entry && head != entry)){return;}if (entry ==head){INIT_LIST_HEAD(list);}else{__list_cut_position(list, head, entry);}
}

 

21__list_splice【将list链表的全部节点(头节点list除外)插入在prevnext节点之间】

static __INLINE__ void __list_splice(const struct list_head *list,struct list_head *prev,struct list_head *next)
{struct list_head *first = list->next;struct list_head *last = list->prev;first->prev= prev;prev->next= first;last->next= next;next->prev= last;
}

 

22list_splice_tail【在list是非空链表的情况下,将其插在head链表的尾部,即head节点的前面】

static __INLINE__ void list_splice_tail(struct list_head *list,struct list_head *head)
{if(!list_empty(list)){__list_splice(list, head->prev, head);}
}

 

23list_splice_init【在list是非空链表的情况下,将其插在head链表的尾部,即head节点的前面。然后对list节点进行初始化,排除不安全因素】

static __INLINE__ void list_splice_init(struct list_head *list,struct list_head *head)
{if(!list_empty(list)){__list_splice(list, head, head->next);INIT_LIST_HEAD(list);}
}

 

24list_splice_tail_init【在list是非空链表的情况下,将其插在head链表的尾部,即head节点的前面。然后对list节点进行初始化,排除不安全因素】

static __INLINE__ void list_splice_tail_init(struct list_head *list, struct list_head *head)
{if(!list_empty(list)){__list_splice(list, head->prev, head);INIT_LIST_HEAD(list);}
}

 

25list_entry【获取type类型结构体的起始指针】

#define list_entry(ptr, type, member) \container_of(ptr, type, member)

 

26list_first_entry【已知type类型的结构体中member成员的指针后,求得它所在的链表的下一个指针所指的member所在的type类型的结构体的起始地址】

#define list_first_entry(ptr, type, member) \list_entry((ptr)->next, type, member)

 

27list_for_each【从head节点开始(不包括head节点)遍历它的每一个节点】

#define list_for_each(pos, head) \for (pos =(head)->next; prefetch(pos->next), pos != (head); \pos =pos->next)

 

28list_for_each_prev【它也是从head节点开始(不包括head节点)向前遍历每一个节点!即从链表的尾部开始遍历】

#define list_for_each_prev(pos, head) \for (pos =(head)->prev; prefetch(pos->prev), pos != (head); \pos =pos->prev)

 

29list_for_each_safe【从head节点开始(不包括head节点!)遍历它的每一个节点!它用n先将下一个要遍历的节点保存起来,防止删除本节点后,无法找到下一个节点,而出现错误】

#define list_for_each_safe(pos, n, head) \for (pos =(head)->next, n = pos->next; pos != (head); \pos = n, n =pos->next)

 

30list_for_each_prev_safe【它也是从head节点开始(不包括head节点)向前遍历每一个节点!即从链表的尾部开始遍历】

#define list_for_each_prev_safe(pos, n, head) \for (pos =(head)->prev, n = pos->prev; \prefetch(pos->prev), pos != (head); \pos = n,n = pos->prev)

 

31list_for_each_entry【已知指向某个结构体的指针pos,以及指向它中member成员的指针head,从下一个结构体开始向后遍历这个结构体链】

#define list_for_each_entry(pos, head, member)        \for (pos =list_entry((head)->next, typeof(*pos), member);  \prefetch(pos->member.next), &pos->member != (head);  \pos = list_entry(pos->member.next,typeof(*pos), member))

 

32list_for_each_entry_reverse【已知指向某个结构体的指针pos,以及指向它中member成员的指针head,从下一个结构体开始向前遍历这个结构体链】

#define list_for_each_entry_reverse(pos, head,member)      \for (pos =list_entry((head)->prev, typeof(*pos), member);  \prefetch(pos->member.prev), &pos->member != (head);  \pos =list_entry(pos->member.prev, typeof(*pos), member))

 

33list_prepare_entry【判断pos这个指针是否为空,为空的话给它赋值list_entry(head, typeof(*pos), member)这条语句求出来的结构体的地址】

#define list_prepare_entry(pos, head, member) \((pos) ? :list_entry(head, typeof(*pos), member))

 

34list_for_each_entry_continue【已知指向某个结构体的指针pos,以及指向它中的member成员的head指针,从它的下一个结构体开始向后遍历这个链表】

#define list_for_each_entry_continue(pos, head,member)     \for (pos =list_entry(pos->member.next, typeof(*pos), member);  \prefetch(pos->member.next), &pos->member != (head);  \pos =list_entry(pos->member.next, typeof(*pos), member))

 

35list_for_each_entry_continue_reverse【已知指向某个结构体的指针pos,以及指向它中的member成员的head指针,从它的前一个结构体开始向前遍历这个链表】

#define list_for_each_entry_continue_reverse(pos,head, member)   \for (pos =list_entry(pos->member.prev, typeof(*pos), member);  \prefetch(pos->member.prev), &pos->member != (head);  \pos =list_entry(pos->member.prev, typeof(*pos), member))

 

36list_for_each_entry_from【从pos节点开始,向后遍历链表】

#define list_for_each_entry_from(pos, head,member)       \for (;prefetch(pos->member.next), &pos->member != (head);  \pos =list_entry(pos->member.next, typeof(*pos), member))

 

37list_for_each_entry_safe【先保存下一个要遍历的节点!从head下一个节点向后遍历链表】

#define list_for_each_entry_safe(pos, n, head,member)      \for (pos =list_entry((head)->next, typeof(*pos), member),  \n =list_entry(pos->member.next, typeof(*pos), member); \&pos->member != (head);         \pos = n,n = list_entry(n->member.next, typeof(*n), member))

 

38list_for_each_entry_safe_continue【先保存下一个要遍历的节点!从pos下一个节点向后遍历链表】

#define list_for_each_entry_safe_continue(pos, n, head,member)     \for (pos =list_entry(pos->member.next, typeof(*pos), member),    \n =list_entry(pos->member.next, typeof(*pos), member);   \&pos->member != (head);           \pos = n,n = list_entry(n->member.next, typeof(*n), member))

 

39list_for_each_entry_safe_from【先保存下一个要遍历的节点!从pos节点向后遍历链表】

#define list_for_each_entry_safe_from(pos, n, head,member)       \for (n =list_entry(pos->member.next, typeof(*pos), member);    \&pos->member != (head);           \pos = n, n = list_entry(n->member.next,typeof(*n), member))

 

40list_for_each_entry_safe_reverse【先保存下一个要遍历的节点!从链表尾部向前遍历链表】

#define list_for_each_entry_safe_reverse(pos, n, head,member)    \for (pos =list_entry((head)->prev, typeof(*pos), member),  \n =list_entry(pos->member.prev, typeof(*pos), member); \&pos->member != (head);         \pos = n,n = list_entry(n->member.prev, typeof(*n), member))

 

41list_safe_reset_next【获取n结构体指针】

#define list_safe_reset_next(pos, n, member)        \n =list_entry(pos->member.next, typeof(*pos), member)

 

// hash链表头
struct hlist_head
{structh list_node *first;
};struct hlist_node
{struct hlist_node *next, **pprev;
};

1INIT_HLIST_HEAD【初始化头节点指针ptr

#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)

 

2INIT_HLIST_NODE【初始化hlist节点】

static __INLINE__ void INIT_HLIST_NODE(struct hlist_node *h)
{h->next =NULL;h->pprev =NULL;
}

 

3hlist_unhashed【判断h->pprev是否为空,是返回1,否返回0

static __INLINE__ int hlist_unhashed(const struct hlist_node *h)
{return !h->pprev;
}

 

4hlist_empty【判断hlist是否为空,是返回1,否返回0

static __INLINE__ int hlist_empty(const struct hlist_head *h)
{return !h->first;
}

 

5__hlist_del【删除结点n

static __INLINE__ void __hlist_del(struct hlist_node*n)
{struct hlist_node *next = n->next;struct hlist_node **pprev = n->pprev;*pprev = next;if (next){next->pprev = pprev;}
}

 

6hlist_del【删除结点n,将结点nextpprev分别指向LIST_POISON1LIST_POISON2。这样设置是为了保证不在链表中的结点项不能被访问】

static __INLINE__ void hlist_del(struct hlist_node *n)
{__hlist_del(n);n->next =LIST_POISON1;n->pprev =LIST_POISON2;
}

 

7hlist_del_init【先判断结点是否为空,不为空删除,再初始化节点】

static __INLINE__ void hlist_del_init(struct hlist_node *n)
{if(!hlist_unhashed(n)){__hlist_del(n);INIT_HLIST_NODE(n);}
}

 

8hlist_add_head【增加结点nh表头】

static __INLINE__ void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{struct hlist_node *first = h->first;n->next =first;if (first){first->pprev = &n->next;}h->first =n;n->pprev =&h->first;
}<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>


9hlist_add_before【增加结点n到结点next之前】

/* next must be != NULL */
static __INLINE__ void hlist_add_before(struct hlist_node *n,struct hlist_node *next)
{n->pprev =next->pprev;n->next = next;next->pprev= &n->next;*(n->pprev)= n;
}

 

10hlist_add_after【增加结点n到结点next之后】

static __INLINE__ void hlist_add_after(struct hlist_node *n,struct hlist_node *next)
{next->next= n->next;n->next =next;next->pprev= &n->next;if(next->next){next->next->pprev  =&next->next;}
}

 

11hlist_move_list【头结点new接管头结点old的所有节点,并初始化old

static __INLINE__ void hlist_move_list(struct hlist_head *old,struct hlist_head *new)
{new->first= old->first;if(new->first){new->first->pprev = &new->first;}old->first= NULL;
}

 

12hlist_entry【已知某一个成员变量的名字、指针和结构体类型的情况下,计算结构体的指针,也就是计算结构体的起始地址】

#define hlist_entry(ptr, type, member) container_of(ptr,type,member)

13hlist_for_each【遍历hlist链表】

#define hlist_for_each(pos, head) \for (pos =(head)->first; pos ; \pos =pos->next)

 

14 hlist_for_each_safe【遍历hlist链表,一般在删除结点使用】

#define hlist_for_each_safe(pos, n, head) \for (pos =(head)->first; pos && ({ n = pos->next; 1; }); \pos = n)

 

15hlist_for_each_entry【遍历找typeof(*tpos)的结构体类型入口地址】

#define hlist_for_each_entry(tpos, pos, head,member)      \for (pos =(head)->first;          \pos&& ({ prefetch(pos->next); 1;}) &&      \({ tpos =hlist_entry(pos, typeof(*tpos), member); 1;}); \pos =pos->next)

 

16hlist_for_each_entry_continue【从结点pos下一个遍历找typeof(*tpos)的结构体类型入口地址】

#define hlist_for_each_entry_continue(tpos, pos,member)     \for (pos =(pos)->next;            \pos&& ({ prefetch(pos->next); 1;}) &&      \({ tpos =hlist_entry(pos, typeof(*tpos), member); 1;}); \pos =pos->next)

 

17hlist_for_each_entry_from【从节点pos开始遍历找typeof(*tpos)的结构体类型入口地址】

#define hlist_for_each_entry_from(tpos, pos,member)       \for (; pos&& ({ prefetch(pos->next); 1;}) &&      \({ tpos =hlist_entry(pos, typeof(*tpos), member); 1;}); \pos =pos->next)

 

18hlist_for_each_entry_safe【从头节点head开始遍历找typeof(*tpos)的结构体类型入口地址】

#define hlist_for_each_entry_safe(tpos, pos, n, head,member)      \for (pos =(head)->first;          \pos&& ({ n = pos->next; 1; }) &&         \({ tpos =hlist_entry(pos, typeof(*tpos), member); 1;}); \pos = n)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/466460.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CAN总线很难吗?CAN总线看不懂是不可能的!

CAN&#xff08;Controller Area Network&#xff09;即控制器局域网&#xff0c;是一种能够实现分布式实时控制的串行通信网络。想到CAN就要想到德国的Bosch公司&#xff0c;因为CAN就是这个公司开发的&#xff08;和Intel&#xff09;CAN有很多优秀的特点&#xff0c;使得它能…

C语言必须写main函数?最简单的 Hello world 你其实一点都不懂!

我们在刚写程序的时候&#xff0c;第一个都是 hello world&#xff0c;而在这里&#xff0c;完整的代码就是&#xff1a;我们打眼一看&#xff0c;其实很简单&#xff0c;就是引入头文件&#xff0c;写一个主函数&#xff0c;然后输出一句话&#xff0c;但是当我们编译出来ELF的…

源码包安装

一、源码包和RPM包的区别 1、区别 安装之前的区别&#xff1a;概念上的区别 安装之后的区别&#xff1a;安装位置不同 2、RPM包安装位置 是安装在默认位置中 注&#xff1a;安装位置是写RPM包的作者决定的 注&#xff1a;RPM包支持指定安装位置&#xff0c;但是不建议指定位置安…

Linux下修改SSH登录端口

Linux下修改SSH登录端口LINUX 的默认SSH 端口是 22。为了防止别人暴力破解&#xff0c;建议修改SSH 访问端口&#xff1a;vim /etc/ssh/sshd_config 找到Port 22 这一行&#xff0c;这是是默认端口22&#xff0c;现在改成Port 1234这个数字自己定&#xff0c;但是不要超过65536…

五岁的时候,你在干什么?

文章写在2021.2.7号——想不到啊想不到&#xff0c;这么快你就五岁了。今天是楠哥的生日&#xff0c;在五年前的今天&#xff0c;小伙子在深圳宝安妇幼出生&#xff0c;刚出生的时候&#xff0c;样子很丑&#xff0c;第一次见面&#xff0c;我心情很紧张&#xff0c;不怎么敢靠…

TQ210——核心板和底板

TQ210——核心板和底板 1、TQ210简介【TQ210_COREB核心板 TQ210_BOARD_V4底板】 三星Cortex-A8 S5PV210芯片&#xff0c;运行最大频率1GHZ&#xff0c;处理器内部为64/32位总线结构&#xff0c;32/32KB一级缓存&#xff0c;512KB二级缓存。自带3D加速引擎&#xff08;SGX540&a…

“华为天才少年”自制百大Up奖杯,网友:技术难度不高侮辱性极强

来源&#xff1a;雷锋网B站硬核黑科技Up主、AI算法工程师稚晖君&#xff0c;停更了好一阵子后&#xff0c;这位自称野生钢铁侠的超硬核Up主终于又发布了新作品。之所以停更这么久&#xff0c;Up主解释说&#xff0c;不是因为在野外被捕了&#xff0c;纯粹是因为工作太忙了。熟悉…

终极教程,带具体实验现象,1个GPIO控制2个LED显示4种状态,欢迎讨论!

芯片之家前几天发了一篇文章&#xff0c;讨论1个GPIO控制2个LED显示4种状态&#xff0c;并没有带具体的实验现象&#xff0c;有点小遗憾&#xff1a;绝妙&#xff01;1个GPIO控制2个LED显示4种状态&#xff0c;什么&#xff1f;你不信&#xff1f;&#xff08;点击阅读&#xf…

《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #15 ramzswap

HACK #15 ramzswap 本节介绍将一部分内存作为交换设备使用的ramzswap。ramzswap是将一部分内存空间作为交换设备使用的基于RAM的块设备。对要换出&#xff08;swapout&#xff09;的页面进行压缩后&#xff0c;不是写入磁盘&#xff0c;而是写入内存。可以使用的内存仅为完成压…

TQ210——底板部分原理图

TQ210——底板部分原理图 1、主电源接口&#xff08;过压过流保护&#xff09; 2、串行接口 2路5线RS232电平的DB9接口&#xff0c;4路TTLT电平扩展接口 COM1和PC之间通信需使用直连串口线 COM2和PC通信需使用交叉串口线 接口UART2可接GPRS模块实现打电话发短信功能&#xff1b…

有趣的灵魂百里挑一,Linux同学你低下头干嘛,起来说下这个问题。

今天我们不讲源码&#xff0c;不说面试题&#xff0c;我们来说点轻松的&#xff0c;聊点好玩的&#xff0c;我们来看看linux下有哪些酷酷的&#xff0c;有意思的命令。0x00 sl - 呜呜&#xff0c;让开&#xff0c;小火车来啦0x01 cowsay - 一起来 say hello0x02 cmatrix - 超级…

nodejs在cmd提示不是内部或外部命令解决方法

今天用cmd安装个库,结果发现node不是内部命令,于是搜索了下解决方法,解决方法是&#xff1a; . 找到变量值中node的安装地址,比如C:develop\nodejs,如果不是这个地址改成现在新的安装的地址,然后保存,重新打开cmd,输入 node -v 查看版本号,就会发现一切正常啦!当然这只针对于安…

用mtrace定位内存泄漏

一. 缘起有的公众号读者&#xff0c;看完我上次写给大学生的查bug方法后&#xff0c;希望我多分享一些查bug的实践经验和具体步骤&#xff0c;比如如何查内存泄漏和core dump问题。所以&#xff0c;就打算写这篇文章。二. 内存泄漏简介内存泄漏&#xff0c;是一个谈虎色变的问题…

为什么每个人都应该尝试Ubuntu下篇 Why Everyone Should Try Ubuntu 分享

但是老实说&#xff0c;我认为 Ubuntu 拥有长期的生存能力重要于其短期的实用主义。最近几年来&#xff0c;对于改进 Linux 桌面方面&#xff0c;Ubuntu 做得比其他发行版本要多。这就是我已详细讨论的&#xff0c;其带来的实际好处&#xff0c;但它也有许多不明确的地方。感谢…

陪我长大的村,镇,学校和家乡

我们村在广西河池市金城江区九圩镇江潭村山岳屯上面的字可以省略&#xff0c;因为看起来确实麻烦。地图上是这样显示的。小说我们村的那座桥&#xff0c;那座老桥据说是我爷爷那一辈人建起来的&#xff0c;用的是实打实的大石头。我上小学那段那会&#xff0c;我们同龄的小孩都…

Linux硬盘分区

MBR分区主分区&#xff1a;14&#xff0c;一块硬盘最多4个主分区&#xff0c;对主机必须有&#xff0c;主分区可以格式化ntfs&#xff0c;存数据。扩展分区&#xff1a;14&#xff0c;一块硬盘最多一个扩展分区&#xff0c;可以没有扩展分区&#xff0c;可以划分成更小的单元&a…

使用FileZilla Server轻松搭建个人FTP服务器

使用FileZilla Server轻松搭建个人FTP服务器 添加一个用户&#xff0c;然后在“共享文件夹”下设置将要设为FTP目录的文件夹和操作权限&#xff0c;点击确定 https://jingyan.baidu.com/article/ed15cb1bb6de421be3698188.html 可以为不同的用户创建不同的目录&#xff0c;新建…

TQ210——启动方式

TQ210——启动方式

AngularJS:应用

ylbtech-AngularJS&#xff1a;应用1.返回顶部 1、AngularJS 应用 现在是时候创建一个真正的 AngularJS 单页 Web 应用&#xff08;single page web application&#xff0c;SPA&#xff09;了。 AngularJS 应用实例 您已经学习了足够多关于 AngularJS 的知识&#xff0c;现在可…

你可能对电灯泡一无所知

今天在公众号“电路啊中看到一篇推文”超低成本的LED恒流驱动电路[1] &#xff0c;作者讲述了看到一个仅仅售价1元人民币的LED灯泡&#xff0c;并包邮时所感到的惊讶。为了消除惊讶&#xff0c;作者还花巨资&#xff08;1&#xff09;购买 薅羊毛 了该款灯泡并拆开一看究竟。▲…