侵权投诉

通过实现一个简单的malloc来描述malloc背后的机制

C语言专家集中营 2018-01-27 23:30 次阅读

任何一个用过或学过C的人对malloc都不会陌生。大家都知道malloc可以分配一段连续的内存空间,并且在不再使用时可以通过free释放掉。但是,许多程序员对malloc背后的事情并不熟悉,许多人甚至把malloc当做操作系统所提供的系统调用或C的关键字。实际上,malloc只是C的标准库中提供的一个普通函数,而且实现malloc的基本思想并不复杂,任何一个对C和操作系统有些许了解的程序员都可以很容易理解。

这篇文章通过实现一个简单的malloc来描述malloc背后的机制。当然与现有C的标准库实现(例如glibc)相比,我们实现的malloc并不是特别高效,但是这个实现比目前真实的malloc实现要简单很多,因此易于理解。重要的是,这个实现和真实实现在基本原理上是一致的。

这篇文章将首先介绍一些所需的基本知识,如操作系统对进程的内存管理以及相关的系统调用,然后逐步实现一个简单的malloc。为了简单起见,这篇文章将只考虑x86_64体系结构,操作系统为Linux

1 什么是malloc

2 预备知识

2.2.1 内存排布

2.2.2 Heap内存模型

2.2.3 brk与sbrk

2.2.4 资源限制与rlimit

2.1.1 虚拟内存地址与物理内存地址

2.1.2 页与地址构成

2.1.3 内存页与磁盘页

2.1 Linux内存管理

2.2 Linux进程级内存管理

3 实现malloc

3.2.1 数据结构

3.2.2 寻找合适的block

3.2.3 开辟新的block

3.2.4 分裂block

3.2.5 malloc的实现

3.2.6 calloc的实现

3.2.7 free的实现

3.2.8 realloc的实现

3.1 玩具实现

3.2 正式实现

3.3 遗留问题和优化

4 其它参考

1 什么是malloc

在实现malloc之前,先要相对正式地对malloc做一个定义。

根据标准C库函数的定义,malloc具有如下原型:

void* malloc(size_t size);

这个函数要实现的功能是在系统中分配一段连续的可用的内存,具体有如下要求:

malloc分配的内存大小至少为size参数所指定的字节数

malloc的返回值是一个指针,指向一段可用内存的起始地址

多次调用malloc所分配的地址不能有重叠部分,除非某次malloc所分配的地址被释放掉

malloc应该尽快完成内存分配并返回(不能使用NP-hard的内存分配算法)

实现malloc时应同时实现内存大小调整和内存释放函数(即realloc和free)

对于malloc更多的说明可以在命令行中键入以下命令查看:

man malloc

2 预备知识

在实现malloc之前,需要先解释一些Linux系统内存相关的知识。

2.1 Linux内存管理

2.1.1 虚拟内存地址与物理内存地址

为了简单,现代操作系统在处理内存地址时,普遍采用虚拟内存地址技术。即在汇编程序(或机器语言)层面,当涉及内存地址时,都是使用虚拟内存地址。采用这种技术时,每个进程仿佛自己独享一片2N

字节的内存,其中N是机器位数。例如在64位CPU和64位操作系统下,每个进程的虚拟地址空间为264

Byte

这种虚拟地址空间的作用主要是简化程序的编写及方便操作系统对进程间内存的隔离管理,真实中的进程不太可能(也用不到)如此大的内存空间,实际能用到的内存取决于物理内存大小。

由于在机器语言层面都是采用虚拟地址,当实际的机器码程序涉及到内存操作时,需要根据当前进程运行的实际上下文将虚拟地址转换为物理内存地址,才能实现对真实内存数据的操作。这个转换一般由一个叫MMU(Memory Management Unit)的硬件完成。

2.1.2 页与地址构成

在现代操作系统中,不论是虚拟内存还是物理内存,都不是以字节为单位进行管理的,而是以页(Page)为单位。一个内存页是一段固定大小的连续内存地址的总称,具体到Linux中,典型的内存页大小为4096Byte(4K)。

所以内存地址可以分为页号和页内偏移量。下面以64位机器,4G物理内存,4K页大小为例,虚拟内存地址和物理内存地址的组成如下:

通过实现一个简单的malloc来描述malloc背后的机制

上面是虚拟内存地址,下面是物理内存地址。由于页大小都是4K,所以页内便宜都是用低12位表示,而剩下的高地址表示页号。

MMU映射单位并不是字节,而是页,这个映射通过查一个常驻内存的数据结构页表来实现。现在计算机具体的内存地址映射比较复杂,为了加快速度会引入一系列缓存和优化,例如TLB等机制。下面给出一个经过简化的内存地址翻译示意图,虽然经过了简化,但是基本原理与现代计算机真实的情况的一致的。

2.1.3 内存页与磁盘页

