|Linux| Synchronization: mutex v.s. semaphore

If you use multi-threads to do something at the same time, that is a Synchronization.
You need to control threads to avoid Race Condition and Critical Section.
mutex and semaphore can solve the critical section.
But what’s different?

Mutex is an object and binary semaphore .

A semaphore is a signal, and a thread that is waiting on a semaphore can be signaled by another thread (more then 2 threads).
But you should notice that the wait and signal operations require to be executed in the correct order. Nevertheless, deadlocks will occur in semaphore.

Roughly, a mutex would be referred to as binary semaphore.
There are many dufferences, you may refer to this website.
https://www.guru99.com/mutex-vs-semaphore.html

How can we use semaphore?

// header
#include <semaphore.h>
class Foo {
    private:
        //declaration
        sem_t firstDone;
        sem_t secondDone;
    
    public:
        Foo() {
            // initialize
            sem_init(&firstDone, 0, 0);
            sem_init(&secondDone, 0, 0);
        }

        void first(function<void()> printFirst) {
            printFirst();
            //broadcast signl: first has done
            sem_post(&firstDone);
        }

        void second(function<void()> printSecond) {
            // waiting the signal from firstDone
            sem_wait(&firstDone);
            printSecond();
            //broadcast signl: second has done
            sem_post(&secondDone);
        }

    void third(function<void()> printThird) {
            // waiting the signal from secondDone
            sem_wait(&secondDone);
            printThird();
    }
};

Ref.
https://www.guru99.com/mutex-vs-semaphore.html
https://sls.weco.net/node/21326
https://jasonblog.github.io/note/linux_system/mutex_yu_semaphore_zui_da_de_cha_yi_shi.html

|C/C++| What are argc and argv ?

In main function, there are two parameters, argc and argv.

int main( int argc, char **argv)

argc := argument count, which is the number of parameters.
argv := argument value, which assign by terminal from user.
argv[0] := the name of this program
argv[1], argv[2] := enter by user


Test:

#include <stdio.h>

int main( int argc, char **argv ){
    int i = 0;
    printf( “argc = %d\n”, argc);
    for( i=0; i!=argc; ++i){
        printf( “argv[%d] = %s\n”, i, argv[i] );
    }
    return 0;
}

|C/C++| Use dlopen and dlsym to get handler of dynamic library(.so)

If you have a dynamic library (libtest.so) and there is a function (int test_foo(float)) in this library, how can I reuse the function in another library?

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

int main(int argc, char **argv)
{
   void     *handle;
   
   // An function pointer, for point to test_foo
   int   (*test_foo_pointer)(float);
   char     *error;

   //open the target library libtest.so
   handle = dlopen("libtest.so", RTLD_LAZY);
   if (!handle) {
       fprintf(stderr, "%s\n", dlerror());
       exit(EXIT_FAILURE);
   }

   dlerror();    // Clear any existing error


   // get test_foo and assign to test_foo_pointer 
   test_foo_pointer = dlsym(handle, "test_foo");
   
   if ((error = dlerror()) != NULL)  {
       fprintf(stderr, "%s\n", error);
       exit(EXIT_FAILURE);
   }

   dlclose(handle);
   exit(EXIT_SUCCESS);
}


|Android| How was the Android system after power up?

This article will stand on high level to scan initialization modules.

User press power key ……

First, Boot ROM
 - Loads the BootLoader into RAM and starts executing.

Second, BootLoader
 - Start up and find the system kernel.
 - Bootloader is a place where manufacturers put their locks
  and restrictions.
 - Detects external RAM
 - Setups the network, memory … etc, which requires to run Kernel.

Third, Kernel
 - Setup cache
  Protected memory
  Scheduling
  Loads drivers
  Starts kernel daemons
  Mounts root file system
  Initializing Input/Output
  Starts interrupts
  Initializes process table ……
 - Looks for “init” in system files
 - Launch root process

Forth, Init
 - Mounts directories like /sys, /dev or/proc
 - Runs /init.rc script. The init.rc is responsible for the initial set up of the system.

Fifth, (Android) Zygote: 
 - VM process that starts as the system boots
 - app_process launces Zygote

Sixth, SystemServer
 - Load a native library called android_servers

ref. for Android: https://maoao530.github.io/2017/01/06/android-start/

refs.
https://blog.csdn.net/a4262562/article/details/76779236
https://danielmaker.github.io/blog/linux/start_kernel.html
https://cjworld1208.pixnet.net/blog/post/8014497
http://embeddedvenkatpari.blogspot.com/2016/05/how-to-load-firmware-using-pil.html?m=1
https://www.twblogs.net/a/5b8d19262b717718833b2c22
http://albert-oma.blogspot.com/2016/07/embedded-u-boot.html?m=1

