Is there a macro definition to check the Linux kernel version? - c

I'm wondering if there is a gcc macro that will tell me the Linux kernel version so I can set variable types appropriately. If not, how would I go about defining my own macro that does this?

The linux/version.h file has a macro called KERNEL_VERSION which will let you check the version you want against the current linux headers version (LINUX_VERSION_CODE) installed. For example to check if the current Linux headers are for kernel v2.6.16 or earlier:
#include <linux/version.h>
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)
...
#else
...
#endif
A better way to get the version information at run-time is to use the utsname function in include/linux/utsname.h.
char *my_kernel_version = utsname()->release;
This is essentially how /proc/version gets the current kernel verison.
See also
Getting kernel version from linux kernel module at runtime

gcc won't know this information. As an alternative, you can determine a lot of kernel information at runtime easily.
You can define your runtime type like
struct unified_foo {
unsigned int kernel_version;
union {
kernel_x_foo_type k_x;
kernel_y_foo_type k_y;
kernel_z_foo_type k_z;
} u;
};
and have code at runtime look at /proc/version (or whatever you need from the kernel runtime environment) and set kernel_version approriately. The kernel_x_foo_type et al. is your type that you want to be conditional on the kernel version. The calling code needs to look at kernel_version and access the appropriate u.k_x, u.k_y, or u.k_z data.

In kernel code first Makefile. You will find version-related variables.
VERSION = 4
PATCHLEVEL = 9
SUBLEVEL = 37

Related

Conditional compilation based on functionality in Linux kernel headers

Consider the case where I'm using some functionality from the Linux headers exported to user space, such as perf_event_open from <linux/perf_event.h>.
The functionality offered by this API has changed over time, as members have been added to the perf_event_attr, such as perf_event_attr.cap_user_time.
How can I write source that compiles and uses these new functionalities if they are available locally, but falls back gracefully if they aren't and doesn't use them?
In particular, how can I detect in the pre-processor whether this stuff is available?
I've used this perf_event_attr as an example, but my question is a general one because structure members, new structures, definitions and functions are added all the time.
Note that here I'm only considering the case where a process is compiled on the same system that it will run on: if you want to compile on one host and run on another you need a different set of tricks.
Use the macros from /usr/include/linux/version.h:
#include <linux/version.h>
int main() {
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)
// ^^^^^^ change for the proper version when `perf_event_attr.cap_user_time` was introduced
// use old interface
#else
// use new interface
// use perf_event_attr.cap_user_time
#endif
}
You might go into this with the following assumptions
The features available in the header files correspond to those documented for the specific Linux version.
The kernel running during execution corresponds to <linux/version.h> during compilation
Ideally, I suggest not to rely on these two assumptions at all.
The first assumption fails primarily due to backports, e.g. in enterprise Linux versions based on ancient kernels. If you care about different versions, you probably care about them.
Instead, I recommend utilizing the methods for checking for struct members and include files in build system, e.g. for CMake:
CHECK_STRUCT_HAS_MEMBER("struct perf_event_attr" cap_user_time linux/perf_event.h HAVE_PERF_CAP_USER_TIME)
CHECK_INCLUDE_FILES can also be useful.
The second assumption can fail for many reasons, even if the binary is not moved between systems; E.g. updating the kernel but not recompiling the binary or simply booting another kernel. Specifically perf_event_open fails with EINVAL if a reserved bit is set. This allows you to retry with an alternative implementation not using the requested feature.
In short, statically check for the feature instead of the version. Dynamically, try and retry the legacy implementation if it failed.
Just in addition to other answers.
If you're aiming for supporting both cross-version and cross-distro code, you should also keep in mind that there are distros (Centos/RHEL) which pull some recent changes from new kernels to old. So you may encounter a situation in which you'll have LINUX_VERSION_CODE equal to some old kernel version, but there will be some changes (new fields in data structures, new functions, etc.) from recent kernel. In such case this macro is insufficient.
You can add something like (to avoid preprocessor errors in case it is not a Centos distro):
#ifndef RHEL_RELEASE_CODE
#define RHEL_RELEASE_CODE 0
#endif
#ifndef RHEL_RELEASE_VERSION
#define RHEL_RELEASE_VERSION(x,y) 1
#endif
And use it with > or >= where you need:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,3,0) || RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,2)
...
for Centos/RHEL custom kernels support.
P.S. of course it's necessary to examine an appropriate versions of Centos/RHEL, and understand when and what exactly has changed in the code sections that affect you.

How do I get the User's operating system?

