Unified entry to C-base memory and unified entry to c-base memory

Source: Internet
Author: User
Tags byte sizes

Unified entry to C-base memory and unified entry to c-base memory

Introduction-malloc quotes

In the C standard, only the malloc, calloc, and realloc memory recovery ports are provided for the heap memory.

Struct person * per = malloc (sizoef (struct person); if (NULL = ptr) {fprintf (stderr, "malloc struct person is error! "); // To do error thing...} // process the normal logic... // reclaim free (per );

In particular, if NULL = ptr operations are cumbersome. It is a bit uncomfortable to build a set of interfaces and try a simple way.

The new upper-layer language is used for reference. It is simple and crude, and fails to crash directly. The general idea is

Struct header * ptr = malloc (sz + sizeof (struct header); // check the memory allocation result if (NULL = ptr) {fprintf (stderr, "_ header_get> % s: % d: % s <alloc error not enough memory start fail! \ N ", file, line, func); exit (EXIT_FAILURE );}

Exit is used to end the allocation of NULL. After all, the computer's first-level memory is insufficient, and all operations are close to the "undefined edge" for the software layer"

References:Cloud Big skynet2 demoHttps://github.com/cloudwu/skynet2/tree/master/skynet-src

 

Preface-define an interface for unified processing

The processing logic is very simple. It mainly provides an entry to the memory application, like new, to return the initial memory and crash directly if the memory is insufficient. First, the interface design is as follows:

Scalloc. h

# Ifndef _ H_SIMPLEC_SCALLOC # define _ H_SIMPLEC_SCALLOC # include <stdlib. h> // release the memory requested by sm_malloc _ and sm_realloc _. void sm_free _ (void * ptr, const char * file, int line, const char * func) must be used together ); // return the requested clean memory void * sm_malloc _ (size_t sz, const char * file, int line, const char * func); // return the re-applied memory, only void * sm_realloc _ (void * ptr, size_t sz, const char * file, int line, const char * func) can be used with sm_malloc ); /** release applied memory * ptr: Applied memory */# define sm_free (ptr) sm_free _ (ptr, _ FILE __, _ LINE __, _ func _)/** returns the applied memory and fills in '\ 0' * sz: the length of the applied memory */# define sm_malloc (sz) sm_malloc _ (sz, _ FILE __, _ LINE __, _ func _)/** returns the applied num * sz length memory, fill in '\ 0' * num: applied quantity * sz: Applied Memory Length */# define sm_calloc (num, sz) sm_malloc _ (num * sz, _ FILE __, _ LINE __, _ func _)/** return the re-applied memory * ptr: Applied memory * sz: length of the applied memory */# define sm_realloc (ptr, sz) sm_realloc _ (ptr, sz, _ FILE __, _ LINE __, _ func __) // define the global memory usage macro and replace the original malloc series functions # ifndef _ SIMPLEC_SCALLOC_CLOSE # define free sm_free # define malloc sm_malloc # define calloc sm_calloc
# Define realloc sm_realloc # endif //! _ H_SIMPLEC_SCALLOC

The above sm_malloc sm_calloc sm_realloc sm_free macro has a few more compilation macro parameters than the previous four functions to facilitate future troubleshooting.

The _ SIMPLEC_SCALLOC_CLOSE header file indicates whether to replace the old memory-related operations.
Here, calloc feels like a design failure.
#include <stdlib.h>void * calloc(size_t nmemb, size_t size);calloc() allocates memory for an array of nmemb elements of size bytes each and returns a pointer to the allocated memory.  The memory is set to zero.

The above description is equivalent to calloc (nmemb, size) <=> malloc (nmemb * size); memset (ptr, 0, nmemb * size); it feels silly.

Let me take a look at the source code implementation in the malloc. c file.

Void * _ libc_calloc (size_t n, size_t elem_size) {mstate av; mchunkptr oldtop, p; bytes, sz, csz, oldtopsize; void * mem; unsigned long clearsize; unsigned long nclears; INTERNAL_SIZE_T * d;/* size_t is unsigned so the behavior on overflow is defined. */bytes = n * elem_size; # define HALF_INTERNAL_SIZE_T \ (INTERNAL_SIZE_T) 1) <(8 * sizeof (INTERNAL_SIZE_T)/2) if (_ builtin_ex Pect (n | elem_size)> = HALF_INTERNAL_SIZE_T, 0) {if (elem_size! = 0 & bytes/elem_size! = N) {_ set_errno (ENOMEM); return 0 ;}} void * (* hook) (size_t, const void *) = atomic_forced_read (_ malloc_hook ); if (_ builtin_exact CT (hook! = NULL, 0) {sz = bytes; mem = (* hook) (sz, RETURN_ADDRESS (0); if (mem = 0) return 0; return memset (mem, 0, sz);} sz = bytes; arena_get (av, sz); if (av) {/* Check if we hand out the top chunk, in which case there may be no need to clear. */# if MORECORE_CLEARS oldtop = top (av); oldtopsize = chunksize (top (av); # if MORECORE_CLEARS <2/* Only newly allocated memory is guaranteed to be clear Ed. */if (av ==& main_arena & oldtopsize <mp _. sbrk_base + av-> max_system_mem-(char *) oldtop) oldtopsize = (mp _. sbrk_base + av-> max_system_mem-(char *) oldtop); # endif if (av! = & Main_arena) {heap_info * heap = heap_for_ptr (oldtop); if (oldtopsize <(char *) heap + heap-> mprotect_size-(char *) oldtop) oldtopsize = (char *) heap + heap-> mprotect_size-(char *) oldtop ;}# endif} else {/* No usable arenas. */oldtop = 0; oldtopsize = 0;} mem = _ int_malloc (av, sz); assert (! Mem | chunk_is_mmapped (mem2chunk (mem) | av = arena_for_chunk (mem2chunk (mem); if (mem = 0 & av! = NULL) {LIBC_PROBE (memory_calloc_retry, 1, sz); av = arena_get_retry (av, sz); mem = _ int_malloc (av, sz);} if (av! = NULL) (void) mutex_unlock (& av-> mutex);/* Allocation failed even after a retry. */if (mem = 0) return 0; p = mem2chunk (mem);/* Two optional cases in which clearing not necessary */if (chunk_is_mmapped (p )) {if (_ builtin_expect (perturb_byte, 0) return memset (mem, 0, sz); return mem;} csz = chunksize (p ); # if MORECORE_CLEARS if (perturb_byte = 0 & (p = oldtop & csz> oldtopsize )) {/* clear only the bytes from non-freshly-sbrked memory */csz = oldtopsize;} # endif/* Unroll clear of <= 36 bytes (72 if 8 byte sizes ). we know that contents have an odd number of INTERNAL_SIZE_T-sized words; minimally 3. */d = (INTERNAL_SIZE_T *) mem; clearsize = csz-SIZE_SZ; nclears = clearsize/sizeof (INTERNAL_SIZE_T); assert (nclears> = 3); if (nclears> 9) return memset (d, 0, clearsize); else {* (d + 0) = 0; * (d + 1) = 0; * (d + 2) = 0; if (nclears> 4) {* (d + 3) = 0; * (d + 4) = 0; if (nclears> 6) {* (d + 5) = 0; * (d + 6) = 0; if (nclears> 8) {* (d + 7) = 0; * (d + 8) = 0 ;}}} return mem ;}View Code

This is complicated. Let's extract the following lines to help you understand.

 ...  /* size_t is unsigned so the behavior on overflow is defined.  */  bytes = n * elem_size;...      return memset (mem, 0, sz);...  if (av != NULL)    (void) mutex_unlock (&av->mutex);...

The implementation is very complicated. It mainly focuses on performance considerations and resets the idea of a memory application. The three points extracted above can indicate that malloc can replace calloc in terms of functionality.

Finally, it indicates that the glibc (gcc) source code is thread-safe. Later, we will analyze the specific implementation of the above interface and test a demo.

 

Body-start implementation and run demo

First, let's look at the specific implementation,Scalloc. c

# Include <stdio. h> # include <stdlib. h> # include <string. h> // identifies the enumeration typedef enum {HF_Alloc, HF_Free} header_e; // The [16-24] bytes of memory consumed each time, used to Record Memory application situations struct header {header_e flag; // int line used by the current memory; // applied file line const char * file; // apply for the file name const char * func; // apply for the function name}; // internally used malloc, the returned memory will use '\ 0' to initialize void * sm_malloc _ (size_t sz, const char * file, int line, const char * func) {struct header * ptr = malloc (Sz + sizeof (struct header); // check the memory allocation result if (NULL = ptr) {fprintf (stderr, "_ header_get> % s: % d: % s <alloc error not enough memory start fail! \ N ", file, line, func); exit (EXIT_FAILURE);} ptr-> flag = HF_Alloc; ptr-> line = line; ptr-> file = file; ptr-> func = func; memset (++ ptr, 0, sz); return ptr;} // get the initial part of the applied memory, check static struct header * _ header_get (void * ptr, const char * file, int line, const char * func) {struct header * node = (struct header *) ptr-1; // if (HF_Alloc! = Node-> flag) {// exception, memory is released multiple times, And fprintf (stderr, "_ header_get free invalid memony flag % d by> % s: % d: % s <\ n ", node-> flag, file, line, func); exit (EXIT_FAILURE);} return node ;} // reallocvoid * sm_realloc _ (void * ptr, size_t sz, const char * file, int line, const char * func) used internally {struct header * node, * buf; if (NULL = ptr) return sm_malloc _ (sz, file, line, func); // reasonable memory division node = _ header _ Get (ptr, file, line, func); node-> flag = HF_Free; // construct the returned memory information buf = realloc (node, sz + sizeof (struct header )); buf-> flag = HF_Alloc; buf-> line = line; buf-> file = file; buf-> func = func; return buf + 1 ;} // internally used free. Each release prints the log information void sm_free _ (void * ptr, const char * file, int line, const char * func) {if (NULL! = Ptr) {// get the memory address and identify it to start releasing struct header * node = _ header_get (ptr, file, line, func); node-> flag = HF_Free; free (node );}}

The memory header is inserted around 1.

// [16-24] bytes of memory consumed each time, used to record the memory Application Status struct header {header_e flag; // int line used by the current memory; // apply for the file line const char * file; // apply for the file name const char * func; // apply for the function name };

When we get the header check for 2 in malloc and _ header_get, we directly exit.

The idea is very clear. If this code runs on a 64-bit machine and runs on an online server, 1000 million malloc objects will be created.

100000*(4 + 4 + 8 + 8) B/1024/1024 = 2.288818359375 MB memory loss. There is also a memory fetch check performance loss.

These are acceptable. In special cases, you can print the information to determine the location of memory call errors.

Here we use enumeration to distinguish between convenience and macros.

// Identify the enumerated typedef enum {HF_Alloc, HF_Free} header_e;

In fact, macros and enumerations are basically the same in C. Only special specifications can be manually added to stipulate the differences between them. macros are used too much, and the complexity will increase. a double-edged sword.

The following is a test demo.Main. c

# Include <stdio. h> # include "scalloc. h "/** test memory management and obtain the memory registration information */int main (int argc, char * argv []) {int * piyo = malloc (10 ); free (piyo); puts ("start testing... "); // test free (piyo); getchar (); return 0 ;}

Demo result

The basic ideas have been introduced here. The main core isSteal a column.

 

Postscript -~ ○ ~

Errors are inevitable. If you have any problems, patch them and fix them. You are welcome to use this idea in your own project.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.