我们知道一般将内存看做磁盘的的缓存,有时MMU在工作时,会发现页表表明某个内存页不在物理内存中,此时会触发一个缺页异常(Page Fault),此时系统会到磁盘中相应的地方将磁盘页载入到内存中,然后重新执行由于缺页而失败的机器指令。关于这部分,因为可以看做对malloc实现是透明的,所以不再详细讲述,有兴趣的可以参考《深入理解计算机系统》相关章节。

最后附上一张在维基百科找到的更加符合真实地址翻译的流程供大家参考,这张图加入了TLB和缺页异常的流程(图片来源页)。

通过实现一个简单的malloc来描述malloc背后的机制

2.2 Linux进程级内存管理

2.2.1 内存排布

明白了虚拟内存和物理内存的关系及相关的映射机制,下面看一下具体在一个进程内是如何排布内存的。

以Linux 64位系统为例。理论上,64bit内存地址可用空间为0x0000000000000000 ~ 0xFFFFFFFFFFFFFFFF,这是个相当庞大的空间,Linux实际上只用了其中一小部分(256T)。

根据Linux内核相关文档描述,Linux64位操作系统仅使用低47位,高17位做扩展(只能是全0或全1)。所以,实际用到的地址为空间为0x0000000000000000 ~ 0x00007FFFFFFFFFFF和0xFFFF800000000000 ~ 0xFFFFFFFFFFFFFFFF,其中前面为用户空间(User Space),后者为内核空间(Kernel Space)。图示如下:

通过实现一个简单的malloc来描述malloc背后的机制

对用户来说,主要关注的空间是User Space。将User Space放大后,可以看到里面主要分为如下几段:

Code:这是整个用户空间的最低地址部分,存放的是指令(也就是程序所编译成的可执行机器码)

Data:这里存放的是初始化过的全局变量

BSS:这里存放的是未初始化的全局变量

Heap:堆,这是我们本文重点关注的地方,堆自低地址向高地址增长,后面要讲到的brk相关的系统调用就是从这里分配内存

Mapping Area:这里是与mmap系统调用相关的区域。大多数实际的malloc实现会考虑通过mmap分配较大块的内存区域,本文不讨论这种情况。这个区域自高地址向低地址增长

Stack:这是栈区域,自高地址向低地址增长

下面我们主要关注Heap区域的操作。对整个Linux内存排布有兴趣的同学可以参考其它资料。

2.2.2 Heap内存模型

一般来说,malloc所申请的内存主要从Heap区域分配(本文不考虑通过mmap申请大块内存的情况)。

由上文知道,进程所面对的虚拟内存地址空间,只有按页映射到物理内存地址,才能真正使用。受物理存储容量限制,整个堆虚拟内存空间不可能全部映射到实际的物理内存。Linux对堆的管理示意如下:

通过实现一个简单的malloc来描述malloc背后的机制

Linux维护一个break指针,这个指针指向堆空间的某个地址。从堆起始地址到break之间的地址空间为映射好的,可以供进程访问;而从break往上,是未映射的地址空间,如果访问这段空间则程序会报错。

2.2.3 brk与sbrk

由上文知道,要增加一个进程实际的可用堆大小,就需要将break指针向高地址移动。Linux通过brk和sbrk系统调用操作break指针。两个系统调用的原型如下:

int brk(void*addr);

void*sbrk(intptr_t increment);

brk将break指针直接设置为某个地址,而sbrk将break从当前位置移动increment所指定的增量。brk在执行成功时返回0,否则返回-1并设置errno为ENOMEM;sbrk成功时返回break移动之前所指向的地址,否则返回(void *)-1。

一个小技巧是,如果将increment设置为0,则可以获得当前break的地址。

另外需要注意的是,由于Linux是按页进行内存映射的,所以如果break被设置为没有按页大小对齐,则系统实际上会在最后映射一个完整的页,从而实际已映射的内存空间比break指向的地方要大一些。但是使用break之后的地址是很危险的(尽管也许break之后确实有一小块可用内存地址)。

2.2.4 资源限制与rlimit

系统对每一个进程所分配的资源不是无限的,包括可映射的内存空间,因此每个进程有一个rlimit表示当前进程可用的资源上限。这个限制可以通过getrlimit系统调用得到,下面代码获取当前进程虚拟内存空间的rlimit:

int main(){

struct rlimit *limit =(struct rlimit *)malloc(sizeof(struct rlimit));

getrlimit(RLIMIT_AS, limit);

printf("soft limit: %ld, hard limit: %ld\n", limit->rlim_cur, limit->rlim_max);

}

其中rlimit是一个结构体:

struct rlimit {

rlim_t rlim_cur;/* Soft limit */

rlim_t rlim_max;/* Hard limit (ceiling for rlim_cur) */

};

每种资源有软限制和硬限制,并且可以通过setrlimit对rlimit进行有条件设置。其中硬限制作为软限制的上限,非特权进程只能设置软限制,且不能超过硬限制。

3 实现malloc

3.1 玩具实现

在正式开始讨论malloc的实现前,我们可以利用上述知识实现一个简单但几乎没法用于真实的玩具malloc,权当对上面知识的复习:

/* 一个玩具malloc */