I am writing a C program, part of which requires me to get the User's operating system. For example, to get the home directory I can do the following:
const char *homedir;
homedir = getpwuid(getuid())->pw_dir;
printf("Home dir: %s\n",homedir);
I can not locate anything related to the OS in pwd.h is there a different file I should be looking at ?
Most of the times (it's not guaranteed), a C compiler defines macros that identify the current operating system (the macro-family, at the very least).
Eg. to identify Windows, Linux and Mac you could do:
#import <stdio.h>
typedef enum {
UNKNOWN,
WINDOWS,
LINUX,
OSX
} OS;
OS current_os();
int main() {
printf("%d",current_os());
return 0;
}
OS current_os() {
#ifdef _WIN32
return WINDOWS;
#elif (defined(__APPLE__) && defined(__MACH__))
return OSX;
#elif defined(__gnu_linux__)
return LINUX;
#else
return UNKNOWN;
#endif
}
On my Mac this prints 3, like it's supposed to do.
You can find a comprehensive list of those macros here.
On SVr4 and POSIX.1-2001 and later compliant UNIXes, use uname() -- for more info, man 2 uname to info about which UNIX, kernel version, and hardware identifier you are on.
Not sure for non-UNIX.
http://man7.org/linux/man-pages/man2/uname.2.html
On Windows, you'll use Win API functions to get exact version (such as GetVersionEx API call).
On Linux - you can use uname(struct utsname *buf) call (man 2 uname).
Other platforms may provide different APIs.
If you want to be portable across operating systems, you should provide a layer of abstraction that hides OS-specific calls and compile-in proper platform-specific implementation that shares a common interface.
Abstractions - this is The Way in cross-platform programming.
Alternatively, you may use pre-processor, which provides some basic platform information at compile-time. Preprocessor is used quite often to select platform-specific code to compile.

Can I use <stdatomic.h> from C11 in Linux driver, or do I must to use Linux functions of memory-barriers?

Can I use #include <stdatomic.h> and atomic_thread_fence() with memory_order from C11 in Linux driver (kernel-space), or do I must to use Linux functions of memory-barriers:
http://lxr.free-electrons.com/source/Documentation/memory-barriers.txt
http://lxr.free-electrons.com/source/Documentation/atomic_ops.txt
Using:
Linux-kernel 2.6.18 or greater
GCC 4.7.2 or greater
If you are writing kernel code, you should do it in C, and do it in the version of C required by the current kernel (shipping gcc). If you want to get it accepted into mainline (or write it as if it were going to get accepted), you should use the Linux functions. You will also find that they work without unexpected surprises, and you will get better debugging help.
Summary: use the linux functions.
EDIT:
It seems not to work.
With or without does not make any difference.
Driver may compile but the lib will fallback to plain integers or NOP
It seems to work.
atomic_store() and atomic_load() provide the threads synchronization I need between the kernel module driver and the userland program.
What is not sure is that if a fallback method is employed, I mean, usage of standard integer and regular assembly instructions by the compiler.
Feel free to give a look in source codes
in functions:
intelfreq.c / Core_Cycle()
and
corefreqd.c / Core_Cycle()

How to use Linux-specific APIs and libraries only on Linux builds with CMake?

I have a project that I run on Linux (primarily), but sometimes on Darwin/Mac OS X. I use CMake to generate Makefiles on Linux and an Xcode project on Mac OS X. So far, this has worked well.
Now I want to use some Linux-specific functions (clock_gettime() and related functions). I get linker errors on Mac OS X when I try to use clock_gettime(), so I assume it is only available on Linux. I am prepared to introduce conditionally-compiled code in the .c files to use clock_gettime() on Linux and plain old clock() on Mac OS. (BTW I was planning to use #include <unistd.h> and #if _POSIX_TIMERS > 0 as the preprocessor expression, unless someone has a better alternative.)
Things get tricky when it comes to the CMakeLists.txt file. What is the preferred way of introducing linkage to Linux-specific APIs only under the Linux build in a cross-platform CMake project?
Note: An earlier revision of this question contained references to glibc, which was overly specific and confusing. The question is really about the right way to use Linux-specific APIs and libraries in a cross-platform CMake project.
Abstracting away from your examples, and answering only this question:
How to use Linux-specific APIs and libraries only on Linux builds with
CMake?
CMake provides numerous useful constants that you can check in order to determine which system you are running:
if (${UNIX})
# *nix-specific includes or actions
elsif (${WIN32})
# Windows-specific includes or actions
elsif (${APPLE})
# ...
endif (${UNIX})
(I know you're asking about glibc, but you really want to know whether clock_gettime is present, right? But nothing in your question is Linux-specific...)
If you want to check for clock_gettime, you can use the preprocessor. If clock_gettime is present, then _POSIX_TIMERS will be defined. The clock_gettime function is part of an optional POSIX extension (see spec), so it is not Linux-specific but not universal either. Mac OS X does not have clock_gettime: it is not declared in any header nor defined in any library.
#include <time.h>
#include <unistd.h> /* for _POSIX_TIMERS definition, if present */
#if _POSIX_TIMERS
...use clock_gettime()...
#else
...use something else...
#endif
This doesn't solve the problem that you still have to link with -lrt on Linux. This is typically solved with something like AC_CHECK_LIB in Autoconf, I'm sure there's an equivalent in CMake.
From man 2 clock_gettime:
On POSIX systems on which these functions are available, the symbol _POSIX_TIMERS is defined in <unistd.h> to a value greater than 0. The symbols _POSIX_MONOTONIC_CLOCK, _POSIX_CPUTIME, _POSIX_THREAD_CPUTIME indicate that CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID, CLOCK_THREAD_CPUTIME_ID are available. (See also sysconf(3).)
On Darwin you can use the mach_absolute_time function if you need a high-resolution monotonic clock. If you don't need the resolution or monotonicity, you should probably be using gettimeofday on both platforms.
There is also built-in CMake macro for checking if symbol exists - CheckSymbolExists.

C macro for OS X version (Lion or not) detection?

Is there a predefined C macro for detecting the version of OS X? I know __APPLE__ and __MACH__ exist, but those are binary. Is there a specific value for __APPLE_CC__ that indicates Lion?
In particular, Lion added a definition of getline() to <stdio.h> in Lion and it would be nice to be able to detect whether or not code was compiling on Lion or not to work around compilation errors.
Specifically, I'm referring to building Unix C code in Bash (outside of XCode).
The Availability.h macros allow you to check for compile- and run-time version dependencies. See the discussion here.
Check in /usr/include/AvailabilityMacros.h - it contains macros such as:
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7
#define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER DEPRECATED_ATTRIBUTE
#else
#define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER
#endif
I came across this stuff because 'openssl/sha1.h' has been slathered with 'deprecated' attributes for Lion, so compiling git gets warnings galore.

Resources