Lecture 2 - Review of Systems Programming, Malloc
Just note that the deadline for the class shouldn't be taken lightly. Do it early plz thx. But you are given 3 free days to use as a late day to submit late. There's no penalty for using them, and no penalty for saving them to the end of the quarter.
Quick Review of pipe
All a pipe
is two file descriptors connected by a buffer. For lab 01 we are trying to do:
ls | sort -r
Manually in main()
via two children who have to communicate with each other (three processes). ls
is the upstream one, sort -r
downstream. The OS will synchronize them.
Say sort -r
tries to read from the pipe. The OS will say 'wait' until ls
has written something to it. ls
will drain the buffer, then wait. sort -r
will add more, ...
When the pipe goes empty, there's no one who can write to it. sort -r
will get EOF
and goes away. But the parent needs to know when that process is done to orchestrate everything.
Doing it Practically
Say we start out with our parent process:
When you fork
, the file descriptors are copied to the child. So if you pipe
and then fork, both have synchronized file descriptors.
But we need to use dup2
system call to make the input stdin
and output stdin
for our certain processes:
- In the child 1 that does the
ls
, you want to setstdin
to the input of the pipe (whatever thatfd
is) - In the child 2 that does the
sort -r
, you want to setstdout
to the output of the pipe
But the problem is we need to close the extra fd
's. Namely the child 2's pipe-out, and child 1's pipe in, and the paren't pipe in and out. We should get:
DON'T FORGET to close the fd
's for the parent!
malloc
!
Remember our buddy malloc()
! Here's the definitions:
void *malloc(size_t size);
void *calloc(size_t num, size_t size); // will just call malloc after multiplying the size and num params
void *free(void* ptr);
void *realloc(void *ptr, size_t size); // good for copying data. Frees ptr tho!
Usually size_t
is a multiple of 16 so that it works with the cache-line of the system.
You're going to want to setup a linked list to organize the address ranges to see if it's alloced or not.
printf()
Be wary that printf()
will use malloc
, so if you're using it you cannot use it to debug. Instead, at pn_cs453/Given/Asgn1
there's a debugger to help you. Other than that just use gdb
.
Recall our format of memory is:
Here if you use sbrk
then it moves the top pointer of the data segment. Namely, if you call it then you 'magically' create that space. However, if you want to move sbrk
2 bytes that's a bad idea! Instead you want to increment it in various page lengths, just some smallest hunk.
The space we increment is brand new memory, but we need to have the data structure in that page. What will help is to put a struct
header at the top bytes of the page:
You can get the next struct's data pointer by just adding the size in bytes to the current structure.
In the assignment notes you can use intptr_t.h
and uintptr_t.h
to help do some of this arithmetic for you, rather than having to consider the type of the pointer and namely its size.
What should the struct have?
- Whether it's allocated or not
- The size of the user data
- The pointer to the user data (can be calculated)
- How big the next block is (for the purposes of merging, easy since you can just remove the middle struct as free data).
Make sure that you use some static
global pointer to be the root of the LL, so that you can traverse the whole list. If you need more space you move sbrk
until you don't need more room.
What happens if sbrk
says no and doesn't move? You need your calls to return NULL
and set errno
to the right error code. For instance ENOMEM
seen in errno.h
.
sbrk
Here's an example of using sbrk()
calls:
#define PAGE_SIE 65536
sbrk(PAGE_SIZE);
h->next->size += PAGE_SIZE;
// ...
It'll help to have a function that iterates through all the LL nodes and then either printf()
or do something else with it to get information when debugging.
Libraries
The process of compilation is:
- Turn all
*.c
into relevant*.o
files viagcc
or similar. - Use
ld
to link all the static libraries (see below) - When running, relevant dynamic libraries are used.
Static Libraries are libraries that are linked at link time (*.a
). A dynamic library aren't linked until you actually run the program (*.so
).
- You use
-I
for include paths for static libraries (ie: your headers) - You use
-L
for dyanmic libraries (it already checks some default locations) - You use
-l<name>
to include certain C libraries.
You need to do it in this order! Otherwise you won't include the implementations of the libraries before you include them.-lc
is always done last.
You need to make sure that you don't include lib C, otherwise you'll use the already used malloc()
. Add a print statement or something in your malloc()
such that you know its running yours!