#include

#include

void*malloc(size_t size)

{

void*p;

p = sbrk(0);

if(sbrk(size)==(void*)-1)

return NULL;

return p;

}

这个malloc每次都在当前break的基础上增加size所指定的字节数,并将之前break的地址返回。这个malloc由于对所分配的内存缺乏记录,不便于内存释放,所以无法用于真实场景。

3.2 正式实现

下面严肃点讨论malloc的实现方案。

3.2.1 数据结构

首先我们要确定所采用的数据结构。一个简单可行方案是将堆内存空间以块(Block)的形式组织起来,每个块由meta区和数据区组成,meta区记录数据块的元信息(数据区大小、空闲标志位、指针等等),数据区是真实分配的内存区域,并且数据区的第一个字节地址即为malloc返回的地址。

可以用如下结构体定义一个block:

typedefstruct s_block *t_block;

struct s_block {

size_t size;/* 数据区大小 */

t_block next;/* 指向下个块的指针 */

int free;     /* 是否是空闲块 */

int padding;/* 填充4字节,保证meta块长度为8的倍数 */

char data[1]/* 这是一个虚拟字段,表示数据块的第一个字节,长度不应计入meta */

};

由于我们只考虑64位机器,为了方便,我们在结构体最后填充一个int,使得结构体本身的长度为8的倍数,以便内存对齐。示意图如下:

通过实现一个简单的malloc来描述malloc背后的机制

3.2.2 寻找合适的block

现在考虑如何在block链中查找合适的block。一般来说有两种查找算法:

First fit:从头开始,使用第一个数据区大小大于要求size的块所谓此次分配的块

Best fit:从头开始,遍历所有块,使用数据区大小大于size且差值最小的块作为此次分配的块

两种方法各有千秋,best fit具有较高的内存使用率(payload较高),而first fit具有更好的运行效率。这里我们采用first fit算法。

/* First fit */

t_block find_block(t_block *last,size_t size){

t_block b = first_block;

while(b &&!(b->free && b->size >= size)){

*last = b;

b = b->next;

}

return b;

}

find_block从frist_block开始,查找第一个符合要求的block并返回block起始地址,如果找不到这返回NULL。这里在遍历时会更新一个叫last的指针,这个指针始终指向当前遍历的block。这是为了如果找不到合适的block而开辟新block使用的,具体会在接下来的一节用到。

3.2.3 开辟新的block

如果现有block都不能满足size的要求,则需要在链表最后开辟一个新的block。这里关键是如何只使用sbrk创建一个struct:

#define BLOCK_SIZE 24/* 由于存在虚拟的data字段,sizeof不能正确计算meta长度,这里手工设置 */

t_block extend_heap(t_block last,size_t s){

t_block b;

b = sbrk(0);

if(sbrk(BLOCK_SIZE + s)==(void*)-1)

return NULL;

b->size = s;

b->next = NULL;

if(last)

last->next = b;

b->free =0;

return b;

}

3.2.4 分裂block

First fit有一个比较致命的缺点,就是可能会让很小的size占据很大的一块block,此时,为了提高payload,应该在剩余数据区足够大的情况下,将其分裂为一个新的block,示意如下:

通过实现一个简单的malloc来描述malloc背后的机制

实现代码:

void split_block(t_block b,size_t s){

t_block new;

new= b->data + s;

new->size = b->size - s - BLOCK_SIZE ;

new->next = b->next;

new->free =1;

b->size = s;

b->next =new;

}

3.2.5 malloc的实现

有了上面的代码,我们可以利用它们整合成一个简单但初步可用的malloc。注意首先我们要定义个block链表的头first_block,初始化为NULL;另外,我们需要剩余空间至少有BLOCK_SIZE + 8才执行分裂操作。

由于我们希望malloc分配的数据区是按8字节对齐,所以在size不为8的倍数时,我们需要将size调整为大于size的最小的8的倍数:

size_t align8(size_t s){

if(s &0x7==0)

return s;

return((s >>3)+1)<<3;

}

#define BLOCK_SIZE 24

void*first_block=NULL;

/* other functions... */

void*malloc(size_t size){

t_block b, last;

size_t s;

/* 对齐地址 */

s = align8(size);

if(first_block){

/* 查找合适的block */

last = first_block;

b = find_block(&last, s);

if(b){

/* 如果可以,则分裂 */

if((b->size - s)>=( BLOCK_SIZE +8))

split_block(b, s);

b->free =0;

}else{

/* 没有合适的block,开辟一个新的 */

b = extend_heap(last, s);

if(!b)

return NULL;

}

}else{

b = extend_heap(NULL, s);

if(!b)

return NULL;

first_block = b;

}

return b->data;

}

3.2.6 calloc的实现

有了malloc,实现calloc只要两步:

malloc一段内存

将数据区内容置为0

由于我们的数据区是按8字节对齐的,所以为了提高效率,我们可以每8字节一组置0,而不是一个一个字节设置。我们可以通过新建一个size_t指针,将内存区域强制看做size_t类型来实现。

void*calloc(size_t number,size_t size){

size_t*new;

size_t s8, i;

new= malloc(number * size);

if(new){

s8 = align8(number * size)>>3;

for(i =0; i < s8; i++)

new[i]=0;

}

returnnew;

}

3.2.7 free的实现

free的实现并不像看上去那么简单,这里我们要解决两个关键问题:

如何验证所传入的地址是有效地址,即确实是通过malloc方式分配的数据区首地址

如何解决碎片问题

首先我们要保证传入free的地址是有效的,这个有效包括两方面:

地址应该在之前malloc所分配的区域内,即在first_block和当前break指针范围内

这个地址确实是之前通过我们自己的malloc分配的

第一个问题比较好解决,只要进行地址比较就可以了,关键是第二个问题。这里有两种解决方案:一是在结构体内埋一个magic number字段,free之前通过相对偏移检查特定位置的值是否为我们设置的magic number,另一种方法是在结构体内增加一个magic pointer,这个指针指向数据区的第一个字节(也就是在合法时free时传入的地址),我们在free前检查magic pointer是否指向参数所指地址。这里我们采用第二种方案:

首先我们在结构体中增加magic pointer(同时要修改BLOCK_SIZE):

typedefstruct s_block *t_block;

struct s_block {

size_t size;/* 数据区大小 */

t_block next;/* 指向下个块的指针 */

int free;     /* 是否是空闲块 */

int padding;/* 填充4字节,保证meta块长度为8的倍数 */

void*ptr;    /* Magic pointer,指向data */

char data[1]/* 这是一个虚拟字段,表示数据块的第一个字节,长度不应计入meta */

};

然后我们定义检查地址合法性的函数:

t_block get_block(void*p){

char*tmp;

tmp = p;

return(p = tmp -= BLOCK_SIZE);

}

int valid_addr(void*p){

if(first_block){

if(p > first_block && p < sbrk(0)){

return p ==(get_block(p))->ptr;

}

}

return0;

}

当多次malloc和free后,整个内存池可能会产生很多碎片block,这些block很小,经常无法使用,甚至出现许多碎片连在一起,虽然总体能满足某此malloc要求,但是由于分割成了多个小block而无法fit,这就是碎片问题。

一个简单的解决方式时当free某个block时,如果发现它相邻的block也是free的,则将block和相邻block合并。为了满足这个实现,需要将s_block改为双向链表。修改后的block结构如下:

typedefstruct s_block *t_block;

struct s_block {

size_t size;/* 数据区大小 */

t_block prev;/* 指向上个块的指针 */

t_block next;/* 指向下个块的指针 */

int free;     /* 是否是空闲块 */

int padding;/* 填充4字节,保证meta块长度为8的倍数 */

void*ptr;    /* Magic pointer,指向data */

char data[1]/* 这是一个虚拟字段,表示数据块的第一个字节,长度不应计入meta */

};

合并方法如下:

t_block fusion(t_block b){

if(b->next && b->next->free){

b->size += BLOCK_SIZE + b->next->size;

b->next = b->next->next;

if(b->next)

b->next->prev = b;

}

return b;

}

有了上述方法,free的实现思路就比较清晰了:首先检查参数地址的合法性,如果不合法则不做任何事;否则,将此block的free标为1,并且在可以的情况下与后面的block进行合并。如果当前是最后一个block,则回退break指针释放进程内存,如果当前block是最后一个block,则回退break指针并设置first_block为NULL。实现如下:

void free(void*p){

t_block b;

if(valid_addr(p)){

b = get_block(p);

b->free =1;

if(b->prev && b->prev->free)

b = fusion(b->prev);

if(b->next)

fusion(b);

else{

if(b->prev)

b->prev->prev = NULL;

else

first_block = NULL;

brk(b);

}

}

}

3.2.8 realloc的实现

为了实现realloc,我们首先要实现一个内存复制方法。如同calloc一样,为了效率,我们以8字节为单位进行复制:

void copy_block(t_block src, t_block dst){

size_t*sdata,*ddata;

size_t i;

sdata = src->ptr;

ddata = dst->ptr;

for(i =0;(i *8)< src->size &&(i *8)< dst->size; i++)

ddata[i]= sdata[i];

}

然后我们开始实现realloc。一个简单(但是低效)的方法是malloc一段内存,然后将数据复制过去。但是我们可以做的更高效,具体可以考虑以下几个方面:

如果当前block的数据区大于等于realloc所要求的size,则不做任何操作

如果新的size变小了,考虑split

如果当前block的数据区不能满足size,但是其后继block是free的,并且合并后可以满足,则考虑做合并

下面是realloc的实现:

