Embedded Linux System Programming - Unit 3
1. System Programming Concepts
System programming refers to writing code that directly interacts with the operating system using
system calls. These include process control, file manipulation, device management, etc.
Common headers:
- <unistd.h>: Declares standard symbolic constants and types, includes read(), write(), fork(), etc.
- <fcntl.h>: File control options for open(), etc.
Example (write system call):
#include <unistd.h>
int main() {
char msg[] = "Hello, system call!\n";
write(1, msg, sizeof(msg)); // 1 is the file descriptor for stdout
return 0;
}
Why use 1?
FD 0: stdin, FD 1: stdout, FD 2: stderr
2. File Operation Programming
We can use system calls to perform file operations (open, read, write, close).
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd = open("example.txt", O_CREAT | O_WRONLY, 0644);
write(fd, "File I/O in Linux\n", 18);
close(fd);
return 0;
}
- open(): opens/creates a file (O_CREAT flag)
- write(): writes to the file
- close(): closes file descriptor
- 0644: file permission (rw-r--r--)
3. IO Operation Programming
We use read() and write() system calls for low-level I/O.
#include <fcntl.h>
#include <unistd.h>
int main() {
char buffer[100];
int fd = open("example.txt", O_RDONLY);
int n = read(fd, buffer, 100);
write(1, buffer, n);
close(fd);
return 0;
}
Here, we:
- Read up to 100 bytes from the file
- Write the content to stdout (fd = 1)
4. Advanced Process Management Programming
Key system calls:
- fork(): creates a new child process
- exec(): replaces process image
- wait(): waits for child
- exit()/_exit(): terminates process
Zombie process: child finished, parent didn't wait()
Orphan process: parent terminated before child
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
execlp("ls", "ls", NULL);
} else {
wait(NULL);
printf("Child process finished\n");
}
return 0;
}
5. exec() without fork() and exit() vs _exit()
exec() can be used without fork(), but it will replace the current process image, so no code after
exec() runs.
exit() vs _exit():
- exit(): flushes stdio buffers, runs cleanup handlers
- _exit(): immediately terminates, no flushing
Flushing means writing any buffered output (like printf) to output files or screen.
6. Thread Programming (pthread_create and pthread_join)
POSIX threads are created with pthread_create()
Syntax:
pthread_create(&thread_id, NULL, function, arg);
- function and arg are void* for generality
pthread_join(thread_id, NULL); // Waits for thread to complete
#include <stdio.h>
#include <pthread.h>
void* print_message(void* arg) {
printf("Hello from thread!\n");
return NULL;
}
int main() {
pthread_t tid;
pthread_create(&tid, NULL, print_message, NULL);
pthread_join(tid, NULL);
return 0;
}
7. Race Condition and Mutex
Race condition: multiple threads access shared data at the same time.
Mutex prevents race conditions by locking the critical section.
#include <stdio.h>
#include <pthread.h>
int counter = 0;
pthread_mutex_t lock;
void* increment(void* arg) {
for (int i = 0; i < 100000; i++) {
pthread_mutex_lock(&lock);
counter++;
pthread_mutex_unlock(&lock);
}
return NULL;
}
int main() {
pthread_t t1, t2;
pthread_mutex_init(&lock, NULL);
pthread_create(&t1, NULL, increment, NULL);
pthread_create(&t2, NULL, increment, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_mutex_destroy(&lock);
printf("Final counter: %d\n", counter);
return 0;
}