1.how Do You Reverse A Singly Linked List? How Do You Reverse A Doubly Linked List? Write A C Program To Do The Same
1.how Do You Reverse A Singly Linked List? How Do You Reverse A Doubly Linked List? Write A C Program To Do The Same
#include <stdio.h>
// Variables
typedef struct node
{
int value;
struct node *next;
}mynode;
// Functions
void add(int value);
void iterative_reverse();
void print_list();
//Print it
print_list();
// Reverse it.
iterative_reverse();
//Print it again
print_list();
return(0);
}
// The reverse function
void iterative_reverse()
{
mynode *p, *q, *r;
p = head;
q = p->next;
p->next = (mynode *)0;
head = p;
}
if(head==(mynode *)0)
{
head=temp;
tail=temp;
}
else
{
tail->next=temp;
tail=temp;
}
}
#include <stdio.h>
// Variables
typedef struct node
{
int value;
struct node *next;
}mynode;
// Globals.
mynode *head, *tail, *temp;
// Functions
void add(int value);
mynode* reverse_recurse(mynode *root);
void print_list();
//Print it
print_list();
// Reverse it.
if(head != (mynode *)0)
{
temp = reverse_recurse(head);
temp->next = (mynode *)0;
}
//Print it again
print_list();
return(0);
}
if(head==(mynode *)0)
{
head=temp;
tail=temp;
}
else
{
tail->next=temp;
tail=temp;
}
}
#include <stdio.h>
// Variables
typedef struct node
{
int value;
struct node *next;
}mynode;
// Functions
void add(mynode **head, mynode **tail, int value);
mynode* reverse_recurse(mynode *current, mynode *next);
void print_list(mynode *);
int main()
{
mynode *head, *tail;
head=(mynode *)0;
//Print it
print_list(head);
// Reverse it.
head = reverse_recurse(head, (mynode *)0);
//Print it again
print_list(head);
getch();
return(0);
}
if(current==(mynode *)0)
{
return((mynode *)0);
}
current->next = next;
return ret;
}
if(*head==(mynode *)0)
{
*head=temp1;
*tail=temp1;
}
else
{
for(temp2 = *head; temp2->next!= (mynode *)0; temp2=temp2->next);
temp2->next = temp1;
*tail=temp1;
}
}
This is really easy, just keep swapping the prev and next pointers and at the end swap the
head and the tail:)
#include<stdio.h>
#include<ctype.h>
int main()
{
head=NULL;
tail=NULL;
add_node(1);
add_node(2);
add_node(3);
add_node(4);
add_node(5);
print_list();
reverse();
print_list();
return(1);
if(head == NULL)
{
printf("\nAdding a head pointer\n");
head=temp;
tail=temp;
temp->value=value;
}
else
{
for(cur=head;cur->next!=NULL;cur=cur->next);
cur->next=temp;
temp->prev=cur;
temp->value=value;
tail=temp;
}
}
void print_list()
{
mynode *temp;
printf("\n--------------------------------\n");
for(temp=head;temp!=NULL;temp=temp->next)
{
printf("\n[%d]\n",temp->value);
}
}
void reverse()
{
mynode *cur, *temp, *save_next;
if(head==tail)return;
if(head==NULL || tail==NULL)
return;
for(cur=head;cur!=NULL;)
{
printf("\ncur->value : [%d]\n",cur->value);
temp=cur->next;
save_next=cur->next;
cur->next=cur->prev;
cur->prev=temp;
cur=save_next;
}
temp=head;
head=tail;
tail=temp;
}
3.How do you sort a linked list? Write a C program to sort a linked list.
The general idea is to decide upon a sorting algorithm (say bubble sort). Then, one needs
to come up with different scenarios to swap two nodes in the linked list when they are not
in the required order. The different scenarios would be something like
1. When the nodes being compared are not adjacent and one of them is the first node.
2. When the nodes being compared are not adjacent and none of them is the first node
3. When the nodes being compared are adjacent and one of them is the first node.
4. When the nodes being compared are adjacent and none of them is the first node.
One example bubble sort for a linked list goes like this
As you can see, the code becomes quite messy because of the pointer logic. Thats why I
have not elaborated too much on the code, nor on variations such as soring a doubly
linked list. You have to do it yourself once to understand it.
if( size<=2 )
{
if(size==1)
{
// Nothing to sort!
return(list);
}
else
{
if(list->value < list->next->value
{
// These 2 nodes are already in right order, no need to sort
return(list);
}
else
{
// Need to swap these 2 nodes
/* Here we have 2 nodes
*
*node 1 -> node2 -> NULL
*
* This should be converted to
*
* node2 -> node1 -> NULL
*
*/
tempnode1 = list;
tempnode2 = list->next;
tempnode2->next = tempnode1;
tempnode1->next = NULL;
return(tempnode2);
}
}
}
else
{
// The size of the linked list is more than 2.
// Need to split this linked list, sort the
// left and right sub-linked lists and merge.
// Split.
// tempnode1 will have the first half of the linked list of size "size1".
// tempnode2 will have the second half of the linked list of size "size2".
return(tempnode3);
}
}
The code to merge the two already sorted sub-linked lists into a sorted linked list could be
something like this..
i = a;
j = b;
c = getNewNode();
k = getNewNode();
if( i != NULL)
k -> next = i ;
else
k -> next = j;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct check
{
int i;
char c;
double d;
}chk[] = { { 1, 'a', 1.1 },{ 2, 'b', 2.2 }, { 3, 'c', 3.3 } };
int main(void)
{
char c[] = { 'a', 'b', 'c', 'd' };
int i[] = { 1, 2, 3, 4 };
char *str[] = { "hello1", "hello2", "hello3", "hello4" };
printf("Printing characters:");
print(list1, printchar);
printf(" : done\n\n");
printf("Printing integers:");
print(list2, printint);
printf(" : done\n\n");
printf("Printing strings:");
print(list3, printstr);
printf(" : done\n\n");
printf("Printing composite:");
print(list4, printcomp);
printf(" : done\n");
return 0;
}
8.How do you find the middle of a linked list? Write a C program to return the
middle of a linked list
Another popular interview question
Here are a few C program snippets to give you an idea of the possible solutions.
Method1
p = head;
q = head;
The heterogeneous linked list contains different data types in its nodes and we
need a link, pointer to connect them. It is not possible to use ordinary pointers for this. So
we go for void pointer. Void pointer is capable of storing pointer to any type as it is a
generic pointer type.
Check out the C program to implement a Generic linked list in the same FAQ.
10.How to compare two linked lists? Write a C program to compare two linked lists.
Check out this C program which creates an exact copy of a linked list.
Before looking at the answer, try writing a simple C program (with a for loop) to do this.
Quite a few people get this wrong.
Here is a solution which is often called as the solution that uses frames.
Suppose one needs to get to the 6th node from the end in this LL. First, just keep on
incrementing the first pointer (ptr1) till the number of increments cross n (which is 6 in
this case)
STEP 1 : 1(ptr1,ptr2) -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10
STEP 2 : 1(ptr2) -> 2 -> 3 -> 4 -> 5 -> 6(ptr1) -> 7 -> 8 -> 9 -> 10
Now, start the second pointer (ptr2) and keep on incrementing it till the first pointer (ptr1)
reaches the end of the LL.
STEP 3 : 1 -> 2 -> 3 -> 4(ptr2) -> 5 -> 6 -> 7 -> 8 -> 9 -> 10 (ptr1)
So here you have!, the 6th node from the end pointed to by ptr2!
struct node
{
int data;
struct node *next;
}mynode;
if( !head )
{
return( NULL ) ;
}
ptr1 = head ;
ptr2 = head ;
count = 0 ;
I have a hunch that interviewers who ask this question are probably hinting at something
called Smart Pointers in C++. Smart pointers are particularly useful in the face of
exceptions as they ensure proper destruction of dynamically allocated objects. They can
also be used to keep track of dynamically allocated objects shared by multiple owners.
This topic is out of scope here, but you can find lots of material on the Internet for Smart
Pointers.
If you have better answers to this question, let me know!
#include <stdio.h>
#include <string.h>
p1 = (char *) malloc(12);
memset(p1,12,'\0');
size=10;
strcpy(p1,"ABCDEFGHI");
p2 = p1 + 2;
printf("\n--------------------------------\n");
printf("\nFrom (before) = [%s]",p1);
printf("\nTo (before) = [%s]",p2);
mymemmove(p2,p1,size);
/* ----------------------------------------
*
* CASE 2 : From (SRC) > To (DEST)
*
* +--+---------------------+--+
*||||
* +--+---------------------+--+
*^^
*||
* To From
*
* --------------------------------------- */
p3 = (char *) malloc(12);
memset(p3,12,'\0');
p4 = p3 + 2;
strcpy(p4, "ABCDEFGHI");
printf("\n--------------------------------\n");
/* ----------------------------------------
*
* CASE 3 : No overlap
*
* --------------------------------------- */
p1 = (char *) malloc(30);
memset(p1,30,'\0');
size=10;
strcpy(p1,"ABCDEFGHI");
p2 = p1 + 15;
printf("\n--------------------------------\n");
printf("\nFrom (before) = [%s]",p1);
printf("\nTo (before) = [%s]",p2);
mymemmove(p2,p1,size);
printf("\n--------------------------------\n");
printf("\n\n");
return 0;
}
p2 = p2 + size;
if (p2 != from)
{
// Overlap detected!
while (size-- != 0)
{
*--p1 = *--p2;
}
}
else
{
// No overlap OR they overlap as CASE 2 above.
// memcopy() would have done this directly.
while (size-- != 0)
{
*p1++ = *p2++;
}
}
return(to);
}
--------------------------------
--------------------------------
So then, whats the difference between the implementation of memmove() and memcpy().
Its just that memcpy() will not care if the memories overlap and will either copy from left
to right or right to left without checking which method to used depending on the type of
the overlap. Also note that the C code proves that the results are the same irrespective of
the Endian-ness of the machine.
/* Searching */
for (j = 0; j <= (n - m); ++j)
{
for (i = 0; i < m && x[i] == y[i + j]; ++i);
if (i >= m) {printf("\nMatch found at\n\n->[%d]\n->[%s]\n",j,y+j);}
}
}
int main()
{
char *string = "hereroheroero";
char *pattern = "hero";
BF(pattern,strlen(pattern),string,strlen(string));
printf("\n\n");
return(0);
}
37. How to generate fibonacci numbers? How to find out if a given number is a
fibonacci number or not? Write C programs to do both.
Lets first refresh ourselves with the Fibonacci sequence
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, .....
Fibonacci numbers obey the following rule
F(n) = F(n-1) + F(n-2)
Here is an iterative way to generate fibonacci numbers and also return the nth number.
int fib( int n )
{
int f[n+1];
f[1] = f[2] = 1;
printf("\nf[1] = %d", f[1]);
printf("\nf[2] = %d", f[2]);
for ( int i = 3 ; i <= n ; i++ )
{
f[i] = f[i-1] + f[i-2];
printf("\nf[%d] = [%d]",i,f[i]);
}
return f[n];
}
Here is a recursive way to generate fibonacci numbers.
int fib(int n)
{
if ( n <= 2 )
return 1;
else
return fib(n-1) + fib(n-2);
}
Here is an iterative way to just compute and return the nth number (without storing the
previous numbers).
int fib(int n)
{
int a = 1, b = 1;
for ( int i = 3 ; i <= n ; i++ )
{
int c = a + b;
a = b;
b = c;
}
return a;
}
39.What Little-Endian and Big-Endian? How can I determine whether a machine's
byte order is big-endian or little endian? How can we convert from one to another?
First of all, Do you know what Little-Endian and Big-Endian mean?
Little Endian means that the lower order byte of the number is stored in memory at the
lowest address, and the higher order byte is stored at the highest address. That is, the little
end comes first.
For example, a 4 byte, 32-bit integer
Byte3 Byte2 Byte1 Byte0 will be arranged in memory as follows:
Base_Address+0 Byte0
Base_Address+1 Byte1
Base_Address+2 Byte2
Base_Address+3 Byte3
Intel processors use "Little Endian" byte order.
"Big Endian" means that the higher order byte of the number is stored in memory at the
lowest address, and the lower order byte at the highest address. The big end comes first.
Base_Address+0 Byte3
Base_Address+1 Byte2
Base_Address+2 Byte1
Base_Address+3 Byte0
Motorola, Solaris processors use "Big Endian" byte order.
In "Little Endian" form, code which picks up a 1, 2, 4, or longer byte number proceed in
the same way for all formats. They first pick up the lowest order byte at offset 0 and
proceed from there. Also, because of the 1:1 relationship between address offset and byte
number (offset 0 is byte 0), multiple precision mathematic routines are easy to code. In
"Big Endian" form, since the high-order byte comes first, the code can test whether the
number is positive or negative by looking at the byte at offset zero. Its not required to
know how long the number is, nor does the code have to skip over any bytes to find the
byte containing the sign information. The numbers are also stored in the order in which
they are printed out, so binary to decimal routines are particularly efficient.
Here is some code to determine what is the type of your machine
int num = 1;
if ( * ( char * ) &num == 1 )
{
printf("\nLittle-Endian\n");
}
else
{
printf("Big-Endian\n");
}
And here is some code to convert from one Endian to another.
int myreversefunc( int num )
{
int byte0, byte1, byte2, byte3;
byte0 = ( num & x000000FF ) >> 0 ;
byte1 = ( num & x0000FF00 ) >> 8 ;
byte2 = ( num & x00FF0000 ) >> 16 ;
byte3 = ( num & xFF000000 ) >> 24 ;
return ( ( byte0 << 24 ) | ( byte1 << 16 ) | ( byte2 << 8 ) | ( byte3 << 0 ) ) ;
}
43.Write a C progam to convert from decimal to any base (binary, hex, oct etc...)
Here is some really cool C code
#include <stdio.h>
int main()
{
decimal_to_anybase( 10, 2 );
decimal_to_anybase( 255, 16 );
getch();
}
decimal_to_anybase ( int n , int base )
{
int i , m , digits[1000] , flag ;
i=0;
printf("\n\n[%d] converted to base [%d] : ", n, base);
while ( n )
{
m = n % base ;
digits[ i ] = "0123456789abcdefghijklmnopqrstuvwxyz" [ m ] ;
n = n / base ;
i++ ;
}
//Eliminate any leading zeroes
for( i-- ; i >= 0 ; i-- )
{
if ( !flag && digits[i] != '0' )
flag=1;
if ( flag )
printf("%c",digits[i]);
}
}
81.Write your own trim() or squeeze() function to remove the spaces from a string.
Here is one version...
#include <stdio.h>
char *trim( char *s ) ;
int main( int argc , char *argv[] )
{
char str1[] = " Hello I am Good " ;
printf("\n\nBefore trimming : [%s]", str1);
printf("\n\nAfter trimming : [%s]", trim(str1));
getch( ) ;
}
// The trim() function...
char *trim( char *s )
{
char *p, *ps;
for ( ps = p = s ; *s != '\0' ; s++ )
{
if ( !isspace( *s ) )
{
*p++ = *s ;
}
}
*p = '\0' ;
return( ps ) ;
}
136.What operations are valid on pointers? When does one get the Illegal use of
pointer in function error?
This is what is Valid
px<py
px>=py
px==py
px!=py
px==NULL
px=px+n
px=px-n
px-py
Everything else is invalid (multiplication, division, addition of two pointers)!
How can I find how many arguments a function was passed?
Any function which takes a variable number of arguments must be able to
determine from the arguments themselves, how many of them there have been passed.
printf() and some similar functions achieve this by looking for the format string. This is
also why these functions fail badly if the format string does not match the argument list.
Another common technique, applicable when the arguments are all of the same type, is to
use a sentinel value (often 0, -1, or an appropriately-cast null pointer) at the end of the
list. Also, one can pass an explicit count of the number of variable arguments. Some older
compilers did provided a nargs() function, but it was never portable.
Is this allowed?
int f(...)
{
...
}
No! Standard C requires at least one fixed argument, in part so that you can hand it to
va_start().
151.If I have the name of a function in the form of a string, how can I invoke that
function?
Keep a table of names and their function pointers:
int myfunc1(), myfunc2();
struct
{
char *name;
int (*func_ptr)();
} func_table[] = {"myfunc1", myfunc1,"myfunc2", myfunc2,};
Search the table for the name, and call via the associated function pointer.
There are some limitations with respect to these bit fields, however:
1. Cannot scanf() directly into bit fields.
2. Pointers cannot be used on bit fields to access them.
3. Cannot have an array of bit fields.
The main use of bitfields is either to allow tight packing of data or to be able to specify
the fields within some externally produced data files. C gives no guarantee of the
ordering of fields within machine words, so if you do use them for the latter reason, you
program will not only be non-portable, it will be compiler-dependent too. The Standard
says that fields are packed into ?storage units?, which are typically machine words. The
packing order, and whether or not a bitfield may cross a storage unit boundary, are
implementation defined. To force alignment to a storage unit boundary, a zero width field
is used before the one that you want to have aligned. Be careful using them. It can require
a surprising amount of run-time code to manipulate these things and you can end up
using more space than they save. Bit fields do not have addresses?you can't have pointers
to them or arrays of them.
206.How can I insert or delete a line (or record) in the middle of a file?
The only way is to rewrite the file.
207.How can I recover the file name using its file descriptor?
Have a wrapper around fopen() to remember the names of files as you open them.
209.Whats the use of fopen(), fclose(), fprintf(), getc(), putc(), getw(), putw(),
fscanf(), feof(), ftell(), fseek(), rewind(), fread(), fwrite(), fgets(), fputs(), freopen(),
fflush(), ungetc()?
Whew!, thats a huge list.
fopen()
This function is used to open a stream.
FILE *fp;
fp = fopen("filename","mode");
fp = fopen("data","r");
fp = fopen("results","w");
Modes
"r" -> Open for reading.
"w" -> Open for writing.
"a" -> Open for appending.
"r+" -> Both reading and writing.
"w+" -> Both reading and writing, create new file if it exists,
"a+" -> Open for both reading and appending.
fopen()
fclose() is used to close a stream .
fclose(fp);
putc(), getc(), putw(), getw(), fgetc(), getchar(), putchar(), fputs()
These functions are used to read/write different types of data to the stream.
putc(ch,fp);
c=getc(fp);
putw(integer, fp);
integer=getw(fp);
fprintf(), fscanf()
Read/Write formatted data from/to the stream.
fprintf(fp,"control string",list);
fscanf(fp,"control string", list);
foef()
Check the status of a stream
if(feof(fp)!=0)
ftell(), fseek(), rewind(), fgetpos(), fsetpos()
Reposition the file pointer of a stream
n=ftell(fp); //Relative offset (in bytes) of the current position.
rewind(fp);
fseek(fp, offset, position);
Position can be
0->start of file
1->current position
2->end of file
fseek(fp,0L,0); // Same as rewind.
fseek(fp,0L,1); // Stay at current position.
fseek(fp,0L,2); // Past end of file.
fseek(fp,m,0); // Move to (m+1) byte.
fseek(fp,m,1) // Go forward m bytes.
fseek(fp,-m,1); // Go backward m bytes from current position.
fseek(fp,-m,2); // Go backward from end of file.
fread(), fwrite()
Binary stream input/output.
fwrite(&customer, sizeof(record),1,fp);
fread(&customer,sizeof(record),1,fp);
Here is a simple piece of code which reads from a file
#include <stdio.h>
#include <conio.h>
int main()
{
FILE *f;
char buffer[1000];
f=fopen("E:\\Misc\\__Temp\\FileDrag\\Main.cpp","r");
if(f)
{
printf("\nOpenened the file!\n");
while(fgets(buffer,1000,f))
{
printf("(%d)-> %s\n",strlen(buffer),buffer);
}
}
fclose(f);
getch();
return(0);
}
210.How to check if a file is a binary file or an ascii file?
Here is some sample C code. The idea is to check the bytes in the file to see if they are
ASCII or not...
#include <stdio.h>
int main(int argc, char *argv[])
{
unsigned char ch;
FILE *file;
int binaryFile = FALSE;
file = fopen(<FILE_PATH>, "rb"); // Open in Binary mode for the first
time.
while((fread(&ch, 1, 1, file) == 1) && (binaryFile == FALSE))
{
if(ch < 9 || ch == 11 || (ch > 13 && ch < 32) || ch == 255)
{
binaryFile = 1;
}
}
fclose(file);
if(binaryFile)
file = fopen(<FILE_PATH>, "rb");
else
file = fopen(<FILE_PATH>, "r");
if(binaryFile)
{
while(fread(&ch, 1, 1, file) == 1)
{
// Do whatever you want here with the binary file byte...
}
}
else
{
while(fread(&ch, 1, 1, file) == 1)
{
// This is ASCII data, can easily print it!
putchar(ch);
}
}
fclose(file);
return(0);
}
void func(void);
jmp_buf place;
main()
{
int retval;
/*
* First call returns 0,
* a later longjmp will return non-zero.
*/
if(setjmp(place)) != 0)
{
printf("Returned using longjmp\n");
exit(EXIT_SUCCESS);
}
/*
* This call will never return - it
* 'jumps' back above.
*/
func();
printf("What! func returned!\n");
}
void func(void)
{
/*
* Return to main.
* Looks like a second return from setjmp,
* returning 4!
*/
longjmp(place, 4);
printf("What! longjmp returned!\n");
}
The val argument to longjmp is the value seen in the second and subsequent ?returns?
from setjmp. It should normally be something other than 0; if you attempt to return 0 via
longjmp, it will be changed to 1. It is therefore possible to tell whether the setjmp was
called directly, or whether it was reached by calling longjmp. If there has been no call to
setjmp before calling longjmp, the effect of longjmp is undefined, almost certainly
causing the program to crash. The longjmp function is never expected to return, in the
normal sense, to the instructions immediately following the call. All accessible objects on
?return? from setjmp have the values that they had when longjmp was called, except for
objects of automatic storage class that do not have volatile type; if they have been
changed between the setjmp and longjmp calls, their values are indeterminate.
The longjmp function executes correctly in the contexts of interrupts, signals and any of
their associated functions. If longjmp is invoked from a function called as a result of a
signal arriving while handling another signal, the behaviour is undefined.
It's a serious error to longjmp to a function which is no longer active (i.e. it has already
returned or another longjump call has transferred to a setjmp occurring earlier in a set of
nested calls).
The Standard insists that, apart from appearing as the only expression in an expression
statement, setjmp may only be used as the entire controlling expression in an if, switch,
do, while, or for statement. A slight extension to that rule is that as long as it is the whole
controlling expression (as above) the setjmp call may be the subject of the ! operator, or
may be directly compared with an integral constant expression using one of the relational
or equality operators. No more complex expressions may be employed.
Examples are:
setjmp(place); /* expression statement */
if(setjmp(place)) ... /* whole controlling expression */
if(!setjmp(place)) ... /* whole controlling expression */
if(setjmp(place) < 4) ... /* whole controlling expression */
if(setjmp(place)<;4 && 1!=2) ... /* forbidden */
232.What do the system calls fork(), vfork(), exec(), wait(), waitpid() do? Whats a
Zombie process? Whats the difference between fork() and vfork()?
The system call fork() is used to create new processes. It does not take any arguments and
returns a process ID. The purpose of fork() is to create a new process, which becomes the
child process of the caller (which is called the parent). After a new child process is
created, both processes will execute the next instruction following the fork() system call.
We can distinguish the parent from the child by testing the returned value of fork():
If fork() returns a negative value, the creation of a child process was unsuccessful. A call
to fork() returns a zero to the newly created child process and the same call to fork()
returns a positive value (the process ID of the child process) to the parent. The returned
process ID is of type pid_t defined in sys/types.h. Normally, the process ID is an integer.
Moreover, a process can use function getpid() to retrieve the process ID assigned to this
process. Unix will make an exact copy of the parent's address space and give it to the
child. Therefore, the parent and child processes will have separate address spaces. Both
processes start their execution right after the system call fork(). Since both processes have
identical but separate address spaces, those variables initialized before the fork() call
have the same values in both address spaces. Since every process has its own address
space, any modifications will be independent of the others. In other words, if the parent
changes the value of its variable, the modification will only affect the variable in the
parent process's address space. Other address spaces created by fork() calls will not be
affected even though they have identical variable names.
Trick question!
The UID (numeric user identity)
The GID (numeric group identity)
A process ID number used to identify the process
A parent process ID
The execution status, e.g. active, runnable, waiting for input etc.
Environment variables and values.
The current directory.
Where the process currently resides in memory
The relative process priority see nice(1)
Where it gets standard input from
Where it sends standard output to
Any other files currently open
Certain process resources are unique to the individual process. A few among these are:
* Stack Space: which is where local variables, function calls, etc. are stored.
* Environment Space: which is used for storage of specific environment variables.
* Program Pointer (counter) : PC.
* File Descriptors
* Variables
Undex unix, each sub directory under /proc corresponds to a running process (PID #). A
ps provide detailed information about processes running
A typical output of ps looks as follows:
PID TTY STAT TIME COMMAND
--------------------------------------------------------------------
8811 3 SW 0:00 (login)
3466 3 SW 0:00 (bash)
8777 3 SW 0:00 (startx)
. . . . .
1262 p7 R 0:00 ps
The columns refer to the following:
PID - The process id's (PID).
TTY - The terminal the process was started from.
STAT - The current status of all the processes. Info about the process status
can be broken into more than 1 field. The first of these fields can
contain the following entries:
R - Runnable.
S - Sleeping.
D - Un-interuptable sleep.
T - Stopped or Traced.
Z - Zombie Process.
The second field can contain the following entry:
W - If the process has no residual pages.
And the third field:
N - If the process has a positive nice value.
TIME - The CPU time used by the process so far.
COMMAND - The actual command.
The init process is the very first process run upon startup. It starts additional processes.
When it runs it reads a file called /etc/inittab which specifies init how to set up the
system, what processes it should start with respect to specific runlevels. One crucial
process which it starts is the getty program. A getty process is usually started for each
terminal upon which a user can log into the system. The getty program produces the
login: prompt on each terminal and then waits for activity. Once a getty process detects
activity (at a user attempts to log in to the system), the getty program passes control over
to the login program.
There are two command to set a process's priority nice and renice.
One can start a process using the system() function call
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("Running ls.....\n");
system("ls -lrt");
printf("Done.\n");
exit(0);
}
The exec() system call
The exec() functions replace a current process with another created according to the
arguments given.
The syntax of these functions is as follows:
#include <unistd.h>
char *env[];
int execl(const char *path, const char *arg0, ..., (char *)0);
int execv(const char *path, const char *argv[]);
int execlp(const char *path, const char *arg0, ..., (char *)0);
int execvp(const char *path, const char *argv[]);
int execle(const char *path, const char *arg0, ... , (char *)0, const char *env[]);
int execve(const char *path, const char *argv[], const char *env[]);
The program given by the path argument is used as the program to execute in place of
what is currently running. In the case of the execl() the new program is passed arguments
arg0, arg1, arg2,... up to a null pointer. By convention, the first argument supplied (i.e.
arg0) should point to the file name of the file being executed. In the case of the execv()
programs the arguments can be given in the form of a pointer to an array of strings, i.e.
the argv array. The new program starts with the given arguments appearing in the argv
array passed to main. Again, by convention, the first argument listed should point to the
file name of the file being executed. The function name suffixed with a p (execlp() and
execvp())differ in that they will search the PATH environment variable to find the new
program executable file. If the executable is not on the path, and absolute file name,
including directories, will need to be passed to the function as a parameter. The global
variable environ is available to pass a value for the new program environment. In
addition, an additional argument to the exec() functions execle() and execve() is available
for passing an array of strings to be used as the new program environment.
Examples to run the ls command using exec are:
const char *argv[] = ("ls", "-lrt", 0);
const char *env[] = {"PATH=/bin:/usr/bin", "TERM=console", 0};
execl("/bin/ls", "ls", "-lrt", 0);
execv("/bin/ls", argv);
execlp("ls", "ls", "-lrt", 0);
execle("/bin/ls", "ls", "-lrt", 0, env);
execvp("ls", argv);
execve("/bin/ls", argv, env);
A simple call to fork() would be something like this
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
pid_t pid;
pid=fork();
switch(pid)
{
case -1:
exit(1); // fork() error.
case 0: // Child process, can call exec here.
break;
default: // Parent.
break;
}
exit(0);
}
The call wait() can be used to determine when a child process has completed it's job and
finished. We can arrange for the parent process to wait untill the child finishes before
continuing by calling wait(). wait() causes a parent process to pause untill one of the child
processes dies or is stopped. The call returns the PID of the child process for which status
information is available. This will usually be a child process which has terminated. The
status information allows the parent process to determine the exit status of the child
process, the value returned from main or passed to exit. If it is not a null pointer the status
information will be written to the location pointed to by stat_loc. We can interrogate the
status information using macros defined in sys/wait.h.
Macro Definition
-----------------------------------------------------------------------------------
WIFEXITED(stat_val); Nonzero if the child is terminated normally
WEXITSTATUS(stat_val); If WIFEXITED is nonzero, this returns child exit code.
WIFSIGNALLED(stat_val); Nonzero if the child is terminated on an uncaught signal.
WTERMSIG(stat_val); If WIFSIGNALLED is nonzero, this returns a signal number.
WIFSTOPPED(stat_val); Nonzero if the child stopped on a signal.
WSTOPSIG(stat_val); If WIFSTOPPED is nonzero, this returns a signal number.
An example code which used wait() is shown below
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void){
pid_t child_pid;
int *status=NULL;
if( fork ( ) )
{
/* wait for child, getting PID */
child_pid=wait(status);
printf("I'm the parent.\n");
printf("My child's PID was: %d\n",child_pid);
}
else
{
printf("I'm the child.\n");
}
return 0;
}
Or a more detailed program
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
pid_t pid;
int exit_code;
pid = fork();
switch(pid)
{
case -1:
exit(1);
case 0:
exit_code = 11; //Set the child exit process
break;
default:
exit_code = 0;
break;
}
if (pid)
{
// This is the parent process
int status;
pid_t child_pid;
child_pid = wait(&status);
printf("Child process finished with PID [%d]\n", child_pid);
if (WIFEXITED(status))
{
printf("Child exited with code [%d]\n", WEXITSTATUS(status));
}
else
{
printf("Child terminated abnormally.\n");
}
}
exit(exit_code);
}