void*realloc(void*p,size_t size){

size_t s;

t_block b,new;

void*newp;

if(!p)

/* 根据标准库文档,当p传入NULL时,相当于调用malloc */

return malloc(size);

if(valid_addr(p)){

s = align8(size);

b = get_block(p);

if(b->size >= s){

if(b->size - s >=(BLOCK_SIZE +8))

split_block(b,s);

}else{

/* 看是否可进行合并 */

if(b->next && b->next->free

&&(b->size + BLOCK_SIZE + b->next->size)>= s){

fusion(b);

if(b->size - s >=(BLOCK_SIZE +8))

split_block(b, s);

}else{

/* 新malloc */

newp = malloc (s);

if(!newp)

return NULL;

new= get_block(newp);

copy_block(b,new);

free(p);

return(newp);

}

}

return(p);

}

return NULL;

}

3.3 遗留问题和优化

以上是一个较为简陋,但是初步可用的malloc实现。还有很多遗留的可能优化点,例如:

同时兼容32位和64位系统

在分配较大快内存时,考虑使用mmap而非sbrk,这通常更高效

可以考虑维护多个链表而非单个,每个链表中的block大小均为一个范围内,例如8字节链表、16字节链表、24-32字节链表等等。此时可以根据size到对应链表中做分配,可以有效减少碎片,并提高查询block的速度

可以考虑链表中只存放free的block,而不存放已分配的block,可以减少查找block的次数,提高效率

还有很多可能的优化,这里不一一赘述。下面附上一些参考文献,有兴趣的同学可以更深入研究。

4 其它参考

这篇文章大量参考了A malloc Tutorial,其中一些图片和代码直接引用了文中的内容,这里特别指出

Computer Systems: A Programmer's Perspective, 2/E一书有许多值得参考的地方

关于Linux的虚拟内存模型,Anatomy of a Program in Memory是很好的参考资料,另外作者还有一篇How the Kernel Manages Your Memory对于Linux内核中虚拟内存管理的部分有很好的讲解

对于真实世界的malloc实现,可以参考glibc的实现

原文标题:如何实现一个malloc

文章出处:【微信号:C_Expert,微信公众号:C语言专家集中营】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
分享:

评论

相关推荐

Linux的发展前景怎么样

言归正传,关于Linux的前景如何,就目前Linux在各行各业中所起到的作用,可以预测未来3-8年内....
发表于 07-10 17:15 54次 阅读
Linux的发展前景怎么样

关于嵌入式Linux项目开发内容

 对于嵌入式Linux开发所需要的知识,也正是处理数据流转时所需要的技术, 对于嵌入式Linux项目....
发表于 07-10 16:57 8次 阅读
关于嵌入式Linux项目开发内容

Linux的特点_Linux与windows的区别

Linux是一套免费使用和自由传播的类Unix操作系统,是一个多用户、多任务、支持多线程和多CPU的....
发表于 07-10 16:53 43次 阅读
Linux的特点_Linux与windows的区别

Linux如何安装软件

在任何操作系统中,我们都需要安装应用程序来完成日常任务。在Windows世界中,每个程序都有一个简单....
发表于 07-10 16:38 9次 阅读
Linux如何安装软件

Linux嵌入式操作系统有哪些优势

嵌入式Linux操作系统是将Linux操作系统进行裁剪,使Linux操作系统能够在嵌入式计算机系统上....
发表于 07-10 16:34 66次 阅读
Linux嵌入式操作系统有哪些优势

Linux操作系统与Windows操作系统的五大区别

Linux操作系统和Windows操作系统身为三大操作系统巨头里面的两大巨头,他们有什么区别呢?首先....
发表于 07-10 16:28 28次 阅读
Linux操作系统与Windows操作系统的五大区别

基于linux的十大操作系统排名

基于linux内核开源的特性,并随着linux的不断发展,各种基于Linux的操作系统版本也在不断发....
发表于 07-10 16:23 13次 阅读
基于linux的十大操作系统排名

关于ARM的三种中断调试方法介绍

在整个流程中,用户首先需要建立工程并对工程做初步的配置,包括配置处理器和配置调试设备。编辑工程文件,....
发表于 07-10 16:01 12次 阅读
关于ARM的三种中断调试方法介绍

在Linux系统下使用top命令查看CPU使用情况

在Linux系统下,使用top命令查看CPU使用情况。
发表于 07-10 11:46 13次 阅读
在Linux系统下使用top命令查看CPU使用情况

开源技术超八成企业认可 开源人才短缺已成行业痛点

近年来,全球开源产业链已经形成,中国企业对开源技术的接受程度也逐年增高,超八成的企业认可开源技术,国....
的头像 工程师人生 发表于 07-10 11:41 91次 阅读
开源技术超八成企业认可 开源人才短缺已成行业痛点

黑客常用的Kali Linux是什么_有什么用

我们很多人都会对黑客感兴趣,甚至觉得黑客霸气外漏,当然也有人是觉得黑客是遭人唾弃,没干啥好事儿。先不....
发表于 07-10 11:38 84次 阅读
黑客常用的Kali Linux是什么_有什么用

Linux创建者说,我不再编写任何代码

VMware副总裁问道:“你怎么办?”,Linux的创建者回答说,他回答了电子邮件。Linus的创建....
的头像 倩倩 发表于 07-10 10:30 124次 阅读
Linux创建者说,我不再编写任何代码

