Embedded Software Interview Questions
Embedded Software Interview Questions
Technical Questions 6
Diff between embedded systems and general computers system 6
What is Thread? 6
What is concurrency and multithreading? 6
Benefits of multithreaded programming? 8
What is dead lock and what cause it? 8
Benefits of process over threads? 9
What is volatile keyword? 9
What is static keyword in C? 10
Difference between declare and defining a variable? 10
What is extern keyword? 10
Extern vs. Static keyword 11
Critical Section 11
Interrupt Handling 12
Upper half and bottom half of ISR 13
Direct Memory Access (DMA) 14
Priority inversion in a RTOS and its solutions 15
Big & Little endian – definitions, representations, write it down, swap them, etc
16
Semaphore vs mutex 17
Software method to reduce power consumption of embedded devices 18
How to realize semaphore? 19
How to multithread? 19
Pass by reference vs. Pass by pointer 20
Describe watch-dog timer. (Qualcomm) 21
Describe virtual memory 21
Demand Paging 22
Difference between Thread and Process 22
What are dangling pointers? where to use them? 23
Explain process state Diagram ? 23
Memory Mapping 24
1
Fragmentation 25
Floating Point Arithmetic 25
Inline function vs. Macro 26
How to allocate memory in kernel? 27
Struct padding and packing in C/C++ 28
How OS detect stack overflow? 28
What is in virtual address space? 29
What is pipeline? 5 stage pipeline in RISC? data hazard? 29
Difference between linked list and array? when to use linked list? 31
How are interrupts handled in RTOS? (Qualcomm) 31
User mode and kernel mode 32
Buffer overflows and impacts/problems 32
Difference between library call and a system call 33
CPU scheduling algorithms 34
Memory Management 35
Paging 36
Segmentation 36
Page Replacement Algorithms 36
Page Fault 37
Memory map of program, Storage classes and their mapping 37
Describe inheritance. (Qualcomm) 38
If we declare more number of variables than the registers available on the
processor? Where they will be stored. 39
What is thrashing? 39
What is Cache? 40
Draw the block diagram of a computer and explain? How will you make a
computer? 41
Spinlocks 42
What if OS does not release spinlock? 43
Context of a process 43
Context switching 44
What is atomic programming/non-locking operation? 44
Explain and describe how binary search tree work 45
2
What is recursion? What actually happens during recursion? does the memory get
stored on stack? what gets called and how does the program know from where to
call? 46
What is free()? how does free know how much memory to de-allocate? 46
what is the difference between class and object? does class or object create
memory? 46
How post increment works. 47
what is virtual function? 47
What is the difference between the stack and the heap 48
Stack Overflow 48
What happens when you try to free a null pointer 48
Difference between CDMA and GSM technologies 49
What is 3G LTE 49
JTAG 50
SPI and I2C 50
What is TCP and UDP? What is the difference? 51
What is IP protocol? 53
IPv4 vs. IPv6 54
What is the network layer structure? 56
OS Composition? 56
What is the difference between struct and union in C? 57
Quick Sort vs. Merge Sort 57
What is polymorphism, what is it for, and how is it used? 58
When is a null pointer used? 58
What is protected keyword? Difference from private member? 59
Friends class/function 59
What is pure virtual function and abstract class? 59
Function Overloading and Overriding 60
What is preemptive multitasking? 60
What goes inside the compilation process? 60
Coding Questions 61
Reverse an 8 bit type. (Qualcomm) 61
C Program to reverse the words in a sentence . (QualComm) 62
3
Count the number of set bits in an integer. (QualComm) 63
Round a number to next largest multiple of N 64
Aligned Malloc and aligned free (memalign) 65
swap even and odd bits of a given number 65
Reverse string, reverse words in a string, find duplicates in an array (QualComm)
66
Write a Link List for deleting a node (Qualcomm) 66
Find the first non-recurring character in a string 66
Implement a queue/fifo with push/pop functionality using linked lists 67
Implement Strlen 68
Swap the values of two pointers without a temp variable 69
Write a function that determines if a given variable is a power of 2 or not 69
Reverse a linked list 70
Find a loop in a linked list. 71
Given a list from 1 to 100, name all the different ways you can determine if there
are duplicates. Which is the most efficient? 72
Write a binary search tree. 72
Implement strcpy function and show me if there are any limitation of this function.
what if the 2 buffers passed to the strcpy function overlaps ? 74
Write a own program for strstr function, optimal way 74
Write a program to convert a given single Linked list to BST 76
Implement memcpy() on your own (and memcpy problem) 76
Calculate Fibonacci Series 77
Palindrome Check 78
Prime Number Check 78
Merge Sort, Quick Sort, Bubble Sort, Insertion Sort, Wiggle sort 79
General Questions (brain teaser) 85
How would you design an elevator system 85
Find out 45 minutes with the help of two ropes. Given that one rope burns
completely in 1 Hr and the rate or burning is not consistent. 85
Why sewer caps are round? 85
There are 9 coins, find the lightest one. How many times you need to weight?
85
4
Behavior Questions 86
What are your interests? 86
Describe a time when you had a technical disagreement you felt strongly about,
with another person on your team, and how you handled this. 86
Talk about a time you disagreed with a team mate, and how did you resolve the
issue. 86
What are your weaknesses? 86
How do you want to see yourself after a year, you intentions, aspirations 86
5
Technical Questions
Embedded System, as it appears from its name, is a part of a bigger system, a computer
restricted to one function (or a finite set of functions) that controls, monitors or integrate with
larger systems like automotive, robotics, home appliances and military applications.
They have very tight constraints regarding size, performance, memory, price and
durability, also an embedded system is required in most cases to respond in real time,
you don’t want your brakes to work after pressing the pedal by a couple of seconds tho..
What is Thread?
A thread of execution is the smallest sequence of programmed instructions that can be
managed independently by a scheduler, which is typically a part of the operating system.[1] The
implementation of threads and processes differs between operating systems, but in most cases
a thread is a component of a process. Multiple threads can exist within one process, executing
concurrently and sharing resources such as memory, while different processes do not share
these resources. In particular, the threads of a process share its executable code and the values
of its variables at any given time.
Systems with a single processor generally implement multithreading by time slicing: the central
processing unit (CPU) switches between different software threads. This context switching
generally happens very often and rapidly enough that users perceive the threads or tasks as
running in parallel. On a multiprocessor or multi-core system, multiple threads can execute in
parallel, with every processor or core executing a separate thread simultaneously; on a
processor or core with hardware threads, separate software threads can also be executed
concurrently by separate hardware threads.
6
concurrently, appropriately supported by the operating system. This approach differs from
multiprocessing, as with multithreading the processes and threads share the resources of a
single or multiple cores: the computing units, the CPU caches, and the translation lookaside
buffer (TLB). (everything including head is shared among threads but only stack is not)
Threads are
known
as
CPU switches rapidly back and forth among the threads giving illusion that the threads are
running in parallel.
As each thread has its own independent resource for process execution, multiple processes can
be executed parallel-y by increasing number of threads.
Advantages:
If a thread gets a lot of cache misses, the other threads can continue taking advantage of the
unused computing resources, which may lead to faster overall execution as these
resources would have been idle if only a single thread were executed. Also, if a thread cannot
use all the computing resources of the CPU (because instructions depend on each other's
result), running another thread may prevent those resources from becoming idle.
If several threads work on the same set of data, they can actually share their cache, leading to
better cache usage or synchronization on its values.
Where multiprocessing systems include multiple complete processing units, multithreading aims
to increase utilization of a single core by using thread-level as well as instruction-level
parallelism.
Disadvantages:
Multiple threads can interfere with each other when sharing hardware resources such as caches
or translation lookaside buffers (TLBs). As a result, execution times of a single thread are not
improved but can be degraded, even when only one thread is executing, due to lower
7
frequencies or additional pipeline stages that are necessary to accommodate thread-switching
hardware.
Resource sharing. Processes may only share resources through techniques such as shared
memory or message passing. Such techniques must be explicitly arranged by the programmer.
However, threads share the memory and the resources of the process to which they belong by
default.
Economy. Allocating memory and resources for process creation is costly. Because threads
share the resources of the process to which they belong, it is more economical to create and
context-switch threads. Empirically gauging the difference in overhead can be difficult, but in
general, it is much more time consuming to create and manage processes than threads.
Better system utilization as an example, a file system using multiple threads can achieve
higher throughput and lower latency since data in a faster medium (such as cache memory) can
be retrieved by one thread while another thread retrieves data from a slower medium (such as
external storage) with neither thread waiting for the other to finish.
Mutual Exclusion: One or more than one resource are non-sharable (Only one process can
use at a time)
Hold and Wait: A process is holding at least one resource and waiting for resources.
No Preemption: A resource cannot be taken from a process unless the process releases the
resource.
Circular Wait: A set of processes are waiting for each other in circular form.
8
Methods for handling deadlock
There are three ways to handle deadlock
1) Deadlock prevention or avoidance: The idea is to not let the system into deadlock state.
2) Deadlock detection and recovery: Let deadlock occur, then do preemption to handle it once
occurred.
3) Ignore the problem all together: If deadlock is very rare, then let it happen and reboot the
system. This is the approach that both Windows and UNIX take.
2. Independency. Another advantage of processes is that they can crash and you can feel
relatively safe in the knowledge that you can just restart them (even across network hosts).
However, if a thread crashes, it may crash the entire process, which may bring down your entire
application. To illustrate: If an Erlang process crashes, you will only lose that phone call, or that
webrequest, etc. Not the whole application.
The volatile keyword is intended to prevent the compiler from applying any optimizations
on objects that can change in ways that cannot be determined by the compiler.
1) Objects declared as volatile are omitted from optimization because their values can be
changed by code outside the scope of current code at any time. The system always
reads the current value of a volatile object from the memory location rather than
keeping its value in temporary register at the point it is requested, even if a previous
instruction asked for a value from the same object.
2) Global variables within a multi-threaded application: There are multiple ways for threads
communication, viz, message passing, shared memory, mail boxes, etc. A global variable is
weak form of shared memory. When two threads sharing information via global
variable, they need to be qualified with volatile. Since threads run asynchronously, any
update of global variable due to one thread should be fetched freshly by another consumer
thread. Compiler can read the global variable and can place them in temporary variable of
current thread context. To nullify the effect of compiler optimizations, such global
variables to be qualified as volatile
9
What is static keyword in C?
- Act as global variable only visible to this function/file (static variables in function)
- limit the scope of the variable/function (static class objects/function)
Static variables have a property of preserving their value even after they are out of their
scope! Hence, static variables preserve their previous value in their previous scope and are not
initialized again in the new scope.
1) A static int variable remains in memory while the program is running. A normal or auto
variable is destroyed when a function call where the variable was declared is over.
2) Static variables are allocated memory in data segment, not stack segment.
3) Static variables (like global variables) are initialized as 0 if not initialized explicitly.
4) In C, static variables can only be initialized using constant literals.
5) Static global variables and functions are also possible in C/C++. The purpose of these is to
limit scope of a variable or function to a file. Access to static functions is restricted to the
file where they are declared. Another reason for making functions static can be reuse of the
same function name in other files.
6) Static variable limits the scope of the variable/function within the module it is been
declared.
The extern keyword means "declare without defining". In other words, it is a way to
explicitly declare a variable, or to force a declaration without a definition. As an alternative to
automatic variables, it is possible to define variables that are external to all functions, that is,
variables that can be accessed by name by any function. It is also possible to explicitly define a
variable, i.e. to force a definition. It is done by assigning an initialization value to a variable. If
neither the extern keyword nor an initialization value are present, the statement can be either a
declaration or a definition. It is up to the compiler to analyse the modules of the program and
decide.
A variable must be defined exactly once in one of the modules of the program. If there is no
definition or more than one, an error is produced, possibly in the linking stage. A variable may
be declared many times, as long as the declarations are consistent with each other and with the
definition (something which header files facilitate greatly). It may be declared in many modules,
10
including the module where it was defined, and even many times in the same module. But it is
usually pointless to declare it more than once in a module.
An external variable may also be declared inside a function. In this case the extern keyword
must be used, otherwise the compiler will consider it a definition of a local (automatic)
variable, which has a different scope, lifetime and initial value. This declaration will only be
visible inside the function instead of throughout the function's module.
The extern keyword applied to a function prototype does absolutely nothing (the extern
keyword applied to a function definition is, of course, non-sensical). A function prototype is
always a declaration and never a definition. Also, in standard C, a function is always external,
but some compiler extensions allow a function to be defined inside a function.
The extern storage class is used to declare a global variable that will be known to the
functions in a file and capable of being known to all functions in a program. This storage
class has a duration that is permanent. Any variable of this class retains its value until changed
by another assignment. The scope is global. A variable can be known or seen by all functions
within a program.
Critical Section
In concurrent programming, concurrent accesses to shared resources can lead to unexpected
or erroneous behavior, so parts of the program where the shared resource is accessed
protected. This protected section is the critical section or critical region. It cannot be
executed by more than one process. Typically, the critical section accesses a shared resource,
such as a data structure, a peripheral device, or a network connection, that would not operate
correctly in the context of multiple concurrent accesses.
11
Interrupt Handling
- CPU checks for HW interrupts after each user mode instruction is executed.
- If an interrupt signal is present, a kernel routine is called to handle the interrupt.
- Different routine or interrupt handler are chosen from the Interrupt vector table to handle
different interrupt. These routine are pre defined code that stored in a fixed memory position
in the kernel memory space.
- Interrupt Descriptor Table is reserved RAM memory block that intel and AMD CPUs provide
special instructions and structures for fast interrupt handling.
- Two types of interrupt handing: slow and fast (all happen in kernel).
- Fast handler is the piece of code invoked directly from IVT (vector table) whenever an
interrupt occurs.
- Slow handlers are invoked at a later time and lower priority (can be preempt by fast
handlers). It will be put in the OS task queue. CPU check this task queue after executing fast
handlers.
- Fast handlers are atomic (un-interruptable), achieving by disabling interrupt while executing
fast handlers.
- There will be trouble if FIH (fast interrupt handler) takes too long because it wont take in other
interrupts in the process.
- An interrupt storm happens when another interrupt is always waiting to be processed
whenever FIH finishes execution (FIH takes too long).
- If OS perpetually handling interrupts, it never run app code and no respond to users input ->
result in live lock - system running but appear freezing. (use power button to solve this)
- Thats why we use two types of handlers. FIH must be fast and atomic!!! If slow, then treat it
as slow IH which can be preempted.
12
Upper half and bottom half of ISR
The generic problem of any ISR is it's latency. Since most often the corresponding interrupt is
disabled during the execution of a ISR, the ISR is expected to be [Link], what if you have to
do a lot of data processing, memory allocation in a ISR. Linux overcomes this problem by
providing an infrastructure in the kernel to split a ISR into
Top-Half: This is the critical section. The interrupt is disabled when this is executed.
Bottom-Half: This is the less critical section. The interrupt is enable when this is executed.
It is not mandatory to split the ISR's in Linux. It is a device driver developers choice. If the driver
developer feels, the ISR is going to very short and can be managed then he may not decide to
implement a bottom-half. Similarly, if the driver developer thinks disabling interrupt for a long
time is OK for his use case, then he may still have a fat top-half. So, it is a design decision.
The big difference between the top-half handler and the bottom half is that all interrupts are
enabled during execution of the bottom half -- that's why it runs at a safer time. In the typical
scenario, the top half saves device data to a device-specific buffer, schedules its bottom half,
and exits: this is very fast. The bottom half then performs whatever other work is required, such
as awakening processes, starting up another I/O operation, and so on. This setup permits the
top half to service a new interrupt while the bottom half is still working.
Every serious interrupt handler is split this way. For instance, when a network interface reports
the arrival of a new packet, the handler just retrieves the data and pushes it up to the protocol
layer; actual processing of the packet is performed in a bottom half.
One thing to keep in mind with bottom-half processing is that all of the restrictions that apply to
interrupt handlers also apply to bottom halves. Thus, bottom halves cannot sleep, cannot
access user space, and cannot invoke the scheduler.
The Linux kernel has two different mechanisms that may be used to implement bottom-half
processing. Tasklets were introduced late in the 2.3 development series; they are now the
preferred way to do bottom-half processing, but they are not portable to earlier kernel versions.
The older bottom-half (BH) implementation exists in even very old kernels, though it is
13
implemented with tasklets in 2.4. We'll look at both mechanisms here. In general, device drivers
writing new code should choose tasklets for their bottom-half processing if possible, though
portability considerations may determine that the BH mechanism needs to be used instead.
Without DMA, when the CPU is using programmed input/output, it is typically fully occupied for
the entire duration of the read or write operation, and is thus unavailable to perform other work.
With DMA, the CPU first initiates the transfer, then it does other operations while the transfer is
in progress, and it finally receives an interrupt from the DMA controller when the operation is
done. This feature is useful at any time that the CPU cannot keep up with the rate of data
transfer, or when the CPU needs to perform work while waiting for a relatively slow I/O data
transfer. Many hardware systems use DMA, including disk drive controllers, graphics cards,
network cards and sound cards. DMA is also used for intra-chip data transfer in multi-core
processors. Computers that have DMA channels can transfer data to and from devices with
much less CPU overhead than computers without DMA channels. Similarly, a processing
element inside a multi-core processor can transfer data to and from its local memory without
occupying its processor time, allowing computation and data transfer to proceed in parallel.
To carry out an input, output or memory-to-memory operation, the host processor initializes the
DMA controller with a count of the number of words to transfer, and the memory address to use.
The CPU then sends commands to a peripheral device to initiate transfer of data. The DMA
controller then provides addresses and read/write control lines to the system memory. Each
time a byte of data is ready to be transferred between the peripheral device and memory, the
DMA controller increments its internal address register until the full block of data is transferred.
14
Priority inversion in a RTOS and its solutions
Consider two tasks H and L, of high and low priority respectively, either of which can acquire
exclusive use of a shared resource R. If H attempts to acquire R after L has acquired it, then H
becomes blocked until L relinquishes the resource. Sharing an exclusive-use resource (R in this
case) in a well-designed system typically involves L relinquishing R promptly so that H (a higher
priority task) does not stay blocked for excessive periods of time. Despite good design,
however, it is possible that a third task M of medium priority (p(L) < p(M) < p(H), where p(x)
represents the priority for task (x)) becomes runnable during L's use of R. At this point, M being
higher in priority than L, preempts L, causing L to not be able to relinquish R promptly, in turn
causing H—the highest priority process—to be unable to run. This is called priority inversion
where a higher priority task is preempted by a lower priority one.
L is running in CS (critical section); H also needs to run in CS ; H waits for L to come out of CS ;
M interrupts L and starts running (while L is in its CS); M runs till completion and relinquishes
control ; L resumes and starts running till the end of CS ; H enters CS and starts running.
Note that neither L nor H share CS with M.
The most famous ‘Priority Inversion’ problem was what happened at Mars Pathfinder.
In multiple CPU systems, a simple variation, "single shared-flag locking" is used. This scheme
provides a single flag in shared memory that is used by all CPUs to lock all inter-processor
critical sections with a busy-wait. Interprocessor communications are expensive and slow on
most multiple CPU systems. Therefore, most such systems are designed to minimize shared
resources. As a result, this scheme actually works well on many practical systems. These
methods are widely used in simple embedded systems, where they are prized for their reliability,
simplicity and low resource use. These schemes also require clever programming to keep the
critical sections very brief. Many software engineers consider them impractical in general-
purpose computers.
A priority ceiling
15
With priority ceilings, the shared mutex process (that runs the operating system code) has a
characteristic (high) priority of its own, which is assigned to the task locking the mutex. This
works well, provided the other high priority task(s) that tries to access the mutex does not have
a priority higher than the ceiling priority.
Priority inheritance
Under the policy of priority inheritance, whenever a high priority task has to wait for some
resource shared with an executing low priority task, the low priority task is temporarily assigned
the priority of the highest waiting priority task for the duration of its own use of the shared
resource, thus keeping medium priority tasks from pre-empting the (originally) low priority task,
and thereby affecting the waiting high priority task as well. Once the resource is released, the
low priority task continues at its original priority level.
Random boosting
Ready tasks holding locks are randomly boosted in priority until they exit the critical section.
This solution is used in Microsoft Windows.
Avoid blocking
Because priority inversion involves a low-priority task blocking a high-priority task, one way to
avoid priority inversion is to avoid blocking, for example by using Non-blocking synchronization
or Read-copy-update.
Little and big endian are two ways of storing multibyte data-types ( int, float, etc). In little endian
machines, last byte of binary representation of the multibyte data-type is stored first. On the
other hand, in big endian machines, first byte of binary representation of the multibyte data-type
is stored first.
Suppose integer is stored as 4 bytes (For those who are using DOS based compilers such as C
++ 3.0 , integer is 2 bytes) then a variable x with value 0x01234567 will be stored as following.
16
Check machine Endianness:
#include <stdio.h>
int main()
{
unsigned int i = 1;
char *c = (char*)&i;
if (*c)
printf("Little endian");
else
printf("Big endian");
getchar();
return 0;
}
In the above program, a character pointer c is pointing to an integer i. Since size of character is
1 byte when the character pointer is de-referenced it will contain only first byte of integer. If
machine is little endian then *c will be 1 (because last byte is stored first) and if machine is big
endian then *c will be 0.
Semaphore vs mutex
As per operating system terminology, mutex and semaphore are kernel resources that
provide synchronization services (also called as synchronization primitives). A
Semaphore is an integer variable. There are two types of semaphores : Binary
Semaphores (mutex) and Counting Semaphores
Binary Semaphores (Not mutex!!!): They can only be either 0 or 1. They are also known as
mutex locks, as the locks can provide mutual exclusion. All the processes can share the
same mutex semaphore that is initialized to 1. Then, a process has to wait until the lock
becomes 0. Then, the process can make the mutex semaphore 1 and start its critical section.
17
When it completes its critical section, it can reset the value of mutex semaphore to 0 and some
other process can enter its critical section.
Counting Semaphores : They can have any value and are not restricted over a certain
domain. They can be used to control access a resource that has a limitation on the number of
simultaneous accesses. The semaphore can be initialized to the number of instances of the
resource. Whenever a process wants to use that resource, it checks if the number of remaining
instances is more than zero, i.e., the process has an instance available. Then, the process can
enter its critical section thereby decreasing the value of the counting semaphore by 1. After the
process is over with the use of the instance of the resource, it can leave the critical section
thereby adding 1 to the number of available instances of the resource.
Using Mutex:
A mutex is costly operation due to protection protocols associated with it. At last, the objective of
mutex is atomic access. A mutex provides mutual exclusion, either producer or consumer can
have the key (mutex) and proceed with their work. As long as the buffer is filled by producer, the
consumer needs to wait, and vice versa.
At any point of time, only one thread can work with the entire buffer. The concept can be
generalized using semaphore.
Using Semaphore:
A semaphore is a generalized mutex. In lieu of single buffer, we can split the 4 KB buffer into
four 1 KB buffers (identical resources). A semaphore can be associated with these four buffers.
The consumer and producer can work on different buffers at the same time.
Difference:
Strictly speaking, a mutex is locking mechanism used to synchronize access to a resource.
Only one task (can be a thread or process based on OS abstraction) can acquire the mutex. It
means there is ownership associated with mutex, and only the owner can release the lock
(mutex).
Semaphore is signaling mechanism (“I am done, you can carry on” kind of signal). For
example, if you are listening songs (assume it as one task) on your mobile and at the same time
your friend calls you, an interrupt is triggered and signals the call processing task to wakeup.
18
In sleep mode the clocking to the microprocessor is stopped [3]. The peripheral modules such
as UART, SPI, Ethernet and so on continue to operate independently and only when data
processing is required the core is awakened from this mode. This is done with the help of
interrupts.
Increment a semaphore
sem_post(3RT)
Prototype:
int sem_init(sem_t *sem, int pshared, unsigned int value);
#include <semaphore.h>
sem_t sem;
int pshared;
int ret;
int value;
Use sema_init(3THR) to initialize the semaphore variable pointed to by sem to value amount. If
the value of pshared is zero, then the semaphore cannot be shared between processes. If the
value of pshared is nonzero, then the semaphore can be shared between processes. (For
Solaris threads, see sema_init(3THR).)
A semaphore must not be reinitialized while other threads might be using it.
How to multithread?
#include <pthread.h>
19
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
...
int main()
{
...
pthread_t *tid = malloc( ntimes * sizeof(pthread_t) );
A pointer can be re-assigned while reference cannot, and must be assigned at initialization only.
Pointer can be assigned NULL directly, whereas reference cannot.
Pointers can iterate over an array, we can use ++ to go to the next item that a pointer is pointing
to.
A pointer is a variable that holds a memory address. A reference has the same memory address
as the item it references.
A pointer to a class/struct uses ‘->'(arrow operator) to access it’s members whereas a reference
uses a ‘.'(dot operator)
A pointer needs to be dereferenced with * to access the memory location it points to, whereas a
reference can be used directly.
Overall, Use references when you can, and pointers when you have to. But if we want to write
C code that compiles with both C and a C++ compiler, you’ll have to restrict yourself to
using pointers.
20
Describe watch-dog timer. (Qualcomm)
A watchdog timer (sometimes called a computer operating properly or COP timer, or simply a
watchdog) is an electronic timer that is used to detect and recover from computer
malfunctions. During normal operation, the computer regularly resets the watchdog timer
to prevent it from elapsing, or "timing out". If, due to a hardware fault or program error, the
computer fails to reset the watchdog, the timer will elapse and generate a timeout signal. The
timeout signal is used to initiate corrective action or actions. The corrective actions typically
include placing the computer system in a safe state and restoring normal system operation.
Watchdog timers are commonly found in embedded systems and other computer-controlled
equipment where humans cannot easily access the equipment or would be unable to react to
faults in a timely manner. In such systems, the computer cannot depend on a human to invoke a
reboot if it hangs; it must be self-reliant. For example, remote embedded systems such as
space probes are not physically accessible to human operators; these could become
permanently disabled if they were unable to autonomously recover from faults. A watchdog timer
is usually employed in cases like these. Watchdog timers may also be used when running
untrusted code in a sandbox, to limit the CPU time available to the code and thus prevent some
types of denial-of-service attacks.
The size of virtual storage is limited by the addressing scheme of the computer system and
amount of secondary memory is available not by the actual number of the main storage
locations.
It is a technique that is implemented using both hardware and software. It maps memory
addresses used by a program, called virtual addresses, into physical addresses in computer
memory.
1). All memory references within a process are logical addresses that are dynamically
translated into physical addresses at run time. This means that a process can be swapped
in and out of main memory such that it occupies different places in main memory at different
times during the course of execution.
2). A process may be broken into number of pieces and these pieces need not be continuously
located in the main memory during execution. The combination of dynamic run-time address
translation and use of page or segment table permits this.
If these characteristics are present then, it is not necessary that all the pages or segments are
present in the main memory during execution. This means that the required pages need to be
loaded into memory whenever required. Virtual memory is implemented using Demand Paging
or Demand Segmentation.
21
Advantages :
• More processes may be maintained in the main memory: Because we are going to load
only some of the pages of any particular process, there is room for more processes. This
leads to more efficient utilization of the processor because it is more likely that at least one of
the more numerous processes will be in the ready state at any particular time.
• A process may be larger than all of main memory: One of the most fundamental
restrictions in programming is lifted. A process larger than the main memory can be executed
because of demand paging. The OS itself loads pages of a process in main memory as
required.
• It allows greater multiprogramming levels by using less of the available (primary)
memory for each process.
Demand Paging
A demand paging system is quite similar to a paging system with swapping where processes
reside in secondary memory and pages are loaded only on demand, not in advance. When a
context switch occurs, the operating system does not copy any of the old program’s pages out
to the disk or any of the new program’s pages into the main memory Instead, it just begins
executing the new program after loading the first page and fetches that program’s pages as they
are reference .
Threads are not independent of one other like processes as a result threads shares with other
threads their code section, data section and OS resources like open files and signals. But,
like process, a thread has its own program counter (PC), a register set, and a stack space.
22
What are dangling pointers? where to use them?
A pointer pointing to a memory location that has been deleted (or freed) is called dangling
pointer. A dangling pointer is a pointer that points to invalid data or to data which is not valid
anymore. (point to unallocated or already freed memory). Need to set pointers to be NULL after
free().
23
Memory Mapping
Memory Mapping is a CPU-to-device communication methods, different than DMA which
device directly talk to memory, bypass the CPU. Compare to interrupt, memory mapping is
CPU-initiated while interrupt is device initiated.
I/O operations can slow memory access if the address and data buses are shared. This is
because the peripheral device is usually much slower than main memory. In some architectures,
port-mapped I/O operates via a dedicated I/O bus, alleviating the problem.
One merit of memory-mapped I/O is that, by discarding the extra complexity that port I/O brings,
a CPU requires less internal logic and is thus cheaper, faster, easier to build, consumes less
power and can be physically smaller. The other advantage is that, because regular memory
instructions are used to address devices, all of the CPU's addressing modes are available for
the I/O as well as the memory, and instructions that perform an ALU operation directly on a
memory operand (loading an operand from a memory location, storing the result to a memory
location, or both) can be used with I/O device registers as well.
Memory-mapped file is a segment of virtual memory that has been assigned a direct byte-for-
byte correlation with some portion of a file or file-like resource. This resource is typically a file
that is physically present on disk, but can also be a device, shared memory object, or other
resource that the operating system can reference through a file descriptor. Once present, this
correlation between the file and the memory space permits applications to treat the mapped
portion as if it were primary memory.
The primary benefit of memory mapping a file is increasing I/O performance, especially when
used on large files. For small files, memory-mapped files can result in a waste of slack space[1]
as memory maps are always aligned to the page size, which is mostly 4 KiB. Therefore, a 5 KiB
file will allocate 8 KiB and thus 3 KiB are wasted. Accessing memory mapped files is faster than
using direct read and write operations for two reasons. Firstly, a system call is orders of
magnitude slower than a simple change to a program's local memory. Secondly, in most
operating systems the memory region mapped actually is the kernel's page cache (file cache),
meaning that no copies need to be created in user space.
Memory-mapped I/O (MMIO) and port-mapped I/O (PMIO) (which is also called isolated I/O)
are two complementary methods of performing input/output (I/O) between the central
processing unit (CPU) and peripheral devices in a computer.
Memory-mapped I/O uses the same address space to address both memory and I/O devices.
The memory and registers of the I/O devices are mapped to (associated with) address values.
So when an address is accessed by the CPU, it may refer to a portion of physical RAM, but it
can also refer to memory of the I/O device. Thus, the CPU instructions used to access the
memory can also be used for accessing devices. Each I/O device monitors the CPU's address
bus and responds to any CPU access of an address assigned to that device, connecting the
data bus to the desired device's hardware register. To accommodate the I/O devices, areas of
the addresses used by the CPU must be reserved for I/O and must not be available for normal
physical memory. The reservation may be permanent or temporary.
24
Port-mapped I/O often uses a special class of CPU instructions designed specifically for
performing I/O, such as the in and out instructions found on microprocessors based on the x86
and x86-64 architectures. Different forms of these two instructions can copy one, two or four
bytes (outb, outw and outl, respectively) between the EAX register or one of that register's
subdivisions on the CPU and a specified I/O port which is assigned to an I/O device. I/O devices
have a separate address space from general memory, either accomplished by an extra "I/O" pin
on the CPU's physical interface, or an entire bus dedicated to I/O. Because the address space
for I/O is isolated from that for main memory, this is sometimes referred to as isolated I/O.
Fragmentation
In computer storage, fragmentation is a phenomenon in which storage space is used
inefficiently, reducing capacity or performance and often both. The exact consequences of
fragmentation depend on the specific system of storage allocation in use and the particular form
of fragmentation. In many cases, fragmentation leads to storage space being "wasted", and in
that case the term also refers to the wasted space itself.
There are three different but related forms of fragmentation: external fragmentation, internal
fragmentation, and data fragmentation, which can be present in isolation or conjunction.
Fragmentation is often accepted in return for improvements in speed or simplicity.
Internal fragmentation
Due to the rules governing memory allocation, more computer memory is sometimes allocated
than is needed. For example, memory can only be provided to programs in chunks divisible by
4, 8 or 16, and as a result if a program requests perhaps 23 bytes, it will actually get a chunk of
32 bytes. When this happens, the excess memory goes to waste. In this scenario, the unusable
memory is contained within an allocated region. This arrangement, termed fixed partitions,
suffers from inefficient memory use - any process, no matter how small, occupies an entire
partition. This waste is called internal fragmentation.
External fragmentation
External fragmentation arises when free memory is separated into small blocks and is
interspersed by allocated memory. It is a weakness of certain storage allocation algorithms,
when they fail to order memory used by programs efficiently. The result is that, although free
storage is available, it is effectively unusable because it is divided into pieces that are too small
individually to satisfy the demands of the application. The term "external" refers to the fact that
the unusable storage is outside the allocated regions.
Data fragmentation
Data fragmentation occurs when a collection of data in memory is broken up into many pieces
that are not close together. It is typically the result of attempting to insert a large object into
storage that has already suffered external fragmentation.
Fixed point is a representation of floating point number in integer format. So operations can be
applied on the number just like on integers. The advantage of using this is that floating point
25
arithmetic is costlier (processing power). Newer processors have dedicated FPUs (floating point
units) for handling that.
So fixed point arithmetic is when processing power is limited, and a little precision loss doesn't
cause a havoc.
Why to use –
When a normal function call instruction is encountered, the program stores the memory address
of the instructions immediately following the function call statement, loads the function being
called into the memory, copies argument values, jumps to the memory location of the called
function, executes the function codes, stores the return value of the function, and then jumps
back to the address of the instruction that was saved just before executing the called function.
Too much run time overhead.
The C++ inline function provides an alternative. With inline keyword, the compiler replaces the
function call statement with the function code itself (process called expansion) and then
compiles the entire code. Thus, with inline functions, the compiler does not have to jump to
another location to execute the function, and then jump back as the code of the called function
is already available to the calling program.
Pros :-
1. It speeds up your program by avoiding function calling overhead.
2. It save overhead of variables push/pop on the stack, when function calling happens.
3. It save overhead of return call from a function.
4. It increases locality of reference by utilizing instruction cache.
5. By marking it as inline, you can put a function definition in a header file (i.e. it can be
included in multiple compilation unit, without the linker complaining)
Cons :-
1. It increases the executable size due to code expansion.
2. C++ inlining is resolved at compile time. Which means if you change the code of the
inlined function, you would need to recompile all the code using it to make sure it will be
updated
3. When used in a header, it makes your header file larger with information which users
don’t care.
4. As mentioned above it increases the executable size, which may cause thrashing in
memory. More number of page fault bringing down your program performance.
26
5. Sometimes not useful for example in embedded system where large executable size is
not preferred at all due to memory constraints.
Inline functions are actual functions whose body is directly injected into their call site. They can
only be used where a function call is appropriate.
- Macros are not type safe, and can be expanded regardless of whether they are syntatically
correct - the compile phase will report errors resulting from macro expansion problems.
- Macros can be used in context where you don't expect, resulting in problems
- Macros are more flexible, in that they can expand other macros - whereas inline functions don't
necessarily do this.
- Macros can result in side effects because of their expansion, since the input expressions are
copied wherever they appear in the pattern.
- Inline function are not always guaranteed to be inlined - some compilers only do this in release
builds, or when they are specifically configured to do so. Also, in some cases inlining may not be
possible.
- Inline functions can provide scope for variables (particularly static ones), preprocessor macros
can only do this in code blocks {...}, and static variables will not behave exactly the same way.
There’re two functions available to allocate memory from heap in Linux kernel process,
1. vmalloc
It’s Linux kernel’s version of malloc() function, which is used in user space. Like malloc, the
function allocates virtually contiguous memory that may or may not physically contiguous.
To free the memory space allocated by vmalloc, one simply call vfree().
2. kmalloc
kmalloc allocates a region of physically contiguous (also virtually contiguous) memory and
return the pointer to the allocated memory. It returns NULL when the operation fails.
The behavior of kmalloc is dependent on the second parameter flags. Here only the two most
popular flags are introduce:
27
GFP_KERNEL: this flag indicates a normal kernel memory allocation.
GFP_ATOMIC: this flag indicates the kmalloc function is atomic operation.
To free up the memory allocated by kmalloc, one can use kfree defined as below.
vmalloc allocates virtually contiguous memory space (not necessarily physically contiguous),
while kmalloc allocates physically contiguous memory (also virtually contiguous). Most of the
memory allocations in Linux kernel are done using kmalloc, due to the following reasons:
On many architectures, hardware devices don’t understand virtual address. Therefore, their
device drivers can only allocate memory using kmalloc.
kmalloc has better performance in most cases because physically contiguous memory region is
more efficient than virtually contiguous memory. The reason behind this is not covered here,
interested readers can search for Linux memory management articles.
To make use of this advantage of processor, data are always aligned as 4 bytes package which
leads to insert empty addresses between other member’s address.
Because of this structure padding concept in C, size of the structure is always not same as what
we think.
Structure packing suppresses structure padding, padding used when alignment matters
most, packing used when space matters most.
Packing, on the other hand prevents compiler from doing padding - this has to be explicitly
requested - under GCC it's __attribute__((__packed__)), so the following:
28
adjusts to demand. However, if the maximum stack size has been reached, we have a stack
overflow and the program receives a Segmentation Fault. While the mapped stack area
expands to meet demand, it does not shrink back when the stack gets smaller. Like the federal
budget, it only expands.
29
Dependencies in a pipelined processor
There are mainly three types of dependencies possible in a pipelined processor. These are :
1) Structural Dependency
This dependency arises due to the resource conflict in the pipeline. A resource conflict is a
situation when more than one instruction tries to access the same resource in the same cycle. A
resource can be a register, memory, or ALU.
2) Control Dependency
This type of dependency occurs during the transfer of control instructions such as BRANCH,
CALL, JMP, etc. On many instruction architectures, the processor will not know the target
address of these instructions when it needs to insert the new instruction into the pipeline. Due to
this, unwanted instructions are fed to the pipeline.
solution: To correct the above problem we need to stop the Instruction fetch until we get target
address of branch instruction. This can be implemented by introducing delay slot until we get
the target address.
30
When the above instructions are executed in a pipelined processor, then data dependency
condition will occur, which means that I2 tries to read the data before I1 writes it, therefore, I2
incorrectly gets the old value from I1.
Difference between linked list and array? when to use linked list?
Deletion is also expensive with arrays until unless some special techniques are used. For
example, to delete 1010 in id[], everything after 1010 has to be moved.
31
User mode and kernel mode
In any modern operating system, the CPU is actually spending time in two very distinct modes:
In Kernel mode, the executing code has complete and unrestricted access to the underlying
hardware. It can execute any CPU instruction and reference any memory address. Kernel mode
is generally reserved for the lowest-level, most trusted functions of the operating system.
Crashes in kernel mode are catastrophic; they will halt the entire PC.
User Mode
In User mode, the executing code has no ability to directly access hardware or reference
memory. Code running in user mode must delegate to system APIs to access hardware or
memory. Due to the protection afforded by this sort of isolation, crashes in user mode are
always recoverable. Most of the code running on your computer will execute in user mode.
A buffer is a temporary area for data storage. When more data (than was originally allocated to
be stored) gets placed by a program or system process, the extra data overflows. It causes
some of that data to leak out into other buffers, which can corrupt or overwrite whatever data
they were holding.
In a buffer-overflow attack, the extra data sometimes holds specific instructions for actions
intended by a hacker or malicious user; for example, the data could trigger a response that
damages files, changes data or unveils private information.
Attacker would use a buffer-overflow exploit to take advantage of a program that is waiting on a
user’s input. There are two types of buffer overflows: stack-based and heap-based. Heap-
based, which are difficult to execute and the least common of the two, attack an application by
flooding the memory space reserved for a program. Stack-based buffer overflows, which are
more common among attackers, exploit applications and programs by using what is known as a
stack: memory space used to store user input.
Let us study some real program examples that show the danger of such situations based on the
C.
In the examples, we do not implement any malicious code injection but just to show that the
buffer can be overflow. Modern compilers normally provide overflow checking option during the
compile/link time but during the run time it is quite difficult to check this problem without any
extra protection mechanism such as using exception handling.
32
// Reserve 5 byte of buffer plus the terminating NULL.
// should allocate 8 bytes = 2 double words,
// To overflow, need more than 8 bytes...
char buffer[5]; // If more than 8 characters input
// by user, there will be access
// violation, segmentation fault
return 0;
}
The vulnerability exists because the buffer could be overflowed if the user input (argv[1]) bigger
than 8 bytes. Why 8 bytes? For 32 bit (4 bytes) system, we must fill up a double word (32 bits)
memory. Character (char) size is 1 byte, so if we request buffer with 5 bytes, the system will
allocate 2 double words (8 bytes). That is why when you input more than 8 bytes; the mybuffer
will be over flowed
Similar standard functions that are technically less vulnerable, such as strncpy(), strncat(), and
memcpy(), do exist. But the problem with these functions is that it is the programmer
responsibility to assert the size of the buffer, not the compiler.
Every C/C++ coder or programmer must know the buffer overflow problem before they do the
coding. A lot of bugs generated, in most cases can be exploited as a result of buffer overflow.
33
The functions which are part of standard C library are known as Library functions. For example
the standard string manipulation functions like strcmp(), strlen() etc are all library functions.
The functions which change the execution mode of the program from user mode to kernel mode
are known as system calls. These calls are required in case some services are required by the
program from kernel. For example, if we want to change the date and time of the system or if we
want to create a network socket then these services can only be provided by kernel and hence
these cases require system calls. For example, socket() is a system call.
System calls acts as entry point to OS kernel. There are certain tasks that can only be done if a
process is running in kernel mode. Examples of these tasks can be interacting with hardware
etc. So if a process wants to do such kind of task then it would require itself to be running in
kernel mode which is made possible by system calls.
1) A library function is linked to the user program and executes in user space while a system call
is not linked to a user program and executes in kernel space.
2) A library function execution time is counted in user level time while a system call execution
time is counted as a part of system time.
3) Library functions can be debugged easily using a debugger while System calls cannot be
debugged as they are executed by the kernel.
First Come First Serve (FCFS): Simplest scheduling algorithm that schedules according to
arrival times of processes.
Shortest Job First(SJF): Process which have the shortest burst time are scheduled first.
Shortest Remaining Time First(SRTF): It is preemptive mode of SJF algorithm in which jobs
are schedule according to shortest remaining time.
34
Round Robin Scheduling: Each process is assigned a fixed time in cyclic way.
Priority Based scheduling (Non Preemptive): In this scheduling, processes are scheduled
according to their priorities, i.e., highest priority process is schedule first. If priorities of two
processes match, then schedule according to arrival time.
Highest Response Ratio Next (HRRN) In this scheduling, processes with highest response
ratio is scheduled. This algorithm avoids starvation.
Multi level Feedback Queue Scheduling: It allows the process to move in between queues.
The idea is to separate processes according to the characteristics of their CPU bursts. If a
process uses too much CPU time, it is moved to a lower-priority queue.
1) FCFS can cause long waiting times, especially when the first job takes too much CPU time.
2) Both SJF and Shortest Remaining time first algorithms may cause starvation. Consider a
situation when long process is there in ready queue and shorter processes keep coming.
3) If time quantum for Round Robin scheduling is very large, then it behaves same as FCFS
scheduling.
4) SJF is optimal in terms of average waiting time for a given set of processes,i.e., average
waiting time is minimum with this scheduling, but problems is, how to know/predict time of next
job.
Memory Management
1: Single Partition Allocation Schemes: The memory is divided into two parts. One part is kept
for use by the OS and the other for use by the users.
35
Paging
Paging is a memory management scheme that eliminates the need for contiguous allocation of
physical memory. This scheme permits the physical address space of a process to be non –
contiguous.
The mapping from virtual to physical address is done by the memory management unit (MMU)
which is a hardware device and this mapping is known as paging technique.
The Physical Address Space is conceptually divided into a number of fixed-size blocks, called
frames.
The Logical address Space is also splitted into fixed-size blocks, called pages.
Page Size = Frame Size
Segmentation
A Memory Management technique in which memory is divided into variable sized chunks which
can be allocated to processes. Each chunk is called a Segment.
Base Address: It contains the starting physical address where the segments reside in memory.
Limit: It specifies the length of the segment.
Paging is the concept used to implement virtual memory in the system, where some
portion of secondary memory is divided into equal size pages which can be swap by the same
size frames from the mail memory at the time of page fault , so that ,that portion of secondary
memory can act like main memory of the system.
Segmentation is totally different concept in which physical memory or RAM is divided into
variable size logical segments which belongs to the same module. Segmentation is done
on the basis of user's requirement.
Initially all slots are empty, so when 1, 3, 0 came they are allocated to the empty slots —> 3
Page Faults.
when 3 comes, it is already in memory so —> 0 Page Faults.
Then 5 comes, it is not available in memory so it replaces the oldest page slot i.e 1. —>1 Page
Fault.
Finally 6 comes, it is also not available in memory so it replaces the oldest page slot i.e 3 —>1
Page Fault.
36
In this algorithm page will be replaced which is least recently used.
Belady’s anomaly
Belady’s anomaly proves that it is possible to have more page faults when increasing the
number of page frames while using the First in First Out (FIFO) page replacement algorithm.
For example, if we consider reference string 3 2 1 0 3 2 4 3 2 1 0
4 and 3 slots, we get 9 total page faults, but if we increase slots to 4, we get 10 page faults.
Page Fault
A page fault is a type of interrupt, raised by the hardware when a running program accesses a
memory page that is mapped into the virtual address space, but not loaded in physical memory.
1. Text Segment:
A text segment , also known as a code segment or simply as text,
is one of the sections of a program in an object file or in memory,
which contains executable instructions.
Note that, data segment is not read-only, since the values of the variables can be altered at run
time.
This segment can be further classified into initialized read-only area and initialized read-write
area.
For instance the global string defined by char s[] = “hello world” in C and a C statement like int
debug=1 outside the main (i.e. global) would be stored in initialized read-write area. And a
global C statement like const char* string = “hello world” makes the string literal “hello world” to
be stored in initialized read-only area and the character pointer variable string in initialized read-
write area.
37
Ex: static int i = 10 will be stored in data segment and global int i = 10 will also be stored in data
segment
For instance a variable declared static int i; would be contained in the BSS segment.
For instance a global variable declared int j; would be contained in the BSS segment.
4. Stack:
The stack area traditionally adjoined the heap area and grew the opposite direction; when the
stack pointer met the heap pointer, free memory was exhausted. (With modern large address
spaces and virtual memory techniques they may be placed almost anywhere, but they still
typically grow opposite directions.)
The stack area contains the program stack, a LIFO structure, typically located in the higher
parts of memory. On the standard PC x86 computer architecture it grows toward address zero;
on some other architectures it grows the opposite direction. A “stack pointer” register tracks the
top of the stack; it is adjusted each time a value is “pushed” onto the stack. The set of values
pushed for one function call is termed a “stack frame”; A stack frame consists at minimum of a
return address.
Stack, where automatic variables are stored, along with information that is saved each time a
function is called. Each time a function is called, the address of where to return to and certain
information about the caller’s environment, such as some of the machine registers, are saved
on the stack. The newly called function then allocates room on the stack for its automatic and
temporary variables. This is how recursive functions in C can work. Each time a recursive
function calls itself, a new stack frame is used, so one set of variables doesn’t interfere with the
variables from another instance of the function.
5. Heap:
Heap is the segment where dynamic memory allocation usually takes place.
The heap area begins at the end of the BSS segment and grows to larger addresses from
[Link] Heap area is managed by malloc, realloc, and free, which may use the brk and sbrk
system calls to adjust its size (note that the use of brk/sbrk and a single “heap area” is not
required to fulfill the contract of malloc/realloc/free; they may also be implemented using mmap
to reserve potentially non-contiguous regions of virtual memory into the process’ virtual address
space). The Heap area is shared by all shared libraries and dynamically loaded modules in a
process.
38
The capability of a class to derive properties and characteristics from another class is called
Inheritance. Inheritance is one of the most important feature of Object Oriented Programming.
Sub Class: The class that inherits properties from another class is called Sub class or Derived
Class.
Super Class:The class whose properties are inherited by sub class is called Base Class or
Super class.
Register variables are a special case of automatic variables. Automatic variables are allocated
storage in the memory of the computer; however, for most computers, accessing data in
memory is considerably slower than processing in the CPU. These computers often have small
amounts of storage within the CPU itself where data can be stored and accessed quickly. These
storage cells are called registers.
Normally, the compiler determines what data is to be stored in the registers of the CPU at what
times. However, the C language provides the storage class register so that the programmer can
``suggest'' to the compiler that particular automatic variables should be allocated to CPU
registers, if possible. Thus, register variables provide a certain control over efficiency of program
execution. Variables which are used repeatedly or whose access times are critical, may be
declared to be of storage class register.
The register class designation is merely a suggestion to the compiler. Not all implementations
will allocate storage in registers for these variables, depending on the number of registers
available for the particular computer, or the use of these registers by the compiler. They may be
treated just like automatic variables and provided storage in memory.
Finally, even the availability of register storage does not guarantee faster execution of the
program. For example, if too many register variables are declared, or there are not enough
registers available to store all of them, values in some registers would have to be moved
to temporary storage in memory in order to clear those registers for other variables.
Thus, much time may be wasted in moving data back and forth between registers and memory
locations. In addition, the use of registers for variable storage may interfere with other uses of
registers by the compiler, such as storage of temporary values in expression evaluation. In the
end, use of register variables could actually result in slower execution. Register variables should
only be used if you have a detailed knowledge of the architecture and compiler for the computer
you are using. It is best to check the appropriate manuals if you should need to use register
variables.
What is thrashing?
Initial degree of multi programming upto some extent of point(lamda), the CPU utilization is very
high and the system resources are utilized 100%. But if we further increase the degree of multi
programming the CPU utilization will drastically fall down and the system will spent more time
39
only in the page replacement and the time taken to complete the execution of the process
will increase. This situation in the system is called as thrashing.
Causes of Thrashing :
1). High degree of multiprogramming : If the number of processes keeps on increasing in the
memory than number of frames allocated to each process will be decreased. So, less number of
frames will be available to each process. Due to this, page fault will occur more frequently and
more CPU time will be wasted in just swapping in and out of pages and the utilization will keep
on decreasing.
For example:
Let free frames = 400
Case 1: Number of process = 100
Then, each process will get 4 frames.
2). Lacks of Frames:If a process has less number of frames then less pages of that process will
be able to reside in memory and hence more frequent swapping in and out will be required. This
may lead to thrashing. Hence sufficient amount of frames must be allocated to each process in
order to prevent thrashing.
What is Cache?
A cache is a smaller, faster memory, closer to a processor core, which stores copies of the data
from frequently used main memory locations. Most CPUs have different independent caches,
including instruction and data caches, where the data cache is usually organized as a hierarchy
of more cache levels (L1, L2, etc.).
When the processor needs to read from or write to a location in main memory, it first checks
whether a copy of that data is in the cache. If so, the processor immediately reads from or writes
to the cache, which is much faster than reading from or writing to main memory.
Most modern desktop and server CPUs have at least three independent caches: an instruction
cache to speed up executable instruction fetch, a data cache to speed up data fetch and
store, and a translation lookaside buffer (TLB) used to speed up virtual-to-physical
address translation for both executable instructions and data. A single TLB could be
provided for access to both instructions and data, or a separate Instruction TLB (ITLB) and data
TLB (DTLB) can be provided.[4] The data cache is usually organized as a hierarchy of more
cache levels (L1, L2, etc.; see also multi-level caches below). However, the TLB cache is part
of the memory management unit (MMU) and not directly related to the CPU caches.
40
Cache size:
L1 - 32 KB (8 ~ 64 KB), L2 - 256 or 512 KB, L3 - 8 MB
Draw the block diagram of a computer and explain? How will you make
a computer?
InputUnit:
Computers need to receive data and instruction in order to solve any problem. Therefore we
need to input the data and instructions into the computers. The input unit consists of one or
more input devices. Keyboard is the one of the most commonly used input device. Other
commonly used input devices are the mouse, floppy disk drive, magnetic tape, etc. All the input
devices perform the following functions.
41
• Convert it to a form that the computer can understand.
• Supply the converted data to the computer system for further processing.
Storage Unit:
The storage unit of the computer holds data and instructions that are entered through the input
unit, before they are processed. It preserves the intermediate and final results before these are
sent to the output devices. It also saves the data for the later use. The various storage devices
of a computer system are divided into two categories.
1. Primary Storage: Stores and provides very fast. This memory is generally used to hold the
program being currently executed in the computer, the data being received from the input
unit, the intermediate and final results of the program. The primary memory is temporary in
nature. The data is lost, when the computer is switched off. In order to store the data
permanently, the data has to be transferred to the secondary memory. The cost of the
primary storage is more compared to the secondary storage. Therefore most computers
have limited primary storage capacity.
2. Secondary Storage: Secondary storage is used like an archive. It stores several programs,
documents, data bases etc. The programs that you run on the computer are first transferred
to the primary memory before it is actually run. Whenever the results are saved, again they
get stored in the secondary memory. The secondary memory is slower and cheaper than the
primary memory. Some of the commonly used secondary memory devices are Hard disk,
CD, etc.,
OutputUnit:
The output unit of a computer provides the information and results of a computation to outside
world. Printers, Visual Display Unit (VDU) are the commonly used output devices. Other
commonly used output devices are floppy disk drive, hard disk drive, and magnetic tape drive.
ArithmeticLogicalUnit:
All calculations are performed in the Arithmetic Logic Unit (ALU) of the computer. It also does
comparison and takes decision. The ALU can perform basic operations such as addition,
subtraction, multiplication, division, etc and does logic operations viz, >, <, =, ‘etc. Whenever
calculations are required, the control unit transfers the data from storage unit to ALU once the
computations are done, the results are transferred to the storage unit by the control unit and
then it is send to the output unit for displaying results.
ControlUnit:
It controls all other units in the computer. The control unit instructs the input unit, where to store
the data after receiving it from the user. It controls the flow of data and instructions from the
storage unit to ALU. It also controls the flow of results from the ALU to the storage unit. The
control unit is generally referred as the central nervous system of the computer that control and
synchronizes its working.
Spinlocks
42
means that acquiring a spinlock is a "busy" operation that possibly burns CPU cycles for a long
time (maybe forever!) while it effectively achieves “nothing".
The main incentive for such an approach is the fact that a context switch has an overhead
equivalent to spinning a few hundred (or maybe thousand) times, so if a lock can be
acquired by burning a few cycles spinning, this may overall very well be more efficient.
Also, for realtime applications it may not be acceptable to block and wait for the scheduler to
come back to them at some far away time in the future.
A spin-lock is usually used when there is low contention for the resource and the CPU will
therefore only make a few iterations before it can move on to do productive work. The
regular lock is used if the resource cannot be acquired in a reasonable time-frame. This is
done to reduce the overhead with context switches in settings where locks usually are
quickly obtained.
It performs very lightweight locking/unlocking but if the locking thread will be preempted by
other which will try to access the same resource the second one will simply try to acquire
resource until it run out of it CPU quanta.
Spin locks are a low-level synchronization mechanism suitable primarily for use on shared
memory multiprocessors.
Spinlocks are, by their nature, intended for use on multiprocessor systems, although a
uniprocessor workstation running a preemptive kernel behaves like SMP, as far as concurrency
is concerned. If a nonpreemptive uniprocessor system ever went into a spin on a lock, it
would spin forever; no other thread would ever be able to obtain the CPU to release the
lock. For this reason, spinlock operations on uniprocessor systems without preemption enabled
are optimized to do nothing, with the exception of the ones that change the IRQ masking status.
Because of preemption, even if you never expect your code to run on an SMP system, you still
need to implement proper locking.
Context of a process
The context of a process includes its address space, stack space, virtual address space,
register set image (e.g. Program Counter (PC), Stack Pointer (SP), Instruction Register
(IR), Program Status Word (PSW) and other general processor registers), updating
43
profiling or accounting information, making a snapshot image of its associated kernel
data structures and updating the current state of the process (waiting, ready, etc).
This state information is saved in the process's process control block which is then moved to the
appropriate scheduling queue. The new process is moved to the CPU by copying the PCB info
into the appropriate locations (e.g. the program counter is loaded with the address of the next
instruction to execute).
Context switching
Switching of CPU to another process means saving the state of old process and loading saved
state for new process. Context switching involved mode switch, because it can only occur
in kernel mode.
In Context Switching the process is stored in the Process Control Block to serve the new
process, so that old process can be resumed from the same part it was left.
Switching from one process to another requires a certain amount of time for doing the
administration – saving and loading registers and memory maps, updating various tables and
lists, etc. What is actually involved in a context switch varies between these senses and
between processors and operating systems. For example, in the Linux kernel, context switching
involves switching registers, stack pointer, and program counter, but is independent of
address space switching, though in a process switch an address space switch also happens.
44
safety property which ensures that operations do not complete in an unexpected or
unpredictable manner.
Lock-free operations:
The implication is that multiple threads can access the data structure concurrently without race
conditions or data corruption, even though there are no locks.
A binary search tree is a rooted binary tree, whose internal nodes each store a key (and
optionally, an associated value) and each have two distinguished sub-trees, commonly denoted
left and right. The tree additionally satisfies the binary search tree property, which states that the
key in each node must be greater than or equal to any key stored in the left sub-tree, and less
than or equal to any key stored in the right sub-tree.
The major advantage of binary search trees over other data structures is that the related sorting
algorithms and search algorithms such as in-order traversal can be very efficient; they are also
easy to code.
45
What is recursion? What actually happens during recursion? does the
memory get stored on stack? what gets called and how does the
program know from where to call?
Each time a function is called, the address of where to return to and certain information about
the caller’s environment, such as some of the machine registers, are saved on the stack. The
newly called function then allocates room on the stack for its automatic and temporary variables.
This is how recursive functions in C can work. Each time a recursive function calls itself, a
new stack frame is used, so one set of variables doesn’t interfere with the variables from
another instance of the function.
What is free()? how does free know how much memory to de-allocate?
When you call malloc(), you specify the amount of memory to allocate. The amount of memory
actually used is slightly more than this, and includes extra information that records (at least)
how big the block is. You can't (reliably) access that other information - and nor should you :-).
When you call free(), it simply looks at the extra information to find out how big the block is.
what is the difference between class and object? does class or object
create memory?
Many programmers still get confused by the difference between class and object. In object-
oriented terminology, a Class is a template for Objects and every Object must belong to a Class.
The terms "Class" and "Object" are related to one another and each term holds its own distinct
meaning.
46
A class is a construct that defines a collection of properties and methods in a single unit,
which does not change during the execution of a program. Objects are created and eventually
destroyed during the execution of a program, so they only live in the program for a short time.
While objects are "living" their attributes may also be changed at execution of a program.
Every object belongs to a class and every class contains one or more related objects (instance
of a class). That means, a Class is created once and Object is created from the same Class
many time as they require. There is no memory space allocation for a Class when it is
crated, while memory space is allocated for an Object when it is created.
- i++ makes a copy, increases i, and returns the copy (old value).
- ++i increases i, and returns i.
In your example it is all about speed. ++i will be the faster than i++ since it doesn't make a copy.
However a compiler will probably optimize it away since you are not storing the returned value
from the increment operator in your example, but this is only possible for fundamental types like
a int.
Virtual functions allow us to create a list of base class pointers and call methods of any of
the derived classes without even knowing kind of derived class object. For example,
consider a employee management software for an organization, let the code has a simple base
class Employee , the class contains virtual functions like raiseSalary(), transfer(), promote(),..
etc. Different types of employees like Manager, Engineer, ..etc may have their own
implementations of the virtual functions present in base class Employee. In our complete
software, we just need to pass a list of employees everywhere and call appropriate functions
without even knowing the type of employee. For example, we can easily raise salary of all
employees by iterating through list of employees. Every type of employee may have its own
logic in its class, we don’t need to worry because if raiseSalary() is present for a specific
employee type, only that function would be called.
Class with pure virtual function is abstract base class which cannot create a object of its
own. But its derived classes can still use the polymorphism feature. A class that declares or
inherits a virtual function is called a polymorphic class.
47
What is the difference between the stack and the heap
Stack is used for static memory allocation and Heap for dynamic memory allocation, both
stored in the computer's RAM .
Variables allocated on the stack are stored directly to the memory and access to this
memory is very fast, and it's allocation is dealt with when the program is compiled. When
a function or a method calls another function which in turns calls another function etc., the
execution of all those functions remains suspended until the very last function returns its value.
The stack is always reserved in a LIFO order, the most recently reserved block is always the
next block to be freed. This makes it really simple to keep track of the stack, freeing a block
from the stack is nothing more than adjusting one pointer.
Variables allocated on the heap have their memory allocated at run time and accessing this
memory is a bit slower, but the heap size is only limited by the size of virtual memory .
Element of the heap have no dependencies with each other and can always be accessed
randomly at any time. You can allocate a block at any time and free it at any time. This makes it
much more complex to keep track of which parts of the heap are allocated or free at any given
time.
You can use the stack if you know exactly how much data you need to allocate before compile
time and it is not too big. You can use heap if you don't know exactly how much data you will
need at runtime or if you need to allocate a lot of data.
In a multi-threaded situation each thread will have its own completely independent stack but
they will share the heap. Stack is thread specific and Heap is application specific. The stack is
important to consider in exception handling and thread executions.
Stack Overflow
48
Difference between CDMA and GSM technologies
1. Technology
The CDMA is based on spread spectrum technology which makes the optimal use of
available bandwidth. It allows each user to transmit over the entire frequency spectrum all the
time. On the other hand GSM operates on the wedge spectrum called a carrier. This carrier is
divided into a number of time slots and each user is assigned a different time slot so that until
the ongoing call is finished, no other subscriber can have access to this. GSM uses both Time
Division Multiple Access (TDMA) and Frequency Division Multiple Access (FDMA) for user
and cell separation. TDMA provides multiuser access by chopping up the channel into different
time slices and FDMA provides multiuser access by separating the used frequencies.
2. Security
More security is provided in CDMA technology as compared with the GSM technology as
encryption is inbuilt in the CDMA. A unique code is provided to every user and all the
conversation between two users are encoded ensuring a greater level of security for CDMA
users. The signal cannot be detected easily in CDMA as compared to the signals of GSM, which
are concentrated in the narrow bandwidth. Therefore, the CDMA phone calls are more secure
than the GSM calls. In terms of encryption the GSM technology has to be upgraded so as to
make it operate more securely.
What is 3G LTE
In telecommunication, Long-Term Evolution (LTE) is a standard for high-speed wireless
communication for mobile devices and data terminals, based on the GSM/EDGE and UMTS/
HSPA technologies. It increases the capacity and speed using a different radio interface
together with core network improvements.[1][2] The standard is developed by the 3GPP (3rd
Generation Partnership Project) and is specified in its Release 8 document series, with minor
enhancements described in Release 9. LTE is the upgrade path for carriers with both GSM/
UMTS networks and CDMA2000 networks. The different LTE frequencies and bands used in
different countries mean that only multi-band phones are able to use LTE in all countries where
it is supported.
LTE is commonly marketed as 4G LTE, but it does not meet the technical criteria of a 4G
wireless service, as specified in the 3GPP Release 8 and 9 document series, for LTE Advanced.
The requirements were originally set forth by the ITU-R organization in the IMT Advanced
specification. However, due to marketing pressures and the significant advancements that
WiMAX, Evolved High Speed Packet Access and LTE bring to the original 3G technologies, ITU
later decided that LTE together with the aforementioned technologies can be called 4G
technologies.[3] The LTE Advanced standard formally satisfies the ITU-R requirements to be
49
considered IMT-Advanced.[4] To differentiate LTE Advanced and WiMAX-Advanced from current
4G technologies, ITU has defined them as "True 4G”.
JTAG
The Joint Test Action Group (JTAG) is an electronics industry association formed in 1985 for
developing a method of verifying designs and testing printed circuit boards after manufacture. In
1990 the Institute of Electrical and Electronics Engineers codified the results of the effort in
IEEE Standard 1149.1-1990, entitled Standard Test Access Port and Boundary-Scan
Architecture.
JTAG implements standards for on-chip instrumentation in electronic design automation (EDA)
as a complementary tool to digital simulation.[1] It specifies the use of a dedicated debug port
implementing a serial communications interface for low-overhead access without requiring direct
external access to the system address and data buses. The interface connects to an on-chip
test access port (TAP) that implements a stateful protocol to access a set of test registers that
present chip logic levels and device capabilities of various parts.
50
– A clock signal named SCLK, sent from the bus master to all slaves; all the SPI signals are
synchronous to this clock signal;
– A slave select signal for each slave, SSn, used to select the slave the master communicates
with;
– A data line from the master to the slaves, named MOSI (Master Out-Slave In)
– A data line from the slaves to the master, named MISO (Master In-Slave Out).
– 7-bits slave addresses: each device connected to the bus has got such a unique address;
– data divided into 8-bit bytes
– a few control bits for controlling the communication start, end, direction and for an
acknowledgment mechanism.
The data rate has to be chosen between 100 kbps, 400 kbps and 3.4 Mbps, respectively called
standard mode, fast mode and high speed mode. Some I²C variants include 10 kbps (low speed
mode) and 1 Mbps (fast mode +) as valid speeds.
Difference
SPI has higher throughput.
SPI has simple receiver hardware -> simple shift registers
SPI supports multiple slaves
SPI has lower power consumption
I2C allows multiple masters and slaves on the bus. On the other hand SPI can only work with
one master device controlling multiple slaves. In both I2C and SPI the master device controls
the clock for all slaves, but an I2C slave device can modify the main bus clock.
TCP and UDP are both transport layer protocols. TCP is connection oriented but UDP is not.
It means TCP has handshake mechanism that ensures proper TCP header parameters are set
before the communication starts resulting in a reliable communication(error and flow control).
Whereas no handshake takes place between machines communicating with UDP protocol
resulting in unreliable serivice. TCP is used for file/doc transfer. But UDP is used for video/audio
transfer.
51
Transmission Control Protocol (TCP) is a connection oriented protocol, which means the
devices should open a connection before transmitting data and should close the
connection gracefully after transmitting the data.
Transmission Control Protocol (TCP) assure reliable delivery of data to the destination.
Delivery of data is guaranteed if you are using Transmission Control Protocol (TCP).
Transmission Control Protocol (TCP) is comparatively slow because of these extensive error
checking mechanism.
Multiplexing and Demultiplexing is possible in Transmission Control Protocol (TCP) using TCP
port numbers.
User Datagram Protocol (UDP) is Datagram oriented protocol with no overhead for opening a
connection (using three-way handshake), maintaining a connection, and closing (terminating) a
connection.
52
User Datagram Protocol (UDP) is efficient for broadcast/multicast type of network
transmission.
User Datagram Protocol (UDP) has only the basic error checking mechanism using checksums.
User Datagram Protocol (UDP) is faster, simpler and more efficient than TCP. However,
User Datagram Protocol (UDP) it is less robust then TCP.
Multiplexing and Demultiplexing is possible in User Datagram Protocol (UDP) using UDP port
numbers.
What is IP protocol?
The Internet Protocol (IP) is the principal communications protocol in the Internet protocol suite
for relaying datagrams across network boundaries. Its routing function enables internet working,
and essentially establishes the Internet.
IP has the task of delivering packets from the source host to the destination host solely based
on the IP addresses in the packet headers. For this purpose, IP defines packet structures that
encapsulate the data to be delivered. It also defines addressing methods that are used to label
the datagram with source and destination information.
53
IPv4 vs. IPv6
IP v6 was developed by Internet Engineering Task Force (IETF) to deal with the problem of IP
v4 exhaustion. IP v6 is 128-bits address having an address space of 2^128, which is way bigger
than IPv4. (40 byte header size)
Difference:
- 32-bit address vs 128-bit address
54
- Less header fields
- No checksum
- No fragmentation in forwarding routers
- Address in Hex rather than decimal
Advantage:
1. More Efficient Routing
IPv6 reduces the size of routing tables and makes routing more efficient and hierarchical.
IPv6 allows ISPs to aggregate the prefixes of their customers' networks into a single prefix and
announce this one prefix to the IPv6 Internet. In addition, in IPv6 networks, fragmentation is
handled by the source device, rather than the router, using a protocol for discovery of the
path's maximum transmission unit (MTU).
6. Security
IPSec, which provides confidentiality, authentication and data integrity, is baked into in IPv6.
Because of their potential to carry malware, IPv4 ICMP packets are often blocked by corporate
firewalls, but ICMPv6, the implementation of the Internet Control Message Protocol for IPv6,
may be permitted because IPSec can be applied to the ICMPv6 packets.
55
What is the network layer structure?
OS Composition?
• Process management
• I/O management
• Main Memory management
• File & Storage Management
• Protection
• Networking
• Command Interpreter
56
What is the difference between struct and union in C?
A structure is a user-defined data type available in C that allows to combining data items of
different kinds. Structures are used to represent a record.
A union is a special data type available in C that allows storing different data types in the same
memory location. You can define a union with many members, but only one member can
contain a value at any given time. Unions provide an efficient way of using the same memory
location for multiple purposes.
57
have O(NlogN) average complexity but the constants differ. For arrays, merge sort loses due to
the use of extra O(N) storage space.
Most practical implementations of Quick Sort use randomized version. The randomized version
has expected time complexity of O(nLogn). The worst case is possible in randomized version
also, but worst case doesn’t occur for a particular pattern (like sorted array) and randomized
Quick Sort works well in practice.
Quick Sort is also a cache friendly sorting algorithm as it has good locality of reference when
used for arrays.
Quick Sort is also tail recursive, therefore tail call optimizations is done.
In arrays, we can do random access as elements are continuous in memory. Let us say we
have an integer (4-byte) array A and let the address of A[0] be x then to access A[i], we can
directly access the memory at (x + i*4). Unlike arrays, we can not do random access in linked
list. Quick Sort requires a lot of this kind of access. In linked list to access i’th index, we
have to travel each and every node from the head to i’th node as we don’t have continuous
block of memory. Therefore, the overhead increases for quick sort. Merge sort accesses data
sequentially and the need of random access is low.
In this we have one base form and other are overridden from the base form
It is the ability of object to take different forms of objects example: function overloading ,function
overriding, virtual functions.
In this same operation has different affect on object. Example '+' for number is addition and '+'
for string is concatenation.
There are two types of polymorphism:- compile time (operator and function overloading) and run
time(virtual functions)
58
3. As a sentinel value.
Protected members are accessible in the class that defines them and in classes that inherit from
that class.
Edit: Both are also accessible by friends of their class, and in the case of protected members,
by friends of their derived classes.
Edit 2: Use whatever makes sense in the context of your problem. You should try to make
members private whenever you can to reduce coupling and protect the implementation of the
base class, but if that's not possible then use protected members. Check C++ FAQ for a better
understanding of the issue. This question about protected variables might also help.
Friends class/function
A friend class can access private and protected members of other class in which it is declared
as friend. It is sometimes useful to allow a particular class to access private members of other
class. For example a LinkedList class may be allowed to access private members of Node.
A pure virtual function (or abstract function) in C++ is a virtual function for which we don’t have
implementation, we only declare it. A pure virtual function is declared by assigning 0 in
declaration. See the following example.
// An abstract class
class Test
{
// Data members of class
public:
// Pure Virtual Function
virtual void show() = 0;
/* Other members */
};
59
Function Overloading and Overriding
Function Overloading:
It provides multiple definitions of the function by changing signature i.e changing number of
parameters, change datatype of parameters, return type doesn’t play anyrole.
Function overloading can be considered as an example of polymorphism feature in C++.
example:
void area(int a);
void area(int a, int b);
Function Overriding:
It is the redefinition of base class function in its derived class with same signature i.e return type
and parameters.
Preemptive multitasking allows an operating system to switch between software programs. This
in turn allows multiple programs to run without necessarily taking complete control over the
processor and resulting in system crashes.
Preemptive multitasking helps prevent a program from taking complete control of the computer
processor and allows multiple programs to continue to operate without crashing
Compiler converts a C program into an executable. There are four phases for a C program to
become an executable:
Pre-processing
Compilation
Assembly
Linking
Pre-processing
This is the first phase through which source code is passed. This phase include:
Removal of Comments
Expansion of Macros
60
Expansion of the included files.
Compiling
The next step is to compile filename.i and produce an; intermediate compiled output file
filename.s. This file is in assembly level instructions.
Assembly
In this phase the filename.s is taken as input and turned into filename.o by assembler. This file
contain machine level instructions. At this phase, only existing code is converted into machine
language, the function calls like printf() are not resolved.
Linking
This is the final phase in which all the linking of function calls with their definitions are done.
Linker knows where all these functions are implemented. Linker does some extra work also, it
adds some extra code to our program which is required when the program starts and ends. For
example, there is a code which is required for setting up the environment like passing command
line arguments. This task can be easily verified by using $size filename.o and $size filename.
Through these commands, we know that how output file increases from an object file to an
executable file. This is because of the extra code that linker adds with our program.
Coding Questions
return reverse_num;
}
Method 2:
61
uint32_t reverseBits(uint32_t n) {
uint32_t m = 0;
for (int i = 0; i < 8; i++, n >>= 1) {
m <<= 1;
m |= n & 1;
}
return m;
}
C: (reverse the whole sentence and then reverse words one by one)
#include<stdio.h>
62
if (*temp == '\0')
{
reverse(word_begin, temp-1);
}
else if(*temp == ' ')
{
reverse(word_begin, temp-1);
word_begin = temp+1;
}
} /* End of while */
/* UTILITY FUNCTIONS */
/*Function to reverse any sequence starting with pointer
begin and ending with pointer end */
void reverse(char *begin, char *end)
{
char temp;
while (begin < end)
{
temp = *begin;
*begin++ = *end;
*end-- = temp;
}
}
class Solution {
public:
int SetBits(uint32_t n) {
int count = 0;
while (n){
count += n & 1;
n = n >> 1;
}
return count;
}
};
63
Subtraction of 1 from a number toggles all the bits (from right to left) till the rightmost set
bit(including the righmost set bit). So if we subtract a number by 1 and do bitwise & with itself (n
& (n-1)), we unset the righmost set bit. If we do n & (n-1) in a loop and count the no of times
loop executes we get the set bit count.
Beauty of the this solution is number of times it loops is equal to the number of set bits in a
given integer.
class Solution {
public:
int hammingWeight(uint32_t n) {
unsigned int count = 0;
while (n)
{
n &= (n-1) ;
count++;
}
return count;
}
};
if (numToRound < 0)
return -(abs(numToRound) - remainder);
else
return numToRound + multiple - remainder;
}
V2:
int roundUp(int numToRound, int multiple)
{
assert(multiple);
int isPositive = (int)(numToRound >= 0);
return ((numToRound + isPositive * (multiple - 1)) / multiple) * multiple;
}
64
Aligned Malloc and aligned free (memalign)
#include <stdlib.h>
#include <stdio.h>
65
Reverse string, reverse words in a string, find duplicates in an array
(QualComm)
ReverseString:
class Solution {
public:
string reverseString(string s) {
int beg = 0;
int end = [Link]()- 1;
while (beg < end)
{
std::swap(s[beg],s[end]);
beg++;
end --;
}
return s;
}
};
method 1:
class Solution {
public:
void deleteNode(ListNode* node) {
if (node == NULL)
return;
node->val = node->next->val;
node->next = node->next->next;
}
};
method 2:
66
2) Again, scan the string from left to right and check for count of each
character, if you find an element who's count is 1, return it.
int firstUniqChar(char* s) {
int co[26] = {0};
Method 2:
Instead of iterate through the original string, iterate through the storage array
int firstUniqChar(char* s) {
typedef struct count {
int cot; // dont initialize in struct definition
int pos;
} count;
free(co);
return rest == INT_MAX ? 1 : rest;
}
67
};
and the structure of the class is
class Queue {
private:
QueueNode *front;
QueueNode *rear;
public :
void push(int);
int pop();
};
*/
/* The method push to push element into the queue*/
void Queue:: push(int x)
{
// Your Code
QueueNode* new_node = new QueueNode;
new_node->data = x;
new_node->next = NULL;
if (!front) {
front = new_node;
rear = front;
}
else {
rear->next = new_node;
rear = new_node;
}
}
/*The method pop which return the element poped out of the queue*/
int Queue :: pop()
{
// Your Code
if (!front) return -1;
int ret = front->data;
QueueNode* del = front;
front = front->next;
delete del;
return ret;
}
Implement Strlen
int my_strlen(char *s)
{
char *p=s;
while(*p!='\0')
p++;
68
return(p-s);
}
return(length);
}
Method 2:
#include <stdio.h>
int main()
{
int x = 10, y = 5;
return 0;
}
69
return count == 1;
}
or
class Solution {
public:
bool isPowerOfTwo(int n) {
if (n <= 0) return false;
bitset<32> digits(n);
return [Link]() == 1;
}
};
Method 2: (recursion)
class Solution {
public:
bool isPowerOfTwo(int n) {
if (n == 1) return true;
if (n%2 != 0 || n == 0) return false;
return isPowerOfTwo(n/2);
}
};
70
}
head = fir;
return head;
}
};
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
/**
use faster and lower runner solution. (2 pointers)
the faster one move 2 steps, and slower one move only one step.
if there's a circle, the faster one will finally "catch" the slower one.
(the distance between these 2 pointers will decrease one every time.)
if there's no circle, the faster runner will reach the end of linked list. (NULL)
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head == NULL || head -> next == NULL)
return false;
return false;
}
};
71
Given a list from 1 to 100, name all the different ways you can
determine if there are duplicates. Which is the most efficient?
Method 1:
use array of size 100. O(n) speed, O(n) space
Method 2:
use hash table such as unordered_map<int, int>. similar to approach 1. O(n) speed, O(n) space
Method 3:
sort the array and check whether adjacent elements are the same. O(nlogn) speed, O(1) space
Method 4:
create a bit set: bitset<100>, iterate through the array set a bit to 1 to that number position in the
bit set, if already set, then we found the duplicate. O(n) speed, O(1) ? space.
bitset<100> bit_check;
for (auto i : array){
if (bit_check[i] == 1) found duplicate!
bit_ckeck[i] ^= 1;
}
Insertion in BST:
struct node
{
int key;
struct node *left, *right;
72
};
Search in BST:
struct node* search(struct node* root, int key)
{
// Base Cases: root is null or key is present at root
if (root == NULL || root->key == key)
return root;
73
return search(root->left, key);
}
Implement strcpy function and show me if there are any limitation of this
function. what if the 2 buffers passed to the strcpy function overlaps ?
Method1:
The strcpy function copies src, including the terminating null character, to the location
specified by dst. No overflow checking is performed when strings are copied or
appended. The behavior of strcpy is undefined if the source and destination strings
overlap. It returns the destination string. No return value is reserved to indicate an error.
Notice the const for the source, which signifies that the function must not change the
source string in anyway!.
Method2:
74
public:
int strStr(string haystack, string needle) {
int m = [Link](), n = [Link]();
if (!n) return 0;
for (int i = 0; i < m - n + 1; i++) {
int j = 0;
for (; j < n; j++)
if (haystack[i + j] != needle[j])
break;
if (j == n) return i;
}
return -1;
}
};
}
};
75
Write a program to convert a given single Linked list to BST
class Solution {
public:
TreeNode *sortedListToBST(ListNode *head)
{
return sortedListToBST( head, NULL );
}
private:
TreeNode *sortedListToBST(ListNode *head, ListNode *tail)
{
if( head == tail )
return NULL;
if( head->next == tail ) //
{
TreeNode *root = new TreeNode( head->val );
return root;
}
ListNode *mid = head, *temp = head;
while( temp != tail && temp->next != tail ) //
{
mid = mid->next;
temp = temp->next->next;
}
TreeNode *root = new TreeNode( mid->val );
root->left = sortedListToBST( head, mid );
root->right = sortedListToBST( mid->next, tail );
return root;
}
};
The behaviour of memcpy is undefined if the source and destination buffers overlap[1]. Use
memmove instead where there is a risk of this happening.
76
What is memmove()?
The trick here is to use a temp array instead of directly copying from src to dest. The use of
temp array is important to handle cases when source and destination addresses are
overlapping.
delete [] temp;
}
int Fibo(n)
{
if(n==0 || n==1)
return n;
return(fibo(n-1)+fibo(n-2));
}
Time Complexity :
fibo(n)
||
77
n-level-Complete
Binary tree [upper bound]
||
2^n -1 nodes
||
||
O(2^n)
Palindrome Check
class Solution {
public:
bool isPalindrome(int x) {
if(x<0 || (x!=0 && x%10==0)) return false;
int sum=0;
while(x>sum)
{
sum = sum*10+x%10;
x = x/10;
}
return (x==sum) || (x==sum/10);
}
};
Method 1:
bool isPrime(int n)
{
// Corner case
if (n <= 1) return false;
return true;
}
78
Method 2:
Instead of checking till n, we can check till √n because a larger factor of n must be a
multiple of smaller factor that has been already checked.
The algorithm can be improved further by observing that all primes are of the form 6k ± 1,
with the exception of 2 and 3. This is because all integers can be expressed as (6k + i) for
some integer k and for i = ?1, 0, 1, 2, 3, or 4; 2 divides (6k + 0), (6k + 2), (6k + 4); and 3
divides (6k + 3). So a more efficient method is to test if n is divisible by 2 or 3, then to
check through all the numbers of form 6k ± 1. (Source: wikipedia)
bool isPrime(int n)
{
// Corner cases
if (n <= 1) return false;
if (n <= 3) return true;
return true;
}
Merge Sort, Quick Sort, Bubble Sort, Insertion Sort, Wiggle sort
Selection Sort
The selection sort algorithm sorts an array by repeatedly finding the minimum element
(considering ascending order) from unsorted part and putting it at the beginning. The algorithm
maintains two subarrays in a given array.
In every iteration of selection sort, the minimum element (considering ascending order) from the
unsorted subarray is picked and moved to the sorted subarray.
79
{
int temp = *xp;
*xp = *yp;
*yp = temp;
}
Bubble Sort
Bubble Sort is the simplest sorting algorithm that works by repeatedly swapping the adjacent
elements if they are in wrong order.
80
Worst and Average Case Time Complexity: O(n^2). Worst case occurs when array is reverse
sorted.
Best Case Time Complexity: O(n). Best case occurs when array is already sorted.
Boundary Cases: Bubble sort takes minimum time (Order of n) when elements are already
sorted.
Merge Sort
Like QuickSort, Merge Sort is a Divide and Conquer algorithm. It divides input array in two
halves, calls itself for the two halves and then merges the two sorted halves. The merge()
function is used for merging two halves. The merge(arr, l, m, r) is key process that assumes that
arr[l..m] and arr[m+1..r] are sorted and merges the two sorted sub-arrays into one. See following
C implementation for details.
81
}
k++;
}
merge(arr, l, m, r);
}
}
Time Complexity:
Sorting arrays on different machines. Merge Sort is a recursive algorithm and time complexity
can be expressed as following recurrence relation.
The above recurrence can be solved either using Recurrence Tree method or Master method. It
falls in case II of Master Method and solution of the recurrence is O(nLogn).
82
Time complexity of Merge Sort is O(nLogn) in all 3 cases (worst, average and best) as merge
sort always divides the array in two halves and take linear time to merge two halves.
Insertion Sort
Insertion sort is a simple sorting algorithm that works the way we sort playing cards in our
hands.
Algorithm
// Sort an arr[] of size n
insertionSort(arr, n)
Loop from i = 1 to n-1.
……a) Pick element arr[i] and insert it into sorted sequence arr[0…i-1]
Boundary Cases: Insertion sort takes maximum time to sort if elements are sorted in reverse
order. And it takes minimum time (Order of n) when elements are already sorted.
QuickSort
Like Merge Sort, QuickSort is a Divide and Conquer algorithm. It picks an element as pivot and
partitions the given array around the picked pivot. There are many different versions of
quickSort that pick pivot in different ways.
83
Always pick last element as pivot (implemented below)
Pick a random element as pivot.
Pick median as pivot.
The key process in quickSort is partition(). Target of partitions is, given an array and an element
x of array as pivot, put x at its correct position in sorted array and put all smaller elements
(smaller than x) before x, and put all greater elements (greater than x) after x. All this should be
done in linear time.
84
int p = partrition(arr, start, end);
printf("p index: %d\n", p);
quickSort(arr, start, p-1);
quickSort(arr, p+1, end);
}
}
Find out 45 minutes with the help of two ropes. Given that one rope
burns completely in 1 Hr and the rate or burning is not consistent.
Have one rope folded and the other one unfolded. Burn the unfolded rope on both ends
simultaneously, it gives 30 minutes. At the time it burns out, burn the folded rope the same way
to get the 15 minutes.
There are 9 coins, find the lightest one. How many times you need to
weight?
2 times.
coin 1,2,3 in one, 4,5,6 in another.
then one coin each side.
85
Behavior Questions
Talk about a time you disagreed with a team mate, and how did you
resolve the issue.
How do you want to see yourself after a year, you intentions, aspirations
86