內(nèi)存管理#3

Stack-Based Allocations

要從棧中進(jìn)行動態(tài)內(nèi)存分配,請使用alloca()系統(tǒng)調(diào)用:

#include <alloca.h>
void *alloca(size_t size);

不需要free。

int open_sysconf (const char *file, int flags, int mode) {
    const char *etc = SYSCONF_DIR; /* "/etc/" */
    char *name;
    name = alloca (strlen (etc) + strlen (file) + 1);
    strcpy (name, etc);
    strcat (name, file);
    return open (name, flags, mode);
}
int open_sysconf (const char *file, int flags, int mode) {
    const char *etc = SYSCONF_DIR; /* "/etc/" */
    char *name;
    int fd;
    name = malloc (strlen (etc) + strlen (file) + 1);
    if (!name) {
        perror ("malloc");
        return ?1;
     }
    strcpy (name, etc);    
    strcat (name, file);
    fd = open (name, flags, mode);
    free (name);
    return fd;
 }

如果您的程序必須保持可移植性,則應(yīng)該避免alloca()。
在Linux上,alloca()是一個非常有用和利用不足的工具。
不要這么使用就行:

/* DO NOT DO THIS! */
ret = foo (x, alloca (10));

Duplicating String on the Stack

/* we want to duplicate 'song' */
char *dup;
dup = alloca (strlen (song) + 1);
strcpy (dup, song);
/* manipulate 'dup'... */
return; /* 'dup' is automatically freed */
#define _GNU_SOURCE
#include <string.h>
char * strdupa (const char *s);
char * strndupa (const char *s, size_t n);

他們是一樣的。

Variable-Length Arrays(VLA)

//典型用法
for (i = 0; i < n; ++i) {
    char foo[i + 1];
    /* use 'foo'... */
}

如果我們使用的是alloca()而不是VLA,則在函數(shù)返回之前不會釋放內(nèi)存。使用VLA確保在循環(huán)的每一次迭代中釋放內(nèi)存。因此,使用VLA消耗最多n個字節(jié),而Alloca()則消耗n*(n+1)/2個字節(jié)。

int open_sysconf (const char *file, int flags, int mode) {
    const char *etc; = SYSCONF_DIR; /* "/etc/" */
    char name[strlen (etc) + strlen (file) + 1];
    strcpy (name, etc);
    strcat (name, file);
    return open (name, flags, mode);
}

Choosing a Memory Allocation Mechansim

Approaches to memory allocation in Linux

Manipulating Memory

Setting Bytes

#include <string.h>
void * memset (void *s, int c, size_t n);

對memset()的調(diào)用將從s開始的n個字節(jié)設(shè)置為字節(jié)c并返回s。

#include <strings.h>
void bzero (void *s, size_t n);

請注意,bzero()(以及其他b接口)需要頭<strings.h>而不是<string.h>。

Comparing Bytes

#include <string.h>
int memcmp (const void *s1, const void *s2, size_t n);

s1 = s2: return 0
s1 < s2: return <0
s1 > s2: return >0

#include <strings.h>
int bcmp (const void *s1, const void *s2, size_t n);

0的話相等,否則不相等。
比較兩個結(jié)構(gòu)體的話用memcmp是不安全的, 如下:

/* are two dinghies identical? (BROKEN) */
int compare_dinghies (struct dinghy *a, struct dinghy *b) {
    return memcmp (a, b, sizeof (struct dinghy));
}

如果要比較結(jié)構(gòu)體的話,我們應(yīng)該比較結(jié)構(gòu)體中的每個元素。

/* are two dinghies identical? */
int compare_dinghies (struct dinghy *a, struct dinghy *b) {
    int ret;
    if (a->nr_oars < b->nr_oars)
        return ?1;
    if (a->nr_oars > b->nr_oars)
        return 1;
    ret = strcmp (a->boat_name, b->boat_name);
    if (ret)
        return ret;
    /* and so on, for each member... */
}

Moving Bytes

#include <string.h>
void * memmove (void *dst, const void *src, size_t n);
#include <strings.h>
void bcopy (const void *src, void *dst, size_t n);

上面這兩個是是支持overlapping(dst的一部分在src中)
但是下面這個是不支持overlapping的,更塊:

