Linux Developer Interview Questions.

vivx_developer

Vivek P

Posted on October 9, 2024

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
💖 💪 🙅 🚩
vivx_developer
Vivek P

Posted on October 9, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Linux Developer Interview Questions.
developer Linux Developer Interview Questions.

October 9, 2024