家用路由器使用过时的Linux操作系统,存在着网络安全的漏洞

至于厂商使用了多少保护嵌入式设备的缓解技术,研究显示AVM使用的防护最多,而友讯最少。整体而言,研究....
发表于 07-10 09:15 55次 阅读
家用路由器使用过时的Linux操作系统,存在着网络安全的漏洞

三星DeX将Linux PC体验带入移动设备

“尽管处于试用阶段,但Galaxy on Linux是我们的创新解决方案,它将PC上的Linux体验....
发表于 07-10 08:21 53次 阅读
三星DeX将Linux PC体验带入移动设备

使用C语言写出的九九乘法表

本文档的主要内容详细介绍的是使用C语言写出的九九乘法表资料合集免费下载。
发表于 07-10 08:00 8次 阅读
使用C语言写出的九九乘法表

系统封装之系统减肥的操作步骤

为了减少Ghost恢复镜像备份文件时所需时间,或在一张光盘上尽可能安装更多的软件,这就需要将Ghos....
发表于 07-09 11:27 18次 阅读
系统封装之系统减肥的操作步骤

Linux查看服务器状态的命令大全

Linux查看服务器状态的命令大全
发表于 07-09 09:34 25次 阅读
Linux查看服务器状态的命令大全

C语言之结构体的声明与定义

我们刚刚申请了一个名叫Info的结构体类型,那么理论上我们可以像声明其他变量的操作一样,去声明我们的....
的头像 玩转单片机 发表于 07-09 09:06 121次 阅读
C语言之结构体的声明与定义

如何把C++的源程序改写成C语言

由于C++解释器比C语言解释器占用的存储空间要大500k左右。为了节省有限的存储空间,降低成本,同时也为了提高效率,将用C+...
发表于 07-08 20:51 134次 阅读
如何把C++的源程序改写成C语言

操作系统的置换算法FIFO-OPT-LRU实现代码

在一个请求分页系统中,设页面大小占100个单元,假如系统分配给一个作业的物理块数为3,试求出用FIF....
发表于 07-08 17:24 15次 阅读
操作系统的置换算法FIFO-OPT-LRU实现代码

Linux内核配置编译分析的设计方案

Linux内核配置编译分析的设计方案
发表于 07-08 16:53 69次 阅读
Linux内核配置编译分析的设计方案

熊谱翔:RT-Thread到底会走向何方?

由于个人兴趣及项目需求动手写了一个RTOS,在2006年初发布了RT-Thread的第一个内核版本并....
的头像 RTThread物联网操作系统 发表于 07-08 15:57 113次 阅读
熊谱翔:RT-Thread到底会走向何方?

一文知道uClinux和Linux的异同

  uClinux是针对控制领域的嵌入式linux操作系统,它从Linux2.0/2.4内核派生而来,沿袭了主流Linux的绝大部分特性。适合不具...
发表于 07-08 15:38 56次 阅读
一文知道uClinux和Linux的异同

和ASAN相比,HWASAN具有哪些缺点?

Shadow memory有一些元数据的思维在里面。它虽然也是内存中的一块区域,但是其中的数据仅仅反....
发表于 07-08 15:24 33次 阅读
和ASAN相比,HWASAN具有哪些缺点?

linux虚拟机下使用IOMETER测试磁盘的IO性能

前面有分享了windows下如何使用IOMETER来测试网络磁盘的IO性能,今天分享一下linux下....
发表于 07-08 10:32 22次 阅读
linux虚拟机下使用IOMETER测试磁盘的IO性能

《Linux驱动开发入门与实战_linux典藏大系_郑强》高清pdf下载链接

《Linux驱动开发入门与实战_linux典藏大系_郑强》高清pdf下载链接百度网盘链接:提取码:dat9 下载链接2: 内容简介 ...
发表于 07-07 21:09 69次 阅读
《Linux驱动开发入门与实战_linux典藏大系_郑强》高清pdf下载链接

嵌入式Linux系统篇:RealARM2410技术手册

  RealARM2410是基于三星公司S3C2410X 高性能ARM 处理器的嵌入开发平台,旨在为....
发表于 07-07 16:35 33次 阅读
嵌入式Linux系统篇:RealARM2410技术手册

单片机C语言程序设计的详细资料

C语言目前己成为电子工程师进行单片机系统开发时的常用编程语言。用C语言来编写目标系统软件,会大大缩短....
发表于 07-07 14:48 50次 阅读
单片机C语言程序设计的详细资料

I.MX6ULL终结者开发板裸机仿真jlink调试

I.MX6ULL‘终结者’开发板预留了JTAG仿真接口,并给出了开发文档,可以实现在JLINK仿真器条件下的单步跟踪、断点...
发表于 07-07 10:56 183次 阅读
I.MX6ULL终结者开发板裸机仿真jlink调试

Windows和Linux虚拟机的双系统结合工具WSL 2

