1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14.

Sunday, October 30, 2011

littleendian and bigendian


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 last.

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 0×01234567 will be stored as following.


How to see memory representation of multibyte data types on your machine?

Here is a sample C code that shows the byte representation of int, float and pointer.
#include

/* function to show bytes in memory, from location start to start+n*/
void show_mem_rep(char *start, int n)
{
int i;
for (i = 0; i < n; i++)
printf(" %.2x", start[i]);
printf("\n");
}

/*Main function to call above function for 0×01234567*/
int main()
{
int i = 0×01234567;
show_mem_rep((char *)&i, sizeof(i));
getchar();
return 0;
}

When above program is run on little endian machine, gives "67 45 23 01″ as output , while if it is run on endian machine, gives "01 23 45 67″ as output.

Is there a quick way to determine endianness of your machine?

There are n no. of ways for determining endianness of your machine. Here is one quick way of doing the same.
#include
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.

Does endianness matter for programmers?

Most of the times compiler takes care of endianness, however, endianness becomes an issue in following cases.

It matters in network programming: Suppose you write integers to file on a little endian machine and you transfer this file to a big endian machine. Unless there is little andian to big endian transformation, big endian machine will read the file in reverse order. You can find such a practical example here.

Standard byte order for networks is big endian, also known as network byte order. Before transferring data on network, data is first converted to network byte order (big endian).

Sometimes it matters when you are using type casting, below program is an example.
#include
int main()
{
unsigned char arr[2] = {0×01, 0×00};
unsigned short int x = *(unsigned short int *) arr;
printf("%d", x);
getchar();
return 0;
}

In the above program, a char array is typecasted to an unsigned short integer type. When I run above program on little endian machine, I get 1 as output, while if I run it on a big endian machine I get 512. To make programs endianness independent, above programming style should be avoided.

What are bi-endians?

Bi-endian processors can run in both modes little and big endian.

What are the examples of little, big endian and bi-endian machines ?
Intel based processors are little endians. ARM processors were little endians. Current generation ARM processors are bi-endian.

Motorola 68K processors are big endians. PowerPC (by Motorola) and SPARK (by Sun) processors were big endian. Current version of these processors are bi-endians.

Does endianness effects file formats?

File formats which have 1 byte as a basic unit are independent of endianness e..g., ASCII files . Other file formats use some fixed endianness forrmat e.g, JPEG files are stored in big endian format.

Which one is better — little endian or big endian

The term little and big endian came from Gulliver's Travels by Jonathan Swift. Two groups could not agree by which end a egg should be opened -a-the little or the big. Just like the egg issue, there is no technological reason to choose one byte ordering convention over the other, hence the arguments degenerate into bickering about sociopolitical issues. As long as one of the conventions is selected and adhered to consistently, the choice is arbitrary.

Saturday, October 29, 2011

How extern will work in c

declaration and definition of a variable/function

Declaration of a variable/function simply declares that the variable/function exists somewhere in the program but the memory is not allocated for them. But the declaration of a variable/function serves an important role. And that is the type of the variable/function. Therefore, when a variable is declared, the program knows the data type of that variable. In case of function declaration, the program knows what are the arguments to that functions, their data types, the order of arguments and the return type of the function. So that's all about declaration. Coming to the definition, when we define a variable/function, apart from the role of declaration, it also allocates memory for that variable/function. Therefore, we can think of definition as a super set of declaration. (or declaration as a subset of definition). From this explanation, it should be obvious that a variable/function can be declared any number of times but it can be defined only once. (Remember the basic principle that you can't have two locations of the same variable/function). So that's all about declaration and definition.

Understanding "extern" keyword in C

it's mandatory to understand declaration/defination to understand the "extern" keyword. Let us first take the easy case. Use of extern with C functions. By default, the declaration and definition of a C function have "extern" prepended with them. It means even though we don't use extern with the declaration/definition of C functions, it is present there. For example, when we write.

int foo(int arg1, char arg2);

There's an extern present in the beginning which is hidden and the compiler treats it as below.

extern int foo(int arg1, char arg2);

Same is the case with the definition of a C function (Definition of a C function means writing the body of the function). Therefore whenever we define a C function, an extern is present there in the beginning of the function definition. Since the declaration can be done any number of times and definition can be done only once, we can notice that declaration of a function can be added in several C/H files or in a single C/H file several times. But we notice the actual definition of the function only once (i.e. in one file only). And as the extern extends the visibility to the whole program, the functions can be used (called) anywhere in any of the files of the whole program provided the declaration of the function is known. (By knowing the declaration of the function, C compiler knows that the definition of the function exists and it goes ahead to compile the program). So that's all about extern with C functions.

Now let us the take the second and final case i.e. use of extern with C variables. I feel that it more interesting and information than the previous case where extern is present by default with C functions. So let me ask the question, how would you declare a C variable without defining it? Many of you would see it trivial but it's important question to understand extern with C variables. The answer goes as follows.

extern int var;

Here, an integer type variable called var has been declared (remember no definition i.e. no memory allocation for var so far). And we can do this declaration as many times as needed. (remember that declaration can be done any number of times) So far so good. :)

Now how would you define a variable. Now I agree that it is the most trivial question in programming and the answer is as follows.

int var;

Here, an integer type variable called var has been declared as well as defined. (remember that definition is the super set of declaration). Here the memory for var is also allocated. Now here comes the surprise, when we declared/defined a C function, we saw that an extern was present by default. While defining a function, we can prepend it with extern without any issues. But it is not the case with C variables. If we put the presence of extern in variable as default then the memory for them will not be allocated ever, they will be declared only. Therefore, we put extern explicitly for C variables when we want to declare them without defining them. Also, as the extern extends the visibility to the whole program, by externing a variable we can use the variables anywhere in the program provided we know the declaration of them and the variable is defined somewhere.

Now let us try to understand extern with examples.

Example 1:
int var;
int main(void)
{
var = 10;
return 0;
}

Analysis: This program is compiled successfully. Here var is defined (and declared implicitly) globally.

Example 2:
extern int var;
int main(void)
{
return 0;
}

Analysis: This program is compiled successfully. Here var is declared only. Notice var is never used so no problems.

Example 3:
extern int var;
int main(void)
{
var = 10;
return 0;
}

Analysis: This program throws error in compilation. Because var is declared but not defined anywhere. Essentially, the var isn't allocated any memory. And the program is trying to change the value to 10 of a variable that doesn't exist at all.

Example 4:
#include "somefile.h"
extern int var;
int main(void)
{
var = 10;
return 0;
}

Analysis: Supposing that somefile.h has the definition of var. This program will be compiled successfully.

Example 5:
extern int var = 0;
int main(void)
{
var = 10;
return 0;
}

Analysis: Guess this program will work? Well, here comes another surprise from C standards. They say that..if a variable is only declared and an initializer is also provided with that declaration, then the memory for that variable will be allocated i.e. that variable will be considered as defined. Therefore, as per the C standard, this program will compile successfully and work.

So that was a preliminary look at "extern" keyword in C.

I'm sure that you want to have some take away from the reading of this post. And I would not disappoint you. :)
In short, we can say

1. Declaration can be done any number of times but definition only once.
2. "extern" keyword is used to extend the visibility of variables/functions().
3. Since functions are visible through out the program by default. The use of extern is not needed in function declaration/definition. Its use is redundant.
4. When extern is used with a variable, it's only declared not defined.
5. As an exception, when an extern variable is declared with initialization, it is taken as definition of the variable as well.

storage class in c

Types of Storage Classes :

Storage classes are categorised in 4 (four) types as,

Automatic Storage Class :

o Keyword : auto

o Storage Location : Main memory

o Initial Value : Garbage Value

o Life : Control remains in a block where it is defined.

o Scope : Local to the block in which variable is declared.

Syntax :

auto [data_type] [variable_name];

Example :

auto int a;

Register Storage Class :

o Keyword : register

o Storage Location : CPU Register

o Initial Value : Garbage

o Life : Local to the block in which variable is declared.

o Scope : Local to the block.

Syntax :

register [data_type] [variable_name];

Example :

register int a;

When the calculations are done in CPU, then the value of variables are transferred from main memory to CPU. Calculations are done and the final result is sent back to main memory. This leads to slowing down of processes.

Register variables occur in CPU and value of that register variable is stored in a register within that CPU. Thus, it increases the resultant speed of operations. There is no waste of time, getting variables from memory and sending it to back again.

It is not applicable for arrays, structures or pointers.

It cannot not used with static or external storage class.

Unary and address of (&) cannot be used with these variables as explicitly or implicitly.

Static Storage Class :

o Keyword : static

o Storage Location : Main memory

o Initial Value : Zero and can be initialize once only.

o Life : depends on function calls and the whole application or program.

o Scope : Local to the block.

Syntax :

static [data_type] [variable_name];

Example :

static int a;

There are two types of static variables as :

a) Local Static Variable
b) Global Static Variable

Static storage class can be used only if we want the value of a variable to persist between different function calls.

External Storage Class :

o Keyword : extern

o Storage Location : Main memory

o Initial Value : Zero

o Life : Until the program ends.

o Scope : Global to the program.

Syntax :

extern [data_type] [variable_name];

Example :

extern int a;

The variable access time is very fast as compared to other storage classes. But few registers are available for user programs.

The variables of this class can be referred to as 'global or external variables.' They are declared outside the functions and can be invoked at anywhere in a program.

Friday, October 7, 2011

Tell about the Memory Layout of a Process in Linux ?

Tell about the Memory Layout of a Process in Linux ?

Every running program (process) occupies some memory for its code and data. Linux follows a particular methodology for assigning memory addresses to various parts of a program.

  1. TEXT
    This is the code segment and contains only the executable code of the program. In Linux, this is a read-only segment, implying that it’s contents can never be overwritten by the program. Thus, Linux does not support self-modifying code.
  2. DATA
    This segment contains all the data that is required throughout program execution. In C terms, this includes all extern and static variables. This is split into two physical parts:

    1. Initialized Data Segment
      This contains all the extern and static variables of the program that been explicitly initialized in the program
    2. Uninitialized Data Segment (Also called BSS: Block Started by Symbol, a historial and outdated term!)
      This contains all the extern and static variables of the program that not been explicitly initialized in the program, and hence have to be automatically initialized to 0.
  3. STACK
    This segment contains all local variables that are created when control enters a function. Note that these also include function parameters.
  4. HEAP
    This segment is internally kept track via a linked list, and is used by dynamic variables (memory allocated using malloc and calloc). Frequent memory allocations and deallocations result in fragmentation here.

The memory layout of a typical C program is shown below:

Memory Segment

Memory Layout for C Programs in Linux

There is a strong reason for this arrangement. Note these points:

  1. The TEXT segment is loaded from the executable file
  2. The DATA segment is also blindly loaded from the executable file
    These points mean that the first part of the memory is a copy of data from the executable
  3. The BSS segment is NOT stored in the executable!
    This saves space, as the whole block is anyway full of zeros.
  4. The HEAP and the STACK grow towards each other.
    This ensures that both have enough space, and you would not get problems like too much stack space and too little heap space or vice-versa
  5. The Command Line Arguments and the Environment are dumped at the far end of the accessible memory block.

Finally, note that all addresses discussed here are logical addresses, and the actual physical address can be mapped anywhere in memory!

What is the use of the poll(file, polltable) API ?

What is the use of the poll(file, polltable) API ?

Ans :-

Applications that use nonblocking I/O often use the poll and epoll system calls as well. poll, and epoll have essentially the same functionality: each allow a process to determine whether it can read from or write to one or more open files without blocking. These calls can also block a process until any of a given set of file descriptors becomes available for reading or writing. Therefore, they are often used in applications that must use multiple input or output streams without getting stuck on any one of them. The same functionality is offered by multiple functions, because two were implemented in Unix almost at the same time by two different groups: whereas poll was the System V solution. The epoll call was added in 2.5.45 as a way of making the polling function scale to thousands of file descriptors.

Actually, epoll is a set of three calls that together can be used to achieve the polling functionality. For our purposes, though, we can think of it as a single call.
Support for any of these calls requires support from the device driver. This support (for all calls) is provided through the driver's poll method. This method has the following prototype:

unsigned int (*poll) (struct file *filp, poll_table *wait);

The driver method is called whenever the user-space program performs a poll or epoll system call involving a file descriptor associated with the driver. The device method is in charge of these two steps:

1. Call poll_wait on one or more wait queues that could indicate a change in the poll status. If no file descriptors are currently available for I/O, the kernel causes the process to wait on the wait queues for all file descriptors passed to the system call.
2.Return a bit mask describing the operations (if any) that could be immediately performed without blocking.

Both of these operations are usually straightforward and tend to look very similar from one driver to the next. They rely, however, on information that only the driver can provide and, therefore, must be implemented individually by each driver.

The poll_table structure, the second argument to the poll method, is used within the kernel to implement the poll, and epoll calls; it is declared in , which must be included by the driver source. Driver writers do not need to know anything about its internals and must use it as an opaque object; it is passed to the driver method so that the driver can load it with every wait queue that could wake up the process and change the status of the poll operation. The driver adds a wait queue to the poll_table structure by calling the function poll_wait:

void poll_wait (struct file *, wait_queue_head_t *, poll_table *);

The second task performed by the poll method is returning the bit mask describing which operations could be completed immediately; this is also straightforward. For example, if the device has data available, a read would complete without sleeping; the poll method should indicate this state of affairs. Several flags (defined via ) are used to indicate the possible operations:

POLLIN :-
This bit must be set if the device can be read without blocking.

POLLRDNORM :-

This bit must be set if "normal" data is available for reading. A readable device returns (POLLIN | POLLRDNORM).

POLLRDBAND :-

This bit indicates that out-of-band data is available for reading from the device. It is currently used only in one place in the Linux kernel (the DECnet code) and is not generally applicable to device drivers.

POLLPRI :-

High-priority data (out-of-band) can be read without blocking. This bit causes select to report that an exception condition occurred on the file, because select reports out-of-band data as an exception condition.

POLLHUP:-

When a process reading this device sees end-of-file, the driver must set POLLHUP (hang-up). A process calling select is told that the device is readable, as dictated by the select functionality.

POLLERR:-

An error condition has occurred on the device. When poll is invoked, the device is reported as both readable and writable, since both read and write return an error code without blocking.

POLLOUT:-

This bit is set in the return value if the device can be written to without blocking.

POLLWRNORM :-

This bit has the same meaning as POLLOUT, and sometimes it actually is the same number. A writable device returns (POLLOUT | POLLWRNORM).

POLLWRBAND :-

Like POLLRDBAND, this bit means that data with nonzero priority can be written to the device. Only the datagram implementation of poll uses this bit, since a datagram can transmit out-of-band data.

It's worth repeating that POLLRDBAND and POLLWRBAND are meaningful only with file descriptors associated with sockets: device drivers won't normally use these flags.

Interaction with read and write :-

The purpose of the poll and select calls is to determine in advance if an I/O operation will block. In that respect, they complement read and write. More important, poll and select are useful, because they let the application wait simultaneously for several data streams.

A correct implementation of the two calls is essential to make applications work correctly: although the following rules have more or less already been stated, we summarize them here.
Reading data from the device

1.If there is data in the input buffer, the read call should return immediately, with no noticeable delay, even if less data is available than the application requested, and the driver is sure the remaining data will arrive soon. You can always return less data than you're asked for if this is convenient for any reason, provided you return at least one byte. In this case, poll should return POLLIN|POLLRDNORM.
2.If there is no data in the input buffer, by default read must block until at least one byte is there. If O_NONBLOCK is set, on the other hand, read returns immediately with a return value of -EAGAIN (although some old versions of System V return 0 in this case). In these cases, poll must report that the device is unreadable until at least one byte arrives. As soon as there is some data in the buffer, we fall back to the previous case.
3. If we are at end-of-file, read should return immediately with a return value of 0, independent of O_NONBLOCK. poll should report POLLHUP in this case.

Writing to the device :-

1. If there is space in the output buffer, write should return without delay. It can accept less data than the call requested, but it must accept at least one byte. In this case, poll reports that the device is writable by returning POLLOUT|POLLWRNORM.

2. If the output buffer is full, by default write blocks until some space is freed. If O_NONBLOCK is set, write returns immediately with a return value of -EAGAIN (older System V Unices returned 0). In these cases, poll should report that the file is not writable. If, on the other hand, the device is not able to accept any more data, write returns -ENOSPC ("No space left on device"), independently of the setting of O_NONBLOCK.

3. Never make a write call wait for data transmission before returning, even if O_NONBLOCK is clear. This is because many applications use select to find out whether a write will block. If the device is reported as writable, the call must not block. If the program using the device wants to ensure that the data it enqueues in the output buffer is actually transmitted, the driver must provide an fsync method. For instance, a removable device should have an fsync entry point.

Although this is a good set of general rules, one should also recognize that each device is unique and that sometimes the rules must be bent slightly. For example, record-oriented devices (such as tape drives) cannot execute partial writes.

device driver interview questions

Device driver interview questions for new commers ,answers will be available soon.

1. Can you tell the memory layout based on Data,BSS,HEAP and STACK ?
2. What is the use of the poll(file, polltable) API ?
3. Tell about the Memory Layout of a Process in Linux ?
4. Tell the relation between malloc() and mmap() ?
5. What is the difference in features between kernel 2.2, 2.4 and 2.6 ?
6. What is a kernel module ?
7. What is the difference between insmod and modprobe ?
8. How will you list the modules ?
9. How do you get the list of currently available drivers ?
10. How will get the driver added into the kernel ? What are Kconfig files ?
11. What are the ways in which linux kernel can be compiled ?
12. What is object file and what are symbols ?
13. Can you tell the memory layout based on Data,BSS,HEAP and STACK ?
14. How will you Access userspace memory from kernel ? What are the various methods ?
15. What is the use of ioctl(inode,file,cmd,arg) ApI ?
16. What is the use of the poll(file, polltable) API ?
17. What is the use of file->private_data in a device driver structure ?
18. What is a device number ?
19. What are the two types of devices drivers from VFS point of view ?
20. What are character devices ?
21. How does the character device driver adds and remove itself from the kernel ?
22. What is the use of register_chrdev and unregister_chrdev ?
23. What is mmap ? MMAP & malloc ? MMAP & brk ? MMAP adv & dis-adv?
24. Advantages of MMAP over Read ?
25. What are Static and Shared libraries ?
26. What is dynamic linking ? What is static linking ?
27. What are the advantages of Dynamic linking or Shared libraries ?
28. Does gcc search for both static and shared libraries ? Which is searched initially by gcc compiler ?
29. What should be done for Shared library based linking in gcc ?
30. What should be done for static library based linking in gcc ?
31. What is the role of interrupts in a device driver ? How are interrupts handled in device driver ?
32. How will you make interrupt handlers as fast as possible ?
33. What are the types of softirqs ?
34. Difference between Timer Softirq and Tasklet Softirq ?
35. What are tasklets ? How are they activated ? when and How are they initialized ?
36. What is task_struct and how are task states maintained ?
37. What is rwlock and spinlock ? Briefly explain about both of them ?
38. When will you use rwlock instead of spinlock ?
39. Can spinlock/rwlock be used in Interrupt handler ?
40. What is Kmalloc and how does it differ from normal malloc ? or Why can't we use malloc in kernel code ?
41. How do you determine the direction of stack growth ?
42. Tell about the method/steps in Linux Kernel Compilation?
43. What is a stack frame, stack pointer & frame pointer ?
44. Tell the role of brk() in malloc / Tell the relation between heap and brk?

 
# #