#include <string.h>
void * memcpy (void *dst, const void *src, size_t n);

還有一種:

#include <string.h>
void * memccpy (void *dst, const void *src, int c, size_t n);

與memcpy()相同,只是如果函數(shù)在src的前n個字節(jié)內(nèi)找到字節(jié)c,則停止復(fù)制。調(diào)用返回在c之后指向dst中下一個字節(jié)的指針,如果沒有找到c,則返回NULL。 .
最后,您可以使用mempcpy()來逐步遍歷內(nèi)存:

#define GNU_Source
#include<string.h>
void*mempcpy(void*dst,const void*src,size_tn);

mempcpy()函數(shù)執(zhí)行與memcpy()相同的操作。只不過它返回一個指針,指向上次復(fù)制的字節(jié)之后的下一個字節(jié)。如果要將一組數(shù)據(jù)復(fù)制到連續(xù)的內(nèi)存位置,這是很有用的。但它并不是一個改進(jìn)者 因為返回值僅僅是dst+n,這個函數(shù)是特定于GNU的。

Searching Bytes

#include <string.h>
void * memchr (const void *s, int c, size_t n);

函數(shù)對s指向的n個字節(jié)的內(nèi)存進(jìn)行c字符掃描,調(diào)用返回第一個與c匹配的字節(jié)的指針,如果未找到c,則返回NULL。

#define _GNU_SOURCE
#include <string.h>
void * memrchr (const void *s, int c, size_t n);

與memrchr()與memchr()是一樣的,但是是逆序查找。

#define _GNU_SOURCE
#include <string.h>
void * memmem (const void *haystack,
                            size_t haystacklen,
                            const void *needle,
                            size_t needlelen);

在haystack中查找needle,找到返回指針,找不到返回NULL。

Frobnicating Bytes

#define _GNU_SOURCE
#include <string.h>
void * memfrob (void *s, size_t n);

對memfrob()的調(diào)用掩蓋了從s開始的內(nèi)存的前n個字節(jié)
再次調(diào)用將返回原來的值。

Locking Memory

Locking Part of an Address Space

#include <sys/mman.h>
int mlock (const void *addr, size_t len);

成功返回0, 失敗返回-1,并設(shè)置errno。

int ret;
/* lock 'secret' in memory */
ret = mlock (secret, strlen (secret));
if (ret)
    perror ("mlock");

Locking All of an Address Space

#include <sys/mman.h>
int mlockall (int flags);

flags參數(shù)列表:

  • MCL_Current 將當(dāng)前映射的所有頁面(堆棧、數(shù)據(jù)段、映射文件等)鎖定到進(jìn)程的地址空間中。
  • MCL_WORVERY,確保將來映射到地址空間的所有頁也被鎖定在內(nèi)存中。
    成功返回0,失敗返回-1,并設(shè)置errno。

Unlocking Memory

#include <sys/mman.h>
int munlock (const void *addr, size_t len);
int munlockall (void);

成功返回0, 失敗返回-1,并且設(shè)置errno。

Locking Limits

擁有CAP_IPC_LOCK功能的進(jìn)程可能會將任意數(shù)量的頁面鎖定到內(nèi)存中。沒有此功能的進(jìn)程可能只鎖定RLIMIT_MEMLOCK字節(jié)。
默認(rèn)情況下,此資源限制為32KB-足夠大,可鎖定內(nèi)存中的一個或兩個密碼,但不足以對系統(tǒng)性能產(chǎn)生不利影響。

Is a Page in Physical Mmeory

為了調(diào)試和診斷目的,Linux提供了mincore()函數(shù),該函數(shù)可用于確定給定范圍的內(nèi)存是在物理內(nèi)存中還是交換到磁盤中:

#include <unistd.h>
#include <sys/mman.h>
int mincore (void *start, size_t length, unsigned char *vec);

調(diào)用通過vec返回向量,并描述從start(必須是頁面對齊)開始的頁面和length字節(jié)的擴(kuò)展(不需要頁面對齊)。vec中的每個字節(jié) 從描述第一頁的第一個字節(jié)開始,然后線性向前移動。
vec 必須大于(length ? 1 + page size) / page size
成功返回0, 失敗返回-1,并且設(shè)置errno。

Opportunistic Allocation

Overcommitting and OOM

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容