|Java| Say Hello to JNI

Although Java is easy to transfer platforms, we still need some C++/C libraries to control drivers or basic modules.

JNI can be an interface between Java and C++/C.
The following article will teach you step by step.

OS: Ubuntu 18.04
Java: Open JDK 8


Step 1. Implement Java: HelloWorldJNI.java

class HelloWorldJNI {
    private native void printByC();
    public static void main(String[] args) {
        new HelloWorldJNI().printByC();
    }
    static {
        System.setProperty("java.library.path", "~/Desktop/JNI/");
        System.loadLibrary("HelloWorldJNI");
    }
}

Step 2. Generate Class and Header
(open your terminal)
$ javac HelloWorldJNI.java
$ javah -jni HelloWorldJNI

Then HelloWorldJNI.class and HelloWorldJNI.h are created.

HelloWorldJNI.h:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorldJNI */

#ifndef _Included_HelloWorldJNI
#define _Included_HelloWorldJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloWorldJNI
 * Method:    printByC
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloWorldJNI_printByC
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

Step 3. Implement your C++/C library: HelloWorldJNI.c

#include <jni.h>
#include <stdio.h>
#include "HelloWorldJNI.h"

JNIEXPORT void JNICALL Java_HelloWorldJNI_printByC (JNIEnv *env, jobject obj){

    printf("Hello from C\n");
    return;
}

Step 4. Build C++/C library as .so
(open your terminal)
$ gcc -shared -I/usr/lib/jvm/java-8-openjdk-amd64/include -I/usr/lib/jvm/java-8-openjdk-amd64/include/linux HelloWorldJNI.c -o libHelloWorldJNI.so

The name ,”lib“+name.so, is important because the definition of “System.loadLibrary” will recognize “lib”.

Step 5. Test
(open your terminal)
$ java HelloWorldJNI


OUTPUT:
$ Hello from C

https://github.com/zivww/HelloJNI

|Linux| what is the difference between subsys_initcall and module_init ?

1. > If you trace two macro, both macro will be replace by __define_initcall(fn, *).
Hence, we can know that subsys_initcall will run earlier than module_init after INIT deamon stars ( do_initcalls ).

2. >
subsys_initcall() can only be used by a built-in (statically linked) module.
module_init can be used by either built-in or loadable modules.
For loadable *.ko modules, subsys_initcall() is mapped to the same as module_init() i.e. device_initcall()
ONLY one module_init() per driver module.

Reference,
root/include/linux/init.h
https://www.cnblogs.com/dakewei/p/10995453.html

|Design| Benefits of using Macro

What is the Macro?
If you write as “#define” in your code, that is “Macro” .

Macro will handle by the preprocessor.
You can think Macro as a method to replace the String which appears in you code..

pros:
– run faster than writing as function

cons:
– occupy memories

concepts:
– there is no the concept of pointer in macro.
just “replacing the string”

An question:
What is the output of the following code?

#define macro_to_func(pp) ({printf("in Macro ...\n"); minus ;})

void do_something(int *a, int *b){
    printf("do_something\n");
    int c;
    c = *a + *b;
    printf("do_something %d \n", c);
}

int minus(int *a, int *b){
    printf("minus\n");
    int c;
    c = *a - *b +1;
    printf("minus a = %d \n", *a);
    printf("minus b = %d \n", *b);
    printf("minus %d \n", c);
    return c;
}

int main()
{
    int (*do_something)(int* c, int* d);
    //printf("fptr 1 do_something %p\n", &do_something);

    int m = -1;
    int n = 7;

    printf("do_something = macro_to_func\n");
    do_something = macro_to_func(pp);
    //printf("fptr minus %p\n", &minus);
    //printf("fptr 2 do_something %p\n", &do_something);
    if(do_something){
        printf("in IF!!\n");
        do_something(&m, &n); --------------------------->???
    }
    return 0;
}

Answer:

do_something = macro_to_func
in Macro …
in IF!!
minus
minus a = -1
minus b = 7
minus -7

Explanation:
“int (*do_something)” is a local function pointer.
In the macro, the macro_to_func will be replaced as “minus”, but do_something !!!

|Code| LLVM initial

One day, if you want to implement a module on the platform x86_64 for arm64, you will need to know the tool chain, “cross compiler”.
The famous modern compiler is LLVM.

Why should we understand the compiler?
– build for multi-platform
– improve the efficiency of your code
– …..

I have a conclusion for the developer whose library is under the user space.
Use suitable optimization provided by LLVM, and make your code readable.
Dazzle coding is meaningless.

你所不知道的 C 語言:編譯器和最佳化原理篇
[共筆] 不深不淺,帶你認識 LLVM
“編譯器 LLVM 淺淺玩” by Pokai Chang

WordPress.com.

Up ↑