Illumos makes me nostalgic

During 20102014, I worked as a software engineer in a telecommunication company which uses Solaris&SPARC as its server, and the working experience is a really pleasant memory for me. So even I have already left the company for more than 5 years, when I meet illumos, it always lets me reminisce about some good old days.

Before I joined in that company, I had only used some Linux distributions, e.g, Fedora 4. Honestly, I didn’t know what is the difference between Linux and Solaris at that time. I still remember the first Solaris tool I learned is pstack, which my colleague showed me how to analyze application’s core dump in a brief way. Although Solaris ships its own debugger, i.e., mdb, it was a shame I was still used to use gdb (installed from Sunfreeware). Since only gdb 6.x was provided, I always tried to build cutting-edge gdb from source code, and it worked smoothly on Solaris.

When talking about our commercial server, IIRC, it had only 2GiB memory. Yes, in the second decade of 21st century, when notebook already has 8GiB memory, our 32-bit application run on 2GiB machine.  Both sever and application could date back to 2005. Our company didn’t have a big name, and throughput of our application was ~30 transactions per second. The server was surprisingly stable. I can’t remember we ever restarted it once during 20102014, and it matched what this picture has said (a Solaris server run for more than 10 years).

Last week, I came across this tweet, so I wanted to relive Solaris once again (even though it is called illumos now). Download and install OmniOS, quite straightforward. Build illumos code, meet some problems, but fix them with the help of enthusiastic people. Find a typo in manual, go through the whole flow of contributing: create issue, send patch to review, send RTI, done! The flow is stricter than “fire-and-forget” mode, but I think it is necessary for code quality. BTW,  I find some Solaris‘s peculiar still exist on illumos. E.g., even in 64-bit environment, gcc will generate 32-bit executable by default, and you should use “-m64” compile option to claim you want 64-bit one.

Thanks for your time and patience to read this ramble rants. If there is only one take-away, that is besides Linux and BSD families, there is another less-popular Unix: illumos. Though it is a small community, it doesn’t have outdated tools. You can give it a shot if you are interested, and maybe you will like it.

Build gdb on illumos

Claim: Since illumos provides its own debugger: mdb, I don’t want to convince anybody to use gdb instead of mdb. Compile and use gdb is just my personal hobby.

I use OmniOS, and I think this material should also apply to other illumos OSs and even Solaris:

(1) Download and extract gdb source code (currently the newest versin is 8.3);
(2) Enter the gdb directory and do configuration:

# cd gdb-8.3
# mkdir build
# ../configure CC='gcc -m64'  CXX='g++ -m64'

(3) Build it:

# gmake

After compiling, testing it:

# ./gdb-8.3/build/gdb/gdb --data-directory=./gdb-8.3/build/gdb/data-directory/ -q a.out
Reading symbols from a.out...
(gdb) start
Temporary breakpoint 1 at 0x4011b6: file test.c, line 4.
Starting program: /root/a.out
[Thread debugging using libthread_db enabled]
[New Thread 1 (LWP 1)]
[Switching to Thread 1 (LWP 1)]

Thread 2 hit Temporary breakpoint 1, main () at test.c:4
4               printf("%d\n", sizeof(long unsigned int));
(gdb) n
8
5               return 0;
(gdb)
6       }
(gdb)

So far so good!

Build illumos source code on OmniOS

To build illumos source code on OmniOS, you need to update system and install related tools (Assume you run as root) first:

# pkg update
# pkg install pkg:/developer/illumos-tools

Then clone illumos source code and compile ():

# git clone https://github.com/illumos/illumos-gate.git
# cd illumos-gate
# time ksh93 usr/src/tools/scripts/nightly /opt/onbld/env/omnios-illumos-gate

That’s it!

References:
Building illumos;
Build error of illumos-gate on OmniOS.

Install OmniOS on VirtualBox

Following document to install OmniOS is quite straightforward. I use r151030 version and pick Solaris 11 during selecting Operating Systems on VirtualBox. To use root login through SSH, I need to do following changes:

(1) Set PermitRootLogin‘s value to yes in /etc/ssh/sshd_config file;

(2) Restart SSH service:

# svcadm restart ssh

A fresh OmniOS is born:

Update:

When installing r151032, I found selecing Oracle Solaris 10 10/09 and later (64-bit) can work.

Reference:
Solaris : How to enable ssh login for root user after a fresh install.

 

A trick of building multithreaded application on Solaris

Firstly, Let’s see a simple multithreaded application:

#include <stdio.h>
#include <pthread.h>
#include <errno.h>

void *thread1_func(void *p_arg)
{
           errno = 0;
           sleep(3);
           errno = 1;
           printf("%s exit, errno is %d\n", (char*)p_arg, errno);
}

void *thread2_func(void *p_arg)
{
           errno = 0;
           sleep(5);
           printf("%s exit, errno is %d\n", (char*)p_arg, errno);
}

int main(void)
{
        pthread_t t1, t2;

        pthread_create(&t1, NULL, thread1_func, "Thread 1");
        pthread_create(&t2, NULL, thread2_func, "Thread 2");

        sleep(10);
        return;
}

What output do you expect from this program? Per my understanding, the errnoshould be a thread-safe variable. Though The thread1_func function changes theerrno, it should not affect errno in thread2_func function.

Let’s check it on Solaris 10:

bash-3.2# gcc -g -o a a.c -lpthread
bash-3.2# ./a
Thread 1 exit, errno is 1
Thread 2 exit, errno is 1

Oh! The errno in thread2_func function is also changed to 1. Why does it happen? Let’s find the root cause from the errno.h file:

/*
 * Error codes
 */

#include <sys/errno.h>

#ifdef  __cplusplus
extern "C" {
#endif

#if defined(_LP64)
/*
 * The symbols _sys_errlist and _sys_nerr are not visible in the
 * LP64 libc.  Use strerror(3C) instead.
 */
#endif /* _LP64 */

#if defined(_REENTRANT) || defined(_TS_ERRNO) || _POSIX_C_SOURCE - 0 >= 199506L
extern int *___errno();
#define errno (*(___errno()))
#else
extern int errno;
/* ANSI C++ requires that errno be a macro */
#if __cplusplus >= 199711L
#define errno errno
#endif
#endif  /* defined(_REENTRANT) || defined(_TS_ERRNO) */

#ifdef  __cplusplus
}
#endif

#endif  /* _ERRNO_H */

We can find the errno can be a thread-safe variable(#define errno (*(___errno()))) only when the following macros defined:

defined(_REENTRANT) || defined(_TS_ERRNO) || _POSIX_C_SOURCE - 0 >= 199506L

Let’s try it:

bash-3.2# gcc -D_POSIX_C_SOURCE=199506L -g -o a a.c -lpthread
bash-3.2# ./a
Thread 1 exit, errno is 1
Thread 2 exit, errno is 0

Yes, the output is right!

From Compiling a Multithreaded Application, we can see:

For POSIX behavior, compile applications with the -D_POSIX_C_SOURCE flag set >= 199506L. For Solaris behavior, compile multithreaded programs with the -D_REENTRANT flag.

So we should pay more attentions when building multithreaded application on Solaris.

P.S., the full code is here.

Reference:
(1) Compiling a Multithreaded Application;
(2) What is the correct way to build a thread-safe, multiplatform C library?