很长一段时间以来,双系统是我唯一的解决方案。我一直在用的是一台苹果笔记本,它几乎可以做任何事:开发、....
的头像 如意 发表于 07-07 10:47 219次 阅读
Windows和Linux虚拟机的双系统结合工具WSL 2

鸟哥的Linux私房菜基础篇第四版PDF电子书免费下载

现在的人们几乎无时无刻都会碰计算机!不管是桌面计算机(桌机)、笔记本电脑(笔电)、平板计算机、智能型....
发表于 07-07 08:00 58次 阅读
鸟哥的Linux私房菜基础篇第四版PDF电子书免费下载

Linux主要应用在那些领域

日前,国际超算大会发布了最新的全球TOP500超级计算机列表,其中最著名的是日本超级计算机“ Fug....
的头像 Wildesbeast 发表于 07-04 11:38 379次 阅读
Linux主要应用在那些领域

如何才能管理Linux中的用户活动

如果要管理Linux服务器,则需要准备好使用几个命令来检查用户活动-用户何时连接以及他们连接的频率、....
的头像 Wildesbeast 发表于 07-04 11:38 371次 阅读
如何才能管理Linux中的用户活动

Windows比Unix和Linux到底有什么优劣势

在大多数情况下,Windows甚至优于Unix / Linux。 对于某些专业技术人员来说,Unix....
的头像 Wildesbeast 发表于 07-04 11:13 287次 阅读
Windows比Unix和Linux到底有什么优劣势

详解Linux服务器的用户活动和命令

如果您在管理Linux服务器,最好准备好使用用于检查用户活动的多个命令——用户何时登录及登录频率、属....
的头像 如意 发表于 07-03 15:30 2095次 阅读
详解Linux服务器的用户活动和命令

一文了解虚拟机Linux系统的几大组件

本文概述了Linux系统的几大组件,描述了这些组件之间的关系。文章解释了术语,并描述看似很基础的细节....
的头像 如意 发表于 07-03 15:20 273次 阅读
一文了解虚拟机Linux系统的几大组件

Linux就该这么学【电子书分享】

公益分享,侵删 章节包括有: 第0章 咱们先来谈谈学习方法和红帽系统。 第1章 部署虚拟环境安装linux系统。 第2章 新手...
发表于 07-03 14:45 605次 阅读
Linux就该这么学【电子书分享】

Linux初学者(学习资料)

根据统计结果,越来越多的人开始转向Linux,而程序员中使用Linux的比重也越来越高。于是整理一些Linux的资料就显得尤为重要。...
发表于 07-03 14:17 1108次 阅读
Linux初学者(学习资料)

浅谈2020年六大主流编程语言

目前,计算机语言的总数总计达9000种。但是,其中只有50种编程语言是较为流行的。今天,小编就给大家....
的头像 如意 发表于 07-03 11:36 297次 阅读
浅谈2020年六大主流编程语言

单片机教程之C语言的程序设计说明

C51是在标准C的基础上,根据单片机存储器硬件结构及内部资源,扩展了相应的数据类型和变量,而C51在....
发表于 07-02 17:45 55次 阅读
单片机教程之C语言的程序设计说明

虚拟机:Linux生成自签名SSL证书

虚拟机:Linux生成自签名SSL证书
的头像 如意 发表于 07-02 16:34 366次 阅读
虚拟机:Linux生成自签名SSL证书

嵌入式必看 Linux内存管理工作原理

机器的内存是有限资源,而进程数量是无法确定的,如果在某些时候已经启动的进程占据了所有内存空间,此时就....
发表于 07-02 15:10 219次 阅读
嵌入式必看 Linux内存管理工作原理

c语言宏定义的使用方法

当宏作为常量使用时,C程序员习惯在名字中只使用大写字母。但是并没有如何将用于其他目的的宏大写的统一做....
发表于 07-02 15:04 95次 阅读
c语言宏定义的使用方法

如何创建AXI CDMA Linux用户空间示例应用

步骤 3:成功完成后,选择“验证设计 (Validate design)”以验证设计,并检查地址编辑....
的头像 FPGA开发圈 发表于 07-02 15:03 543次 阅读
如何创建AXI CDMA Linux用户空间示例应用

C语言二级指针的用法与原理

