Difference between brk and sbrk - c

I new to this can any one tell the exact difference between brk and sbrk with a brief example ? is there any efficiency factor to choose from any of the two?
malloc and new internally call brk or sbrk.

int brk(void *addr);
brk() sets the end of the data segment to the value specified by
addr, when that value is reasonable, the system has enough memory,
and the process does not exceed its maximum data size.
On success, brk() returns zero. On error, -1 is returned, and errno
is set to ENOMEM.
void *sbrk(intptr_t increment);
sbrk() increments the program's data space by increment bytes.
Calling sbrk() with an increment of 0 can be used to find the current
location of the program break.
On success, sbrk() returns the previous program break. (If the break
was increased, then this value is a pointer to the start of the newly
allocated memory). On error, (void *) -1 is returned, and errno is
set to ENOMEM.
From linux manual page

brk sets the upper limit of the data segment, sbrk increments it. In ancient Unixes malloc/free used sbrk. On modern ones things could be very different, for example, OSX does not use brk/sbrk to manage heap allocations but mmap, brk/sbrk exist but are just emulation in a small segment of memory. This is almost the same on Linux (source code of mention the history of transition from brk/sbrk to mmap).

Related

Do I need to reset protection before calling free

I allocated some large chunks of memory through malloc and aligned_alloc, and then I setup a fence to a region inside the memory with size of one page size, using mprotect:
void *buf = malloc(128 * PAGE_SIZE);
int ret = mprotect(buf, PAGE_SIZE, PROT_NONE);
Now I'm done with the memory and is calling free(buf); to release it, my questions is do I need to reset mprotect before calling free, like this:
ret = mprotect(buf, PAGE_SIZE, PROT_READ|PROT_WRITE);
free(buf);
Or should I just do free? I read that glibc will sometimes reuse some of the previously allocated memory, so if this region of memory is returned to later malloc, will accessing it cause problems(since it's PROT_NONE)?
On top of #doron 's answer, I digged a little more because I was specifically working on Linux. According posix standard of mprotect:
The behavior of this function is unspecified if the mapping was not established by a call to mmap().
While on Linux, it is a bit different:
On Linux it is always permissible to call mprotect() on any address in a process's address space (except for the kernel vsyscall area). In particular it can be used to change existing code mappings to be writable.
That means it is indeed allowed to call mprotect on malloc'ed memory regions, but free will not reset it as there is no way to know the old protection flags, so I have to reset the flags before calling free.
In fact, this is exactly the problem I met, when the program runs for a while, it randomly crashes in malloc, that is because malloc is writing it's housekeeping data into the previously allocated memory and that memory was set to PROT_NONE by an earlier mprotect, after I set the memory to writable before calling free, the program never crash again.
That's being said, this is only on Linux, to write portable program, one should use mprotect only on mmap'ed memory.
Malloc allocated from the heap does not necessarily request the memory from the system. Likewise free does not necessarily return the memory to the system.
For what you are doing you should use mmap munmap which always goes to the system. If you use mfree there is no need to call mprotect beforehand.

Use of sbrk system call in Unix

What is the use of sbrk system call in Unix and how is it to be used in C? Most of them say that malloc uses the sbrk system call to allocate memory. So, how does the sbrk system call allocate memory? What are the arguments and return type of sbrk?
sbrk() takes a positive integer as an argument. It increases the size of the current process's heap by that number of bytes, then returns a void * to the new end of the heap.
However, the information you're reading is outdated. To quote the Mac OS X manual page for this system call:
The brk and sbrk functions are historical curiosities left over from earlier days before the advent of virtual memory management.
Modern systems do not use sbrk() for memory management, as it is incredibly limited. In particular, it cannot manage a heap which is not contiguous in memory! Instead, they use the mmap() system call to map new ranges of memory as needed.
Function definition: void *sbrk(intptr_t increment);
sbrk() increments the program's data space by increment bytes. Calling sbrk() with an increment of 0 can be used to find the current location of the program break.
From http://linux.die.net/man/2/sbrk

Is calloc exactly the same as malloc + memset?

In linux, is calloc exactly the same as malloc + memset or does this depend on the exact linux/kernel version?
I am particularly interested in the question of whether you can calloc more RAM than you physically have (as you can certainly malloc more RAM than you physically have, you just can't write to it). In other words, does calloc always actually write to the memory you have been allocated as the specs suggest it should.
Of course, that depends on the implementation, but on a modern day Linux, you probably can. Easiest way is to try it, but I'm saying this based on the following logic.
You can malloc more than the memory you have (physical + virtual) because the kernel delays allocation of your memory until you actually use it. I believe that's to increase the chances of your program not failing due to memory limits, but that's not the question.
calloc is the same as malloc but zero initializes the memory. When you ask Linux for a page of memory, Linux already zero-initializes it. So if calloc can tell that the memory it asked for was just requested from the kernel, it doesn't actually have to zero initialize it! Since it doesn't, there is no access to that memory and therefore it should be able to request more memory than there actually is.
As mentioned in the comments this answer provides a very good explanation.
Whether calloc needs to write to the memory depends on whether it got the allocation from heap pages that are already assigned to the process, or it had to request more memory be assigned to the process by the kernel (using a system call such as sbrk() or mmap()). When the kernel assigns new memory to a process, it always zeroes it first (typically using a VM optimization, so it doesn't actually have to write to the page). But if it's reusing memory that was assigned previously, it has to use memset() to zero it.
It is not mentioned in the cited duplicate or here. Linux uses virtual memory and can allocate more memory that physically available in the system. A naive implementation of calloc() that simply does a malloc() plus memset() in user space will touch every page.
As Linux typically allocates in 4k chunks, all of the calloc() blocks are the same and initially read as zero. That is the same 4k chunk of memory can be mapped read only and the entire calloc() space in only taking up approximately size/4k * pointer_size + 4k. As the program writes to the calloc() space, a page fault happens and Linux will allocate a new page (4k) and resume the program.
This is called copy-on-write or COW for short. malloc() will generally behave the same way. For small sizes, the 'C' library will use binning and share 4k pages with other small sized allocation.
So, there are typically two layers involved.
Linux kernel's process memory management.
glibc heap management.
If the memory size requested is large and requires new memory allocated to the process, then most of the above applies (via Linux's process memory management). However, if the memory requested is small, then it will be like a malloc() plus memset(). In the large allocation size, the memset() is damaging as it touches the memory and the kernel thinks it needs a new page to allocate.
You can't malloc(3) more ram than the kernel gives the process doing the malloc(3)-ing. malloc(3) returns NULL if you can't allocate the amount of memory you want to allocate. In addition, malloc(3) and memset(3) are defined by your c library (libc.so) and not your kernel. The Linux kernel defines mmap(2) and other low-level memory allocation functions, not the *alloc(3) family (excluding kalloc()).

How to set memory use limit when writing C program and what happens if once it exceeds this limit?

I am writing a C program on linux and I wonder:
How to limit the total memory my c program consumes?
If I set a memory limit to my c program, say 32M, what happens if it requires much more memory than 32M?
You should use the setrlimit system call, with the RLIMIT_DATA and RLIMIT_STACK resources to limit the heap and stack sizes respectively. It is tempting to use RLIMIT_AS or RLIMIT_RSS but you will discover that they do not work reliably on many old Linux kernels, and I see no sign on the kernel mailing list that the problems have been resolved in the latest kernels. One problem relates to how mmap'd memory is counted or rather not counted toward the limit totals. Since glibc malloc uses mmap for large allocations, even programs that don't call mmap directly can exceed the limits.
If you exceed the RLIMIT_STACK limit (call stack too deep, or allocate too many variables on the stack), your program will receive a SIGSEGV. If you try to extend the data segment past the RLIMIT_DATA limit (brk, sbrk or some intermediary like malloc), the attempt will fail. brk or sbrk will return < 0 and malloc will return a null pointer.
See ulimit command in your system.
From the man page:
-v The maximum amount of virtual memory available to the process
If your program is well-written it should take of the cases where a dynamic memory allocation fails: check the return value of malloc, calloc and realloc functions.
On Linux, within your C program, use setrlimit() to set your program's execution environment limits. When you run out of memory, for instance, then calls to malloc() will return NULL etc.
#include <sys/resource.h>
{ struct rlimit rl = {32000, 32000}; setrlimit(RLIMIT_AS, &rl); }

Why malloc+memset is slower than calloc?

It's known that calloc is different than malloc in that it initializes the memory allocated. With calloc, the memory is set to zero. With malloc, the memory is not cleared.
So in everyday work, I regard calloc as malloc+memset.
Incidentally, for fun, I wrote the following code for a benchmark.
The result is confusing.
Code 1:
#include<stdio.h>
#include<stdlib.h>
#define BLOCK_SIZE 1024*1024*256
int main()
{
int i=0;
char *buf[10];
while(i<10)
{
buf[i] = (char*)calloc(1,BLOCK_SIZE);
i++;
}
}
Output of Code 1:
time ./a.out
**real 0m0.287s**
user 0m0.095s
sys 0m0.192s
Code 2:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define BLOCK_SIZE 1024*1024*256
int main()
{
int i=0;
char *buf[10];
while(i<10)
{
buf[i] = (char*)malloc(BLOCK_SIZE);
memset(buf[i],'\0',BLOCK_SIZE);
i++;
}
}
Output of Code 2:
time ./a.out
**real 0m2.693s**
user 0m0.973s
sys 0m1.721s
Replacing memset with bzero(buf[i],BLOCK_SIZE) in Code 2 produces the same result.
My question is: Why is malloc+memset so much slower than calloc? How can calloc do that?
The short version: Always use calloc() instead of malloc()+memset(). In most cases, they will be the same. In some cases, calloc() will do less work because it can skip memset() entirely. In other cases, calloc() can even cheat and not allocate any memory! However, malloc()+memset() will always do the full amount of work.
Understanding this requires a short tour of the memory system.
Quick tour of memory
There are four main parts here: your program, the standard library, the kernel, and the page tables. You already know your program, so...
Memory allocators like malloc() and calloc() are mostly there to take small allocations (anything from 1 byte to 100s of KB) and group them into larger pools of memory. For example, if you allocate 16 bytes, malloc() will first try to get 16 bytes out of one of its pools, and then ask for more memory from the kernel when the pool runs dry. However, since the program you're asking about is allocating for a large amount of memory at once, malloc() and calloc() will just ask for that memory directly from the kernel. The threshold for this behavior depends on your system, but I've seen 1 MiB used as the threshold.
The kernel is responsible for allocating actual RAM to each process and making sure that processes don't interfere with the memory of other processes. This is called memory protection, it has been dirt common since the 1990s, and it's the reason why one program can crash without bringing down the whole system. So when a program needs more memory, it can't just take the memory, but instead it asks for the memory from the kernel using a system call like mmap() or sbrk(). The kernel will give RAM to each process by modifying the page table.
The page table maps memory addresses to actual physical RAM. Your process's addresses, 0x00000000 to 0xFFFFFFFF on a 32-bit system, aren't real memory but instead are addresses in virtual memory. The processor divides these addresses into 4 KiB pages, and each page can be assigned to a different piece of physical RAM by modifying the page table. Only the kernel is permitted to modify the page table.
How it doesn't work
Here's how allocating 256 MiB does not work:
Your process calls calloc() and asks for 256 MiB.
The standard library calls mmap() and asks for 256 MiB.
The kernel finds 256 MiB of unused RAM and gives it to your process by modifying the page table.
The standard library zeroes the RAM with memset() and returns from calloc().
Your process eventually exits, and the kernel reclaims the RAM so it can be used by another process.
How it actually works
The above process would work, but it just doesn't happen this way. There are three major differences.
When your process gets new memory from the kernel, that memory was probably used by some other process previously. This is a security risk. What if that memory has passwords, encryption keys, or secret salsa recipes? To keep sensitive data from leaking, the kernel always scrubs memory before giving it to a process. We might as well scrub the memory by zeroing it, and if new memory is zeroed we might as well make it a guarantee, so mmap() guarantees that the new memory it returns is always zeroed.
There are a lot of programs out there that allocate memory but don't use the memory right away. Sometimes memory is allocated but never used. The kernel knows this and is lazy. When you allocate new memory, the kernel doesn't touch the page table at all and doesn't give any RAM to your process. Instead, it finds some address space in your process, makes a note of what is supposed to go there, and makes a promise that it will put RAM there if your program ever actually uses it. When your program tries to read or write from those addresses, the processor triggers a page fault and the kernel steps in to assign RAM to those addresses and resumes your program. If you never use the memory, the page fault never happens and your program never actually gets the RAM.
Some processes allocate memory and then read from it without modifying it. This means that a lot of pages in memory across different processes may be filled with pristine zeroes returned from mmap(). Since these pages are all the same, the kernel makes all these virtual addresses point to a single shared 4 KiB page of memory filled with zeroes. If you try to write to that memory, the processor triggers another page fault and the kernel steps in to give you a fresh page of zeroes that isn't shared with any other programs.
The final process looks more like this:
Your process calls calloc() and asks for 256 MiB.
The standard library calls mmap() and asks for 256 MiB.
The kernel finds 256 MiB of unused address space, makes a note about what that address space is now used for, and returns.
The standard library knows that the result of mmap() is always filled with zeroes (or will be once it actually gets some RAM), so it doesn't touch the memory, so there is no page fault, and the RAM is never given to your process.
Your process eventually exits, and the kernel doesn't need to reclaim the RAM because it was never allocated in the first place.
If you use memset() to zero the page, memset() will trigger the page fault, cause the RAM to get allocated, and then zero it even though it is already filled with zeroes. This is an enormous amount of extra work, and explains why calloc() is faster than malloc() and memset(). If you end up using the memory anyway, calloc() is still faster than malloc() and memset() but the difference is not quite so ridiculous.
This doesn't always work
Not all systems have paged virtual memory, so not all systems can use these optimizations. This applies to very old processors like the 80286 as well as embedded processors which are just too small for a sophisticated memory management unit.
This also won't always work with smaller allocations. With smaller allocations, calloc() gets memory from a shared pool instead of going directly to the kernel. In general, the shared pool might have junk data stored in it from old memory that was used and freed with free(), so calloc() could take that memory and call memset() to clear it out. Common implementations will track which parts of the shared pool are pristine and still filled with zeroes, but not all implementations do this.
Dispelling some wrong answers
Depending on the operating system, the kernel may or may not zero memory in its free time, in case you need to get some zeroed memory later. Linux does not zero memory ahead of time, and Dragonfly BSD recently also removed this feature from their kernel. Some other kernels do zero memory ahead of time, however. Zeroing pages during idle isn't enough to explain the large performance differences anyway.
The calloc() function is not using some special memory-aligned version of memset(), and that wouldn't make it much faster anyway. Most memset() implementations for modern processors look kind of like this:
function memset(dest, c, len)
// one byte at a time, until the dest is aligned...
while (len > 0 && ((unsigned int)dest & 15))
*dest++ = c
len -= 1
// now write big chunks at a time (processor-specific)...
// block size might not be 16, it's just pseudocode
while (len >= 16)
// some optimized vector code goes here
// glibc uses SSE2 when available
dest += 16
len -= 16
// the end is not aligned, so one byte at a time
while (len > 0)
*dest++ = c
len -= 1
So you can see, memset() is very fast and you're not really going to get anything better for large blocks of memory.
The fact that memset() is zeroing memory that is already zeroed does mean that the memory gets zeroed twice, but that only explains a 2x performance difference. The performance difference here is much larger (I measured more than three orders of magnitude on my system between malloc()+memset() and calloc()).
Party trick
Instead of looping 10 times, write a program that allocates memory until malloc() or calloc() returns NULL.
What happens if you add memset()?
Because on many systems, in spare processing time, the OS goes around setting free memory to zero on its own and marking it safe for calloc(), so when you call calloc(), it may already have free, zeroed memory to give you.
On some platforms in some modes malloc initialises the memory to some typically non-zero value before returning it, so the second version could well initialize the memory twice

Resources