Introduction
One of the reasons C remains important today is that operating systems such as FreeBSD and Linux are largely written in C. At first glance, C programming on FreeBSD and Linux appears almost identical. Both systems provide a POSIX-compatible environment, use the GCC or Clang compilers, and offer similar development tools.
However, there are important differences in system libraries, APIs, kernel interfaces, development tools, and operating system design that programmers should understand.
This article explores the similarities and differences between developing C applications on FreeBSD and Linux.
The Similarities
Most user-space C programs compile on both systems with little or no modification.
For example:
#include <stdio.h>
int main(void)
{
printf("Hello World\n");
return 0;
}
Compile on Linux:
gcc hello.c -o hello
Compile on FreeBSD:
cc hello.c -o hello
Both systems provide:
- POSIX APIs
- Standard C Library
- File I/O
- Processes
- Signals
- Sockets
- Threads
- Dynamic libraries
For many command-line utilities, no code changes are required.
Compiler Differences
Linux
Linux distributions typically use:
- GCC (GNU Compiler Collection)
- Clang (optional)
Example:
gcc program.c -o program
or
clang program.c -o program
FreeBSD
FreeBSD switched from GCC to Clang years ago.
The default compiler is usually:
cc
which is Clang underneath.
Example:
cc program.c -o program
or
clang program.c -o program
Advantages include:
- Faster compilation
- Better diagnostics
- Simpler licensing
The C Standard Library
Linux
Most Linux systems use:
glibc
The GNU C Library provides:
- POSIX functions
- GNU extensions
- Linux-specific features
Examples:
strdupa()
getline()
pthread_setname_np()
Many GNU extensions exist that are not portable.
FreeBSD
FreeBSD uses:
libc
which is developed as part of the operating system itself.
Benefits:
- Smaller
- Tightly integrated with the OS
- Consistent across releases
Some GNU-specific functions are unavailable.
System Calls
This is where the biggest differences begin.
A C program often calls a library function:
read(fd, buffer, size);
Eventually that becomes a kernel system call.
Linux and FreeBSD implement different kernels.
The APIs look similar:
open()
read()
write()
close()
but internally:
- Different syscall numbers
- Different kernel implementations
- Different kernel subsystems
The programmer rarely notices unless writing low-level software.
Process Management
Most POSIX process functions are identical.
fork();
exec();
wait();
work on both systems.
Example:
pid_t pid = fork();
if(pid == 0)
{
execl("/bin/ls", "ls", NULL);
}
This behaves similarly on both platforms.
Threading
Both systems support POSIX threads.
Example:
#include <pthread.h>
Creating a thread:
pthread_create();
pthread_join();
works on both.
Historically:
Linux:
- NPTL (Native POSIX Thread Library)
FreeBSD:
- libthr
Most modern applications see little difference.
Socket Programming
Networking code is highly portable.
Example:
socket();
bind();
listen();
accept();
connect();
works on both systems.
Example TCP server:
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
No changes are usually required.
Event Notification Systems
This is one area where differences are significant.
Linux: epoll
Linux uses:
epoll_create()
epoll_wait()
for scalable I/O.
Example:
epoll_wait(epfd, events, maxevents, timeout);
Used by:
- Nginx
- Redis
- Node.js
FreeBSD: kqueue
FreeBSD uses:
kqueue()
kevent()
Example:
int kq = kqueue();
Many developers consider kqueue cleaner and more flexible.
Used by:
- FreeBSD services
- High-performance network servers
Programs that directly use epoll require changes to work on FreeBSD.
Device Access
Linux exposes devices through:
/dev
FreeBSD also uses:
/dev
However:
- Different drivers
- Different ioctl interfaces
- Different device names
Example:
Linux:
/dev/sda
FreeBSD:
/dev/ada0
Low-level device software often requires porting.
Kernel Modules
Linux:
insmod
modprobe
Kernel code:
module_init();
module_exit();
FreeBSD:
kldload
kldunload
Kernel modules use different APIs.
Example:
DECLARE_MODULE(...)
Linux kernel modules cannot simply be recompiled on FreeBSD.
They must be rewritten.
Package and Build Systems
Linux
Common tools:
apt
dnf
pacman
Development packages:
apt install build-essential
FreeBSD
Uses:
pkg
pkg install gcc
and
Ports Collection
cd /usr/ports/category/program
make install clean
The Ports Collection allows compiling software from source with custom options.
Header File Differences
Many programs compile unchanged.
However, some Linux-specific code includes headers such as:
#include <linux/if.h>
or
#include <sys/epoll.h>
These do not exist on FreeBSD.
Equivalent FreeBSD headers must be used.
Linux-Specific APIs
Examples:
epoll
inotify
signalfd
eventfd
timerfd
Programs depending on these APIs require modification.
FreeBSD-Specific APIs
Examples:
kqueue
jails
capsicum
These features have no direct Linux equivalent.
Programs using them become FreeBSD-specific.
Security Features
Linux developers may use:
- SELinux
- AppArmor
- seccomp
FreeBSD developers may use:
- Jails
- Capsicum
- MAC Framework
Example:
FreeBSD’s Capsicum can restrict a process to only specific file descriptors.
This can greatly reduce attack surface.
Kernel Development
For kernel developers, the differences are enormous.
Linux kernel code:
struct file_operations
FreeBSD kernel code:
struct cdevsw
Memory management, scheduling, locking, drivers, and filesystems all use different APIs.
Kernel programming knowledge does not transfer directly.
Portability Tips
To write portable C code:
- Follow POSIX standards.
- Avoid GNU-specific extensions.
- Avoid Linux-only APIs.
- Use feature tests.
- Test on multiple operating systems.
- Use standard networking APIs.
- Prefer portable libraries when possible.
Example:
#ifdef __linux__
/* Linux code */
#endif
#ifdef __FreeBSD__
/* FreeBSD code */
#endif
Conclusion
For user-space programming, FreeBSD and Linux are very similar. Most command-line tools, network programs, and utilities compile on both systems with little effort.
The largest differences appear when working with:
- System calls
- Event notification systems
- Device drivers
- Kernel modules
- Security frameworks
- Operating-system-specific APIs
If you learn standard POSIX C programming, your skills will transfer well between FreeBSD and Linux. If you move into systems programming, kernel development, or advanced networking, understanding the unique features of each operating system becomes increasingly important.
The good news is that learning one platform makes learning the other much easier because both share a common UNIX heritage and a strong commitment to C programming.