AIdrifter
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # [AIdrifter CS 浮生筆錄](https://hackmd.io/s/rypeUnYSb)<br> Thread Synchronization in Linux ## Create Process & Wait Process ### 常見的Create and Wait - Create child process - `fork()` (<unistd.h>) - `system()` - exec – 有 `execve()` 等共六個 functions (<unistd.h>) - Wait child process - `wait()` & `waidpid()` (<sys/types.h> <sys/wait.h>) - Wait **other** processes - 建議 **popen** + **ps** + **grep** - **popen ??** - Third-party library -> libprocps ### fork() Theorem - Unix/Linux 以 `fork()` 產生 process,`exec()` 執行 process - 呼叫 `fork()` 的為 Parent,產生出的為 Child,運行先後順序無法保證,用 `vfork()` 可以保證 Child 一定先運行 (直到exit 或 exec) - Child 將完美複製整個 Parent,但由於**太浪費記憶體**,現有系統不再完整複製,只複製變更的部分 (**copy-on-write**) - Process 結束後會產生結束狀態(程式碼,Exit code,CPU使用時間) 由系統保管,直到 Parent 領回才真正消滅 - 如果 Parent 先結束 - 把 Child 托孤給 **init** - Parent 用 `wait()` 等待 Child - Parent 先轉成 **idle**,直到 Child 結束,Parent 領回 Child 的結束狀態後繼續運行 - Parent 活著且不處理 Child 的結束狀態 - Child 變成 **zombie** 且 zombie 一直存在 (占用 Process List) - Wiki **Hello World** - On success, the PID of the child process is returned in the parent, and 0 is returned in the child. On failure, -1 is returned in the parent, no child process is created, and errno is set appropriately. ```C int main(void) { pid_t pid = fork(); // parenet get child pid, child get 0 if (pid == -1) { perror("fork failed"); exit(EXIT_FAILURE); } else if (pid == 0) { printf("Hello from the child process!\n"); _exit(EXIT_SUCCESS); } else { int status; (void)waitpid(pid, &status, 0); // parent wait child pid } return EXIT_SUCCESS; } ``` ### How to Prevent Zombine - 有時候 Parent 正在忙,沒空處理 Child 的結束狀態 - 利用兩次 `fork()`,第一個 child 扮演原本的 parent,第二個 child 扮演真正的 child - 原本 Parent 結束,兩個 Child 都托孤給 init -> 不會有 **zombie** ```C #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdbool.h> #include <string.h> #include <unistd.h> #define COUNT 3 int main() { pid_t pid = 0; printf("parent process %u \n",getpid()); if ((pid = fork()) == 0) { int i = 0; while(i++ < COUNT) { printf("first child %u pid %u \n",getpid(), pid); sleep(1); } } else { if ((pid = fork()) == 0) { int i = 0; while(i++ < COUNT) { printf("second child %u pid %u \n",getpid(), pid); sleep(1); } } else { int i = 0; while(i++ < COUNT) { printf("parent died %u pid %u \n",getpid(), pid); sleep(1); } } } } ``` - output result ```shell parent process 73802 parent died 73802 pid 73804 first child 73803 pid 0 second child 73804 pid 0 first child 73803 pid 0 parent died 73802 pid 73804 second child 73804 pid 0 parent died 73802 pid 73804 second child 73804 pid 0 first child 73803 pid 0 ``` - gdb ```shell (gdb) info proc process 73130 cmdline = '/home/aidrifter/a.out' cwd = '/home/aidrifter' exe = '/home/aidrifter/a.out' ``` - 利用兩次 `fork()`,第一個 child 扮演原本的第二個child 的 parent,再托孤給 init ```C if (fork() == 0) { pritnf("first child %u \n",getpid()); if (fork() == 0) pritnf("second child %u \n",getpid()); else pritnf("first child died %u \n",getpid()); } else { // Parent 領回第一個 child 的結束狀態, // 再繼續做 Parent 該忙碌的事情 } ``` ## Create Thread & Wait Thread ### Create Pthread and Wait ```C // sucess : return 0 // failed : return EAGAIN,EINVAL,EPERM int pthread_create ( // 建立一個新的 thread pthread_t *tid, // 指標指向 thread ID pthread_attr_t *attr, // thread 屬性設定 void *func(void *), // function name void *arg); // parameter for function // sucess : return 0 // failed : return EDEADLK, EINVAL, ESRCH int pthread_join ( // 等待某個 thread 结束 pthread_t tid, // thread ID void **retval); // pthread_exit() 回傳值 ``` ### Hint - 沒有特別設定,一個 thread 可以被 `pthread_join()` **等待** - 不想被等待的話有兩種方式 - 建立前設定 `attr` - `pthread_attr_init()` 做 init, - `pthread_attr_setdetachstate()` 設定 detach - `pthread_create()` 傳入 attr - `pthread_attr_destroy()` 用完 attr 後要記得 free resource - 建立完後才用 pthread_detach (thread_id); - #include <pthread.h> - gcc 的時候參數 `–lpthread` - Reference - [POSIX Threads Programming](https://computing.llnl.gov/tutorials/pthreads/) ### basic pthread - 會先印出 num=110 幾秒過後再印出 x=100 ```C void main(void) { int x = 100; pthread_t tid = 0; if ((pthread_create(&tid, NULL, aa, (void *)(&x))) != 0) { printf (“Create thread failed \n”); return; } pthread_join (tid,NULL); printf (“x = %d\n”, x); } void *aa(void *arg) { int num = 0; num = *( (int *)arg ); num = num + 10; printf (“num = %d\n”, num); sleep (5); } ``` ## Mutex - 保證一次只有一個進入 ### Mutex relation function in Linux ```C /* create one Mutex */ int pthread_mutex_init ( pthread_mutex_t *mutex, // Init mutex pthread_mutexattr_t *attr); // set NULL commonly /* lock mutex */ int pthread_mutex_lock ( pthread_mutex_t *mutex ); /* release mutex */ int pthread_mutex_unlock ( pthread_mutex_t *mutex ); /* try to lock mutex(no wait) */ int pthread_mutex_trylock ( pthread_mutex_t *mutex ); /* * release mutex back to OS * sucess: return 0 * failed: Error num > 0 * */ int pthread_mutex_destroy( pthread_mutex_t *mutex ); ``` ### Concept : Mutex in Linux(attr) - 用法跟 Windows 差不多,都是在**保證一次只有一個thread能夠進入** - 正常狀況下,pthread_mutex_lock() 不允許重複進入 (跟 Window 不同),重複進入將造成 deadlock - 想要 **Recursive** 时候**repeatly 進入 Mutex** - 利用 **attr** 設定 `PTHREAD_MUTEX_RECURSIVE` - 一樣,mutex_lock 幾次,就要相對 mutex_unlock 幾次 - How to use attr in pthread ? ```C // Initialize attr pthread_mutexattr_t attr; pthread_mutexattr_init (&attr); pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); // set attr // create Mutex 的時候傳入 attr pthread_mutex_init (&g_mutex, &attr); // release attr pthread_mutexattr_destroy(&attr); ``` - pthread mutex sample code - mutex is in **user sapce**, some guy create shm to communicate with each thread(**cross-processes**) ```C #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdbool.h> #include <string.h> #include <sys/types.h> #include <pthread.h> #include <unistd.h> #include <sys/syscall.h> #define gettid() syscall(__NR_gettid) pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; int j = 0; int aa(void) { pthread_mutex_lock (&g_mutex); j += 2; printf("j = %u tid %lu \n",j ,gettid()); pthread_mutex_unlock (&g_mutex); return 0; } int bb(void) { pthread_mutex_lock (&g_mutex); j += 3; printf("j = %u tid %lu\n",j ,gettid()); pthread_mutex_unlock (&g_mutex); return 0; } int main(void) { pthread_t tid1,tid2; pthread_mutex_init(&g_mutex,NULL); /* create aa & bb threads */ pthread_create (&tid1, NULL, aa, NULL); pthread_create (&tid2, NULL, bb, NULL); /* wait aa bb threads */ pthread_join (tid1, NULL); pthread_join (tid2, NULL); printf("j = %u tid %lu\n",j ,gettid()); pthread_mutex_destroy(&g_mutex); return 0; } ``` - gcc compile parameter ```shell gcc thread.c -lpthread ``` ### Ignore pthread_mutex_init() - [PTHREAD_MUTEX_INITIALIZER vs pthread_mutex_init ( &mutex, param)](https://stackoverflow.com/questions/14320041/pthread-mutex-initializer-vs-pthread-mutex-init-mutex-param) ```C pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; // lock pthread_mutex_lock (&g_mutex); { /* Critical Section (memory operation)*/ } // unlock pthread_mutex_unlock (&g_mutex); ``` ## Shared-Exclusive Lock - 可以同時讀取,不能同時寫入, 也不能同時讀取+寫入 (限定同一個 process 的 threads 共同使用) - 用在有些 threads 讀取共用資源,有些 threads 修改 - 幾個 threads 可以同時讀取,不會有問題 - 讀取與寫入不可同時發生,靠鎖定保證沒有這樣的狀況 ![](https://i.imgur.com/QZsaXxg.png) ## File Lock - 類似 Shared-Exclusive Lock 可以跨 process 使用 - 利用 fcntl() 實現,fcntl() 功能強大,這只邊介紹 File Lock - 利用一個檔案本來就可**同時被讀取**,但不能同時**讀取+寫入** - 需要 `<unistd.h>` , `<fcntl.h>` - 通常可以用來 - Multi-process 同時使用一個資源狀況下,做到 Read/Write Lock - 利用 File Lock 性質,在 Multi-process 建立 Critical Section - 可以當成【如果 Process A 運行中,Process B 不運行】,舉例來說可以做成 Singleton (確保只有一個相同的 Daemon 在運行) - CMD Parameter - **F_SETLK**:執行鎖定,鎖定方式由 `stLock` 指定,若無法鎖定將返回EACCES 或 EAGAIN,不等待 - **F_SETLKW**:無法鎖定時會等待,直到等到為止 - **F_GETLK**:取得目前鎖定狀況,結果存放在`stLock`中 - `stLock`.**l_type** 可以是 - F_RDLCK (Read Lock) - F_WRLCK (Write Lock) - F_UNLCK (Unlock) - `stLock` 的 **l_whence**, **l_start**, **l_len** 可指定鎖定 File 的某個範圍 ```C #include <unistd.h> #include <fcntl.h> /* * return value * sucess: return value accronding to cmd * failed: -1 * */ extern int fcntl ( int fd, // use open() to get File descriptor int cmd, // F_SETLK, F_SETLKW, F_GETLK struct flock *stLock); // flock structure ``` ## Event ### Condition Variable 相關 functions ```C // create Condition Variable int pthread_cond_init ( pthread_cond_t *cond, // Create Condition Variable pthread_condattr_t *attr ); // 建立的设定,可以给 NULL 就好 // wait some Condition Variable int pthread_cond_wait ( pthread_cond_t *cond, // Wait Condition Variable pthread_mutex_t *mutex); // Accompany wait 所使用的 mutex // 通知一個wait的 thread int pthread_cond_signal ( pthread_cond_t *cond); // 通知所有wait的 threads int pthread_cond_broadcast ( pthread_cond_t *cond); // release condition // sucess : return 0 // failed : Error num > 0 int pthread_cond_destroy (pthread_cond_t *cond); ``` ### Condition Variable Usage ```C #include <stdio.h> #include <stdlib.h> #include <pthread.h> pthread_cont_t g_cond; pthread_mutex_t g_mutex; void *aa(void *arg) { … /* 做完, signal 通知 (event)*/ pthread_cond_signal (&g_cond, &g_mutex); } main() { pthread_t tid; // init condition pthread_cond_init(&g_cond, NULL); pthread_mutex_init(&g_mutex, NULL); // create thread aa pthread_create(&tid, NULL, aa, NULL); … // wiat until aa terminates pthread_cond_wait(&g_cond, &g_mutex); // aa has been finished, we can continue ... … // release source pthread_cond_destroy(&g_cond); pthread_mutex_destroy(&g_mutex); } ``` ### Signal Function ```C // clear set int sigemptyset (sigset_t *set) // add one signal to set group int sigaddset ( sigset_t *set, // signal set int signum); // 要加入的 signal number // delete one signal form signal set int sigdelset ( sigset_t *set, // signal set int signum); // delete signal number // set waitting singnals int sigprocmask ( int how, // wait way : SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK const sigset_t *set, // 打算等待的哪些 signals (signal set) sigset_t *oldset); // 返回原本的 set,可以是 NULL // wait set 裡面的all signal(s) int sigwait (*set, int* sig); // notice some process -> signum has already happened int kill (pid, signum); ``` ### Use Signal Like Event ```C #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <pthread.h> #include <sys/types.h> #include <unistd.h> void *aa(void *arg) { pid_t ppid=0; // Get parent pid ppid = getppid(); printf(" %s is working \n",__func__); // aa finished, notice via signal kill (ppid, SIGUSR1); } int main() { pthread_t tid; sigset_t set; int sig=0; // Init set (only wait SIGUSR1) sigemptyset (&set); sigaddset (&set, SIGUSR1); sigprocmask (SIG_SETMASK, &set, NULL); // Create thread aa pthread_create (&tid, NULL, aa, NULL); // Wait aa's signal terminate (SIGUSR1) sigwait (&set, &sig); // aa was already finished , continue ... // … printf(" %s is working \n",__func__); return 0; } ``` - [FIXME] prcoess will terminate tmux when `kill()` ```shell (gdb) bt #0 kill () at ../sysdeps/unix/syscall-template.S:84 #1 0x0000555555554952 in aa (arg=0x0) at event.c:17 #2 0x00007ffff7bc06da in start_thread (arg=0x7ffff77f1700) at pthread_create.c:456 #3 0x00007ffff78fad7f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:105 (gdb) c Continuing. User defined signal 1 ``` ## Semaphore ### Semaphore Function ```C // Create one anonymous Semaphore // sucess: return 0 // failed: return -1 int sem_init ( sem_t *sem, // 要使用的 Semaphore int pshared, // 0: 不能跨 proc 使用,非0: 可以跨 proc unsigned int value ); // 一開始的 resource count // Create one named Semaphore // sucess: return 0 // failed: return -1 sem_t sem_open ( const char *name, // 取名字,透過名字共用 int oflag, // O_CREAT,O_CREAT | O_EXCL mode_t mode, // S_IRWXU,S_IRUSR…,S_IRWXG, S_IWGRP…,S_IRWXO,S_IXOTH… unsigned int value ); //一開始的 resource count int sem_post (sem_t *sem); // increase resource. unlimited , never sleep int sem_wait (sem_t *sem); // decrease resource,sleep when resource count = 0 int sem_trywait (sem_t *sem); // decrease resource,Don't slee[ int sem_getvalue (sem_t *sem, int *sval); // 取得目前 count (放 sval) int sem_close (sem_t *sem); // close sem_t from sem_open() int sem_destroy (sem_t *sem); // destory sem_t from sem_init() int sem_unlink (sem_t *sem); // release sem_t's resource from sem_open() ``` ### Semaphore Concept - 使用 `sem_wait()` or `sem_trywait()` 當消費者 - 消費者一次把 Resource count 減一 - Resource count=0 的時候,消費者睡覺 - 用 `sem_post()` 當生產者 - 生產者不會真的睡覺 - 連上限都沒有 - 生產者要睡覺,要自己想辦法 - 單純使用`sem_init()` + `fork()`,Parent 跟 Child 將不會共用同一個semaphore,要加上 shared memory 配合使用。 ==這是個常見的陷阱==

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully