Inter Process Communication:
TCP:
http://www.cs.berkeley.edu/~kfall/EE122/lec23/sld001.htm
http://www.cs.berkeley.edu/~kfall/EE122/
C:
http://www.interviewmantra.net/2008/11/list-of-all-c-interview-questions-in.html
Inter Process Communication:
TCP:
http://www.cs.berkeley.edu/~kfall/EE122/lec23/sld001.htm
http://www.cs.berkeley.edu/~kfall/EE122/
C:
http://www.interviewmantra.net/2008/11/list-of-all-c-interview-questions-in.html
pthread_mutex_lock(&mutex); /* lock the mutex */ predicate=1; /* set the predicate */ pthread_cond_broadcast(&condvar); /* wake everyone up */ pthread_mutex_unlock(&mutex); /* unlock the mutex */
pthread_cond_broadcast() will wake up all the threads waiting for that condition.
When waken up, pthread_cond_wait() will relock the mutex and do further processing.
List of major pthreads routines
Signal handling function:
sigaction function can be used to set a signal disposition. The first parameter is the signal number. The next two parameters are pointers to sigaction structures. The fist of these contains the desired disposition for that signal number, while the second receives the previous disposition. The most important field in the first or second sigaction structure is sa_handler . It takes one of the three values.
- SIG_DFL - default disposition for the signal
- SIG_IGN - signal should be ignored
- A pointer to signal-handler function. The function should take one parameter, ie., signal number and return void.
Signal handler should perform minimum work necessary to respond to the signal and then return control to the main program. A signal handler may be interrupted by another signal. So we need to be careful while placing code in the signal handler. Even assigning a value to a global variable can be dangerous as the assignment may actually be carried out in 2 or more machine instructions and a second signal may occur between them, leaving the variable in a corrupted state.
If we use global variable to flag a signal in a signal-handler function , it should be of special type sig_atomic_t . Linux guarantees the assignment of these variables is carried out in a single instruction.
Ex:
----
kill(child_pid, SIGTERM) - to kill the child from the parent. Include the sys/type.h and signal.h if we want to use kill function.
Wait - Wait system call allow us to wait for a process to finish executing, and enable the parent process to retrieve information about its child's termination.
wait(&child_status) - Waits for the child to exit and gets the return value in child_status
waitpid(child_pid) - Waits for child with pid = child_pid to exit.
wait3 - returns the cpu usage statistics about the exiting child process
wait4 - allows us to specify additional options about which process to wait for.
Zombie processes - a process that has terminated but has not been cleaned up yet.
This happens when a child process teminates, when the parent is not calling wait(). It is the responsibility of the parent process to clean up its zombie children.
When the parent exits without cleaning the zombie child, this child is inherited by the init process (pid =1 ) and init cleans up all the zombie process it inherits
When a child terminates, Linux sends a SIGCHLD signal to the parent process. The signal handler can be modified to clean up the child processes.
Preprocessing is the first pass of any C compilation. It processes include-files, conditional compilation instructions and macros.
Compilation is the second pass. It takes the output of the preprocessor, and the source code, and generates assembler source code.
Assembly is the third stage of compilation. It takes the assembly source code and produces an assembly listing with offsets. The assembler output is stored in an object file.
Linking is the final stage of compilation. It takes one or more object files or libraries as input and combines them to produce a single (usually executable) file. In doing so, it resolves references to external symbols, assigns final addresses to procedures/functions and variables, and revises code and data to reflect new addresses (a process called relocation)
ELF - (Executable and Linking Format) is file format that defines how an object file is composed and organized. With this information, your kernel and the binary loader know how to load the file, where to look for the code, where to look the initialized data, which shared library that needs to be loaded and so on.
Different sections in an executable format are:
Section
Description
.text
This section contains the executable instruction codes and is shared among every process running the same binary. This section usually has READ and EXECUTE permissions only. This section is the one most affected by optimization.
.bss
BSS stands for ‘Block Started by Symbol’. It holds un-initialized global and static variables. Since the BSS only holds variables that don't have any values yet, it doesn't actually need to store the image of these variables. The size that BSS will require at runtime is recorded in the object file, but the BSS (unlike the data section) doesn't take up any actual space in the object file.
.data
Contains the initialized global and static variables and their values. It is usually the largest part of the executable. It usually has READ/WRITE permissions.
.rdata
Also known as .rodata (read-only data) section. This contains constants and string literals.
.reloc
Stores the information required for relocating the image while loading.
Symbol table
A symbol is basically a name and an address. Symbol table holds information needed to locate and relocate a program’s symbolic definitions and references. A symbol table index is a subscript into this array. Index 0 both designates the first entry in the table and serves as the undefined symbol index. The symbol table contains an array of symbol entries.
Relocation records
Relocation is the process of connecting symbolic references with symbolic definitions. For example, when a program calls a function, the associated call instruction must transfer control to the proper destination address at execution. Re-locatable files must have relocation entries’ which are necessary because they contain information that describes how to modify their section contents, thus allowing executable and shared object files to hold the right information for a process's program image. Simply said relocation records are information used by the linker to adjust section contents.
Good link describing the use of readelf and object dump:
Process Loading:
Before we can run an executable, firstly we have to load it into memory.
This is done by the loader, which is generally part of the operating system. The loader does the following things (from other things):
Memory and access validation - Firstly, the OS system kernel reads in the program file’s header information and does the validation for type, access permissions, memory requirement and its ability to run its instructions. It confirms that file is an executable image and calculates memory requirements.
Process setup includes:
Allocates primary memory for the program's execution.
Copies address space from secondary to primary memory.
Copies the .text and .data sections from the executable into primary memory.
Copies program arguments (e.g., command line arguments) onto the stack.
Initializes registers: sets the esp (stack pointer) to point to top of stack, clears the rest.
Jumps to start routine, which: copies main()'s arguments off of the stack, and jumps to main().
Address space is memory space that contains program code, stack, and data segments or in other word, all data the program uses as it runs.
The memory layout, consists of three segments (text, data, and stack), in simplified form is shown in Figure w.5.
The dynamic data segment is also referred to as the heap, the place dynamically allocated memory (such as from malloc() and new) comes from. Dynamically allocated memory is memory allocated at run time instead of compile/link time.
This organization enables any division of the dynamically allocated memory between the heap (explicitly) and the stack (implicitly). This explains why the stack grows downward and heap grows upward.
Before we can run an executable, firstly we have to load it into memory.
This is done by the loader, which is generally part of the operating system. The loader does the following things (from other things):
Memory and access validation - Firstly, the OS system kernel reads in the program file’s header information and does the validation for type, access permissions, memory requirement and its ability to run its instructions. It confirms that file is an executable image and calculates memory requirements.
Process setup includes:
Allocates primary memory for the program's execution.
Copies address space from secondary to primary memory.
Copies the .text and .data sections from the executable into primary memory.
Copies program arguments (e.g., command line arguments) onto the stack.
Initializes registers: sets the esp (stack pointer) to point to top of stack, clears the rest.
Jumps to start routine, which: copies main()'s arguments off of the stack, and jumps to main().
Address space is memory space that contains program code, stack, and data segments or in other word, all data the program uses as it runs.
The memory layout, consists of three segments (text, data, and stack), in simplified form is shown in Figure w.5.
The dynamic data segment is also referred to as the heap, the place dynamically allocated memory (such as from malloc() and new) comes from. Dynamically allocated memory is memory allocated at run time instead of compile/link time.
This organization enables any division of the dynamically allocated memory between the heap (explicitly) and the stack (implicitly). This explains why the stack grows downward and heap grows upward.
Run time data structure: From sections to segments
A process is a running program. This means that the operating system has loaded the executable file for the program into memory, has arranged it to have access to its command-line arguments and environment variables, and has started it running.
Typically a process has 5 different areas of memory allocated to it as listed in Table w.5 (refer to Figure w.4):
Segment
Description
Code - text segment
Often referred to as the text segment, this is the area in which the executable instructions reside. For example, Linux/Unix arranges things so that multiple running instances of the same program share their code if possible. Only one copy of the instructions for the same program resides in memory at any time. The portion of the executable file containing the text segment is the text section.
Initialized data – data segment
Statically allocated and global data that are initialized with nonzero values live in the data segment. Each process running the same program has its own data segment. The portion of the executable file containing the data segment is the data section.
Uninitialized data – bss segment
BSS stands for ‘Block Started by Symbol’. Global and statically allocated data that initialized to zero by default are kept in what is called the BSS area of the process. Each process running the same program has its own BSS area. When running, the BSS data are placed in the data segment. In the executable file, they are stored in the BSS section. For Linux/Unix the format of an executable, only variables that are initialized to a nonzero value occupy space in the executable’s disk file.
Heap
The heap is where dynamic memory (obtained by malloc(), calloc(), realloc() and new for C++) comes from. Everything on a heap is anonymous, thus you can only access parts of it through a pointer. As memory is allocated on the heap, the process’s address space grows. Although it is possible to give memory back to the system and shrink a process’s address space, this is almost never done because it will be allocated to other process again. Freed memory (free() and delete) goes back to the heap, creating what is called holes. It is typical for the heap to grow upward. This means that successive items that are added to the heap are added at addresses that are numerically greater than previous items. It is also typical for the heap to start immediately after the BSS area of the data segment. The end of the heap is marked by a pointer known as the break. You cannot reference past the break. You can, however, move the break pointer (via brk() and sbrk() system calls) to a new position to increase the amount of heap memory available.
Stack
The stack segment is where local (automatic) variables are allocated. In C program, local variables are all variables declared inside the opening left curly brace of a function body including the main() or other left curly brace that aren’t defined as static. The data is popped up or pushed into the stack following the Last In First Out (LIFO) rule. The stack holds local variables, temporary information, function parameters, return address and the like. When a function is called, a stack frame (or a procedure activation record) is created and PUSHed onto the top of the stack. This stack frame contains information such as the address from which the function was called and where to jump back to when the function is finished (return address), parameters, local variables, and any other information needed by the invoked function. The order of the information may vary by system and compiler. When a function returns, the stack frame is POPped from the stack. Typically the stack grows downward, meaning that items deeper in the call chain are at numerically lower addresses and toward the heap.
Table w.5
When a program is running, the initialized data, BSS and heap areas are usually placed into a single contiguous area called a data segment.
The stack segment and code segment are separate from the data segment and from each other as illustrated in Figure w.4.
Although it is theoretically possible for the stack and heap to grow into each other, the operating system prevents that event.
The relationship among the different sections/segments is summarized in Table w.6, executable program segments and their locations.
Executable file section
(disk file)
Address space segment
Program memory segment
.text
Text
Code
.data
Data
Initialized data
.bss
Data
BSS
-
Data
Heap
-
Stack
Stack
The Process:
The diagram below shows the memory layout of a typical C’s process. The process load segments (corresponding to "text" and "data" in the diagram) at the process's base address.
The main stack is located just below and grows downwards. Any additional threads or function calls that are created will have their own stacks, located below the main stack.
Each of the stack frames is separated by a guard page to detect stack overflows among stacks frame. The heap is located above the process and grows upwards.
In the middle of the process's address space, there is a region is reserved for shared objects. When a new process is created, the process manager first maps the two segments from the executable into memory.
It then decodes the program's ELF header. If the program header indicates that the executable was linked against a shared library, the process manager will extract the name of the dynamic interpreter from the program header.
The dynamic interpreter points to a shared library that contains the runtime linker code. The process manager will load this shared library in memory and will then pass control to the runtime linker code in this library.
Function prototype | Function description |
int getchar(void) | Input the next character from the standard input (keyboard) and return it as an integer. |
char *gets(char *s) | Input characters from the standard input (keyboard) into the array s until a newline or end-of-file character is encountered. A terminating NULL character is appended to the array. |
int putchar(int c) | Print the character stored in c. |
int puts(const char *s) | Print the string s followed by a newline character. |
int sprintf(char *s, const char *format, …) | Equivalent to printf() except the output is stored in the array s instead of printing on the screen. |
int sscanf(char *s, const char *format, …) | Equivalent to scanf() except the input is read from the array s instead of reading from the keyboard. |
char *strtok(char *s1, const char *s2)
A sequence of calls to strtok() breaks string s1 into “tokens”, logical pieces such as words in a line of text, separated by characters contained in string s2. The first call contains s1 as the first argument, and subsequent calls to continue tokenizing the same string contain NULL as the first argument. A pointer to the current token is returned by each call. If there are no more tokens when the function is called, NULL is returned.
Memory Functions:
Function prototype | Function description |
void *memcpy(void *s1, const void *s2, size_t n) | Copies n characters from the object pointed to by s2 into the object pointed to by s1. A pointer to the resulting object is returned. |
void *memmove(void *s1, const void *s2, size_t n) | Copies n characters from the object pointed to by s2 into the object pointed to by s1. The copy is performed as if the characters are first copied from the object pointed to by s2 into temporary array, then from the temporary array into the object pointed to by s1. A pointer to the resulting object is returned. |
int memcmp(const void *s1, const void *s2, size_t n) | Compares the first n characters of the objects pointed to by s1 and s2. The function return 0, less than 0, or greater than 0 if s1 is equal to, less than, or greater than s2. |
void *memchr(const void *s, int c, size_t n) | Locates the first occurrence of c (converted to unsigned char) in the first n characters of the object pointed to by s. If c is found, a pointer to c in the object is returned. Otherwise NULL is returned. |
void *memset(void *s, int c, size_t n) | Copies c (converted to unsigned char) into the first n characters of the object pointed to by s. A pointer to the result is returned. |
|
Dynamic Memory Allocation:
- Dynamic memory is allocated on the heap.
For more details: http://www.tenouk.com/ModuleZ.html
Heap
- The heap segment provides more stable storage of data for a program; memory allocated in the heap remains in existence for the duration of a program.
- global variables (external storage class), and static variables are allocated on the heap.The memory allocated in the heap area, if initialized to zero at program start, remains zero until the program makes use of it. Thus, the heap area need not contain garbage.
malloc() :
char * test;
test = (char *) malloc(10); // allocates 10 bytes of memory
calloc():
int * test;
test = (int *) calloc(5, sizeof(int)); // allocates 5* 4 (size of int) bytes
- Initializes the allocated memory elements to zero.
realloc():
reallocates the already allocated memory
void * realloc (void * pointer, size_t elemsize);
rmdir() - Removes a directory.
mkdir() - Creates a directory
Pre-processor directives:
#error - prints the error message including the tokens specified
ex: