DEV Community

Vivek P
Vivek P

Posted on • Edited on

Linux Developer Interview Questions.

Linux System Developer Interview Questions and Answers

Basic Linux Knowledge

Q: What happens when you type 'ls -l' in a terminal? Explain the entire process.

Answer: When you type 'ls -l' and press Enter, the following process occurs:

  1. The shell (e.g., bash) reads the input and parses it.
  2. The shell identifies 'ls' as an external command and '-l' as an argument.
  3. The shell fork()s to create a child process.
  4. The child process uses execve() to replace itself with the 'ls' program.
  5. The 'ls' program:
    • Parses command-line arguments
    • Opens the current directory (or specified directory)
    • Reads directory entries using getdents() system call
    • For each file:
      • Calls stat() to get file information
      • Formats the output (permissions, owner, size, date, name)
    • Writes the formatted output to stdout
  6. The parent shell waits for the 'ls' command to complete
  7. The shell displays a new prompt

Q: What is the Linux kernel?

A: The Linux kernel is the core component of Linux operating systems. It's a free and open-source, monolithic, modular Unix-like operating system kernel. It manages:

  • System hardware resources
  • Process scheduling
  • File systems
  • Device drivers
  • System calls

Key characteristics:

  • Written primarily in C
  • Created by Linus Torvalds in 1991
  • Released under GNU General Public License v2

Q: Explain the boot process of a Linux system.

Answer: The Linux boot process consists of the following stages:

  1. BIOS/UEFI Stage

    • Power-on self-test (POST)
    • Identifies boot device
  2. Bootloader Stage (e.g., GRUB)

    • Loads kernel image into memory
    • Passes control to kernel with initial RAM disk (initrd)
  3. Kernel Stage

    • Initializes hardware and memory
    • Mounts root filesystem
    • Starts init process (PID 1)
  4. Init Stage

    • SystemD or traditional SysV init
    • Starts system services
    • Brings up network interfaces
    • Mounts additional filesystems
  5. Runlevel/Target Stage

    • Reaches the specified runlevel or target
    • System is ready for use

Q: What is the difference between a soft link and a hard link?

Answer:

Hard Links:

  • Share the same inode number as the original file
  • Can't cross filesystem boundaries
  • Can't link to directories (usually)
  • File content is only deleted when all hard links are deleted
  • Same file size as the original file

Example creating a hard link:

ln original.txt hardlink.txt
Enter fullscreen mode Exit fullscreen mode

Soft Links (Symbolic Links):

  • Contain a path to the original file
  • Can cross filesystem boundaries
  • Can link to directories
  • Can become dangling if original file is deleted
  • Very small in size (just contains the path)

Example creating a soft link:

ln -s original.txt softlink.txt
Enter fullscreen mode Exit fullscreen mode

Q: Explain the difference between user space and kernel space.
A:
Kernel Space:

  • Highest privileged level (Ring 0)
  • Full access to hardware
  • Executes kernel code and device drivers
  • Cannot be accessed directly by user applications

User Space:

  • Lower privilege level (Ring 3)
  • Limited access to hardware
  • Executes user applications
  • Must use system calls to access kernel services

Example of crossing the boundary:

// User space code
int fd = open("file.txt", O_RDONLY);  // System call to kernel space

// Kernel space implementation
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{
    // Kernel code here
}
Enter fullscreen mode Exit fullscreen mode

Q: What are system calls? List and explain five common ones.
A: System calls are interfaces between user space programs and the kernel.

Five common system calls:

  1. fork()
pid_t pid = fork();
Enter fullscreen mode Exit fullscreen mode

Creates a new process by duplicating the calling process

  1. read()
ssize_t bytes = read(fd, buffer, count);
Enter fullscreen mode Exit fullscreen mode

Reads data from a file descriptor

  1. write()
ssize_t bytes = write(fd, buffer, count);
Enter fullscreen mode Exit fullscreen mode

Writes data to a file descriptor

  1. open()
int fd = open("file.txt", O_RDONLY);
Enter fullscreen mode Exit fullscreen mode

Opens a file or creates it if it doesn't exist

  1. close()
int status = close(fd);
Enter fullscreen mode Exit fullscreen mode

Closes a file descriptor

Q: Explain the Linux process scheduling algorithm.

A: Linux uses the Completely Fair Scheduler (CFS):

Key concepts:

  1. Virtual Runtime (vruntime)

    • Tracks process execution time
    • Normalized by process priority
  2. Red-Black Tree

    • Processes sorted by vruntime
    • O(log n) insertion and removal

Example of how priority affects scheduling:

struct sched_param param;
param.sched_priority = 51;  // Range: 1-99 for real-time

sched_setscheduler(pid, SCHED_FIFO, &param);
Enter fullscreen mode Exit fullscreen mode

Scheduler classes (in order of priority):

  1. Stop scheduler (internal use)
  2. Deadline scheduler
  3. Real-time scheduler
  4. CFS scheduler
  5. Idle scheduler

Q: What is a page fault? Explain different types.

A: A page fault occurs when a program tries to access memory that is mapped in the virtual address space but not loaded in physical memory.

Types of page faults:

  1. Minor Page Fault

    • Page is in memory but not marked in MMU
    • No disk I/O required
  2. Major Page Fault

    • Page must be loaded from disk
    • Requires disk I/O
  3. Invalid Page Fault

    • Access to invalid memory address
    • Results in segmentation fault

Example of handling page faults in kernel:

static int __do_page_fault(struct mm_struct *mm, unsigned long addr,
                          unsigned int flags, struct task_struct *tsk)
{
    struct vm_area_struct *vma;
    int fault;

    vma = find_vma(mm, addr);
    if (!vma)
        return VM_FAULT_BADMAP;

    fault = handle_mm_fault(vma, addr, flags);
    return fault;
}
Enter fullscreen mode Exit fullscreen mode

Q: What are the different types of process states in Linux?

Answer: Linux processes can be in the following states:

  1. Running (R)

    • Currently executing on a CPU or waiting to be executed
  2. Sleeping

    • Interruptible Sleep (S): Waiting for an event, can be interrupted
    • Uninterruptible Sleep (D): Usually I/O, can't be interrupted
  3. Stopped (T)

    • Process has been stopped, usually by user signal (SIGSTOP)
  4. Zombie (Z)

    • Process has completed but parent hasn't read its exit status
  5. Dead (X)

    • Process is being terminated

Example command to see process states:

ps aux | awk '{print $8}' | sort | uniq -c
Enter fullscreen mode Exit fullscreen mode

System Programming

Q: What is the difference between a process and a thread?

Answer:

Processes:

  • Have separate memory spaces
  • Have their own file descriptors, program counter, stack
  • Communication between processes requires IPC mechanisms
  • Higher overhead for creation and context switching
  • More isolated and secure

Threads:

  • Share the same memory space within a process
  • Share file descriptors, code, and data segments
  • Can communicate through shared memory
  • Lower overhead for creation and context switching
  • Less isolated, potential for race conditions

Example of creating a process vs a thread:

// Process creation
#include <unistd.h>

pid_t pid = fork();
if (pid == 0) {
    // Child process
} else {
    // Parent process
}

// Thread creation
#include <pthread.h>

void* thread_function(void* arg) {
    // Thread code
    return NULL;
}

pthread_t thread;
pthread_create(&thread, NULL, thread_function, NULL);
Enter fullscreen mode Exit fullscreen mode

6. Q: What are signals in Linux? How do you handle signals in a program?

Answer: Signals are software interrupts used for inter-process communication. They can be:

  • Sent by the kernel to processes
  • Sent by processes to other processes
  • Sent by processes to themselves

Common signals:

  • SIGTERM (15): Termination request
  • SIGKILL (9): Immediate termination
  • SIGINT (2): Interactive attention (Ctrl+C)
  • SIGSEGV (11): Segmentation violation

Example of signal handling:

#include <signal.h>
#include <stdio.h>

void signal_handler(int signum) {
    printf("Caught signal %d\n", signum);
}

int main() {
    // Register signal handler
    signal(SIGINT, signal_handler);

    while(1) {
        printf("Running...\n");
        sleep(1);
    }
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Debugging & Performance

Q: How would you profile a Linux application for performance optimization?

Answer: Several tools and techniques can be used:

  1. perf - Linux profiling tool
perf record ./myapp
perf report
Enter fullscreen mode Exit fullscreen mode
  1. gprof - GNU profiler
gcc -pg program.c -o program
./program
gprof program gmon.out > analysis.txt
Enter fullscreen mode Exit fullscreen mode
  1. Valgrind - Memory profiler
valgrind --tool=callgrind ./myapp
Enter fullscreen mode Exit fullscreen mode
  1. strace - Trace system calls
strace -c ./myapp
Enter fullscreen mode Exit fullscreen mode

Key areas to look for:

  • CPU usage (user vs system time)
  • Memory allocation/deallocation patterns
  • I/O operations
  • Cache misses
  • System call usage

Example of using perf to find hotspots:

perf record -g ./myapp
perf report --stdio
Enter fullscreen mode Exit fullscreen mode

Coding Questions

Q: Write a program to create a daemon process.

Answer:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <syslog.h>

int main() {
    // Fork off the parent process
    pid_t pid = fork();
    if (pid < 0) {
        exit(EXIT_FAILURE);
    }
    if (pid > 0) {
        exit(EXIT_SUCCESS);  // Parent exits
    }

    // Create new session
    if (setsid() < 0) {
        exit(EXIT_FAILURE);
    }

    // Fork again (recommended)
    pid = fork();
    if (pid < 0) {
        exit(EXIT_FAILURE);
    }
    if (pid > 0) {
        exit(EXIT_SUCCESS);
    }

    // Set file permissions
    umask(0);

    // Change working directory
    chdir("/");

    // Close all open file descriptors
    for (int x = sysconf(_SC_OPEN_MAX); x >= 0; x--) {
        close(x);
    }

    // Open logs
    openlog("mydaemon", LOG_PID, LOG_DAEMON);

    // Daemon-specific initialization
    while (1) {
        syslog(LOG_NOTICE, "Daemon is running");
        sleep(30);
    }

    closelog();
    return EXIT_SUCCESS;
}
Enter fullscreen mode Exit fullscreen mode

Key points about daemons:

  • Double forking ensures the process isn't a session leader
  • Changing directory to / prevents locking mounted filesystems
  • Closing file descriptors prevents resource leaks
  • Using syslog for logging as stdout/stderr are closed

Q: What are signals in Linux? How do you handle them?
A: Signals are software interrupts used for inter-process communication.

Common signals:

  1. SIGTERM (15) - Termination request
  2. SIGKILL (9) - Immediate termination
  3. SIGINT (2) - Interactive attention (Ctrl+C)
  4. SIGSEGV (11) - Segmentation violation

Signal handling example:

#include <signal.h>

void signal_handler(int signum) {
    printf("Caught signal %d\n", signum);
}

int main() {
    struct sigaction sa;
    sa.sa_handler = signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;

    sigaction(SIGINT, &sa, NULL);

    while(1) {
        sleep(1);
    }
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Sending signals:

// Send signal to another process
kill(pid, SIGTERM);

// Send signal to process group
killpg(pgid, SIGTERM);
Enter fullscreen mode Exit fullscreen mode

Q: What is a deadlock? How can you prevent it?

A: A deadlock occurs when two or more processes are waiting indefinitely for resources held by each other.

Four conditions for deadlock:

  1. Mutual Exclusion
  2. Hold and Wait
  3. No Preemption
  4. Circular Wait

Example of potential deadlock:

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;

void* thread1_function(void* arg) {
    pthread_mutex_lock(&mutex1);
    sleep(1);  // Increase chance of deadlock
    pthread_mutex_lock(&mutex2);
    // ... critical section ...
    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);
    return NULL;
}

void* thread2_function(void* arg) {
    pthread_mutex_lock(&mutex2);
    sleep(1);
    pthread_mutex_lock(&mutex1);
    // ... critical section ...
    pthread_mutex_unlock(&mutex1);
    pthread_mutex_unlock(&mutex2);
    return NULL;
}
Enter fullscreen mode Exit fullscreen mode

Prevention strategies:

  1. Lock ordering
  2. Lock timeout
  3. Deadlock detection
  4. Use lock-free algorithms

Q: Explain the difference between threads and processes.

A:

Processes:

  • Separate address space
  • Higher creation overhead
  • More isolation
  • IPC required for communication

Threads:

  • Shared address space
  • Lower creation overhead
  • Less isolation
  • Can communicate through shared memory

Example of creating both:

// Process creation
pid_t pid = fork();
if (pid == 0) {
    // Child process
    exit(0);
}

// Thread creation
pthread_t thread;
pthread_create(&thread, NULL, thread_function, NULL);
pthread_join(thread, NULL);
Enter fullscreen mode Exit fullscreen mode

Memory comparison:

// Process - separate memory
int main() {
    int x = 5;
    if (fork() == 0) {
        x = 6;  // Only changes in child
        exit(0);
    }
    // Parent's x is still 5
}

// Thread - shared memory
void* thread_func(void* arg) {
    int* x = (int*)arg;
    *x = 6;  // Changes for all threads
    return NULL;
}
Enter fullscreen mode Exit fullscreen mode

Q: What is a race condition? How can you prevent it?

A: A race condition occurs when multiple threads access shared data concurrently, and the outcome depends on the order of execution.

Example of a race condition:

int counter = 0;

void* increment(void* arg) {
    for (int i = 0; i < 1000000; i++) {
        counter++;  // Race condition here
    }
    return NULL;
}

int main() {
    pthread_t t1, t2;
    pthread_create(&t1, NULL, increment, NULL);
    pthread_create(&t2, NULL, increment, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    printf("Counter: %d\n");  // Will be less than 2000000
}
Enter fullscreen mode Exit fullscreen mode

Prevention methods:

  1. Mutex
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void* safe_increment(void* arg) {
    for (int i = 0; i < 1000000; i++) {
        pthread_mutex_lock(&mutex);
        counter++;
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}
Enter fullscreen mode Exit fullscreen mode
  1. Atomic Operations
#include <stdatomic.h>
atomic_int counter = 0;

void* atomic_increment(void* arg) {
    for (int i = 0; i < 1000000; i++) {
        atomic_fetch_add(&counter, 1);
    }
    return NULL;
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)