提到指针,我们都知道指针是用来存储一个变量的地址。所以,当我们定义了一个指向指针的指针的时候(poi....
发表于 07-02 14:52 87次 阅读
C语言二级指针的用法与原理

Linux下对于函数调用的工作原理

高地址的一部分空间会分配给内核,称为内核空间,剩下的内存空间给用户使用,称为用户空间。
发表于 07-02 14:46 73次 阅读
Linux下对于函数调用的工作原理

基于POSIX和Unix的多用户、支持多线程和多CPU的操作系统

Linux,全称GNU/Linux,是一套免费使用和自由传播的类UNIX操作系统,其内核由林纳斯·本....
的头像 ST视频 发表于 07-02 08:14 554次 观看
基于POSIX和Unix的多用户、支持多线程和多CPU的操作系统

HW资料华-为硬件(硬件开发C语言PCB设计天线通信)

链接:https://pan.baidu.com/s/1HFxkb4O1jAZvCEccWVcd4w 提取码:stns 回复可见提取码,资源需求可加q1051514663 ...
发表于 07-01 18:20 85次 阅读
HW资料华-为硬件(硬件开发C语言PCB设计天线通信)

梳理嵌入式Linux的一些知识

学单片机的朋友有些已经有一些Linux基础了,但也不乏有些朋友没用过Linux,甚至有些初学的读者朋....
的头像 玩转单片机 发表于 07-01 16:56 460次 阅读
梳理嵌入式Linux的一些知识

C语言和PHP,新手选择哪个比较好?

计科专业从事嵌入式开发多年,从现在的市场行情以及就业的机会上讲php的就业空间还是大于C语言,但并不....
的头像 如意 发表于 07-01 16:15 129次 阅读
C语言和PHP,新手选择哪个比较好?

宋宝华: Linux为什么一定要copy_from_user ?

传给dev_set_name()的根本是个stack区域的临时变量,是一个匆匆过客。而device的....
的头像 Linuxer 发表于 07-01 14:49 392次 阅读
宋宝华: Linux为什么一定要copy_from_user ?

Python的发展趋势,未来可期

首先,当前Python作为一门流行程度比较高的全场景编程语言,在IT互联网行业和一部分传统行业已经有....
的头像 如意 发表于 07-01 10:19 225次 阅读
Python的发展趋势,未来可期

怎么样才能在Keil C51环境下实现64位浮点库的设计

为解决Keil C51环境下无法进行64位双精度计算的问题,采用分部存储和分部计算的方法,设计了在K....
发表于 07-01 08:00 18次 阅读
怎么样才能在Keil C51环境下实现64位浮点库的设计

怎么样才能使用Keil C51开发大型嵌入式程序

 结合在8051系列单片机平台上的实际开发应用的经验,介绍用 Keil C在8051单片机上进行大型....
发表于 07-01 08:00 18次 阅读
怎么样才能使用Keil C51开发大型嵌入式程序

详谈选择排序算法的定义和过程

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是:第一次从待排序的数....
的头像 如意 发表于 06-30 17:06 90次 阅读
详谈选择排序算法的定义和过程

C语言模拟实现memmove函数

memmove用于从src拷贝count个字节到dest,如果目标区域和源区域有重叠的话,memmo....
的头像 如意 发表于 06-29 17:53 99次 阅读
C语言模拟实现memmove函数

远程电话控制器设计的C语言程序免费下载

本文档的主要内容详细介绍的是远程电话控制器设计的C语言程序免费下载。
发表于 06-29 17:20 36次 阅读
远程电话控制器设计的C语言程序免费下载

极度详细之Linux官方最新版本5.4移植教程

    我们在学习Linux内核开发的时候,拿到的Linux内核源码都是开发板厂家移植修改过的。我们按照厂家的使用手册,...
发表于 06-29 11:15 1975次 阅读
极度详细之Linux官方最新版本5.4移植教程

请问为什么要在Linux下学习C语言?

前些日子,由于看到同学安装了deepin,所以我也搞了一个玩玩,最后双系统折腾来折腾去,一直在纠结用什么发行版比较好,什么版本...
发表于 06-24 14:25 1728次 阅读
请问为什么要在Linux下学习C语言?

大学生如何学习FPGA初学者怎么学FPGA

无论是从自身的发展,还是从FPGA所拥有的巨大市场来讲,学习FPGA都是一个不错的选择,对大家来说FPGA技术可能还是新...
发表于 06-23 15:12 289次 阅读
大学生如何学习FPGA初学者怎么学FPGA

AM4379 AM437x ARM Cortex-A9 微处理器 (MPU)

TI AM437x高性能处理器基于ARM Cortex-A9内核。 这些处理器通过3D图形加速得到增强,可实现丰富的图形用户界面,还配备了协处理器,用于进行确定性实时处理(包括EtherCAT,PROFIBUS,EnDat等工业通信协议)。该器件支持高级操作系统(HLOS)。基于Linux的® 可从TI免费获取。其它HLOS可从TI的设计网络和生态系统合作伙伴处获取。 这些器件支持对采用较低性能ARM内核的系统升级,并提供更新外设,包括QSPI-NOR和LPDDR2等存储器选项。 这些处理器包含功能方框图中显示的子系统,并且后跟相应的“说明”中添加了更多信息说明。 处理器子系统基于ARM Cortex-A9内核,PowerVR SGX™图形加速器子系统提供3D图形加速功能以支持显示和高级用户界面。 可编程实时单元子系统和工业通信子系统(PRU-ICSS与ARM内核分离,允许单独操作和计时,以实现更高的效率和灵活性.PRU-ICSS支持更多外设接口和EtherCAT,PROFINET,EtherNet /IP,PROFIBUS,以太网Powerlink,Sercos,EnDat等...
发表于 09-25 11:51 144次 阅读
AM4379 AM437x ARM Cortex-A9 微处理器 (MPU)