Try   HackMD

Process State-fork, exit, wait, signal, thread

tags: C LANGUAGE linux fork signal wait mmap thread

Author: WhoAmI
email: kccddb@gmail.com
Date: 20230915
Copyright: CC BY-NC-SA

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

也許以初學者而言您認為不重要, 但是這是設計穩定有效的程式基礎知識

Families of Operating system

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Typical layout of a simple computer's program memory with the text, various data, and stack and heap sections.

  1. Text segment (i.e. instructions)

  2. Initialized Data Segment, e.g., char s[] = “hello world”, static int i = 10;

  3. Uninitialized Data Segment,
    e.g., static int i;

  4. stack x86: LIFO structure

  5. Heap: Heap is the segment where dynamic memory allocation usually takes place.

    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →

Ref.
Memory Layout of C Programs
Linker

ld - The GNU linker. Usually the last step in compiling a program is to run ld.

https://computersciencewiki.org/index.php/Operating_system#Virtual_memory

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

fork system call
Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

fork+interrupt
Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

您可用 pstree 看你 Linux processes
由此圖, 可看出 設計網路 server 程式 單獨使用 fork 可能不是很適合.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

fork() system call

duplicate
1. file descriptor (* Notice the side effect, e.g., lseek)
2. global variables (* Notice copy-on-write), local variables (except pid, why?)
3.
( If you want to know the details, you may try to understand the Linux process.)

​​​​   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. ( #include <errno.h>)

child termination:
The termination signal of the child is always SIGCHLD. Notice you must handle signal SIGCHLD for important applications. See man 7 signal

pthread (執行緒)

Multithreaded Programming (POSIX pthreads Tutorial)
使用 pthread 的 echo server (有bug! why?)
C 語言 pthread 多執行緒平行化程式設計入門教學與範例, by G. T. Wang

Futher Reading (Linux kernel 的, 初學者可跳過):

Semaphore, by ian910297

Linux VFS (virtual file system) and System Call

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Time Dependent Finite State Machine

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

context switching

根據上圖 FSM 的觀念, 必須將 process 的狀態 Mn 存入(save) 記憶體中, 等待換其執行前再(upload)已經紀錄的 Mn, 該processs始能繼續執行 . 至於等多久需視作業系統排程(scheduler)決定

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Ref. State Machine Design in C

alignment 問題

Note: 對於 某些 CPU 可能 引起 重大負擔, 甚至 Linux kernel crash

The handling of interrupts was split into two parts:

  1. Top half;
  2. Bottom half -> defer interrupt handling in Linux kernel

There are three types of deferred interrupts in the Linux kernel:

softirqs; # cat /proc/softirqs
tasklets;

enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ,
NR_SOFTIRQS
};

workqueues;

Tasklet

workqueue can sleep and hold the lock for longtime.

kernel thread
Kernel threads are the basis of the workqueue mechanism. Essentially, a kernel thread is a thread that only runs in kernel mode and has no user address space or other user attributes

Ref. https://0xax.gitbooks.io/linux-insides/content/Interrupts/linux-interrupts-9.html

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Principle of locality
MMU

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

MC68451 MEMORY MANAGEMENTUNIT

第二十一天 Virtual Memory(虛擬記憶體)(一)

linux kernel - how to get physical address (memory management)?

Linux 核心設計: 記憶體管理, by 宅色夫
The page size is typically 4096 bytes in Linux for x86-64 processors.

Linux HugeTLB Pages

# Day 12 Cache and TLB Flushing Under Linux (四)

Professional
Linux® Kernel Architecture, by Wolfgang Mauerer

Page fault is an exception that the memory management unit (MMU) raises when a process accesses a memory page without proper preparations.

Content-addressable memory (CAM)

Logical and Physical Address in Operating System
Translation lookaside buffer
POSIX thread (pthread) libraries
The Linux Kernel documentation


See also
Pthread Scheduling, Fairness, Power

man 2 wait

A child that terminates, but has not been waited for becomes a
"zombie". The kernel maintains a minimal set of information
about the zombie process (PID, termination status, resource usage
information) in order to allow the parent to later perform a wait
to obtain information about the child. As long as a zombie is
not removed from the system via a wait, it will consume a slot in
the kernel process table, and if this table fills, it will not be
possible to create further processes.

Linux Signal

請注意 的default 行為 (man 7 signal)

​​​​   Signal      Standard   Action   Comment
​​​​   SIGABRT      P1990      Core    Abort signal from abort(3)
​​​​   SIGALRM      P1990      Term    Timer signal from alarm(2)
​​​​   SIGBUS       P2001      Core    Bus error (bad memory access)
​​​​   SIGCHLD      P1990      Ign     Child stopped or terminated
​​​​   SIGCLD         -        Ign     A synonym for SIGCHLD
​​​​   SIGCONT      P1990      Cont    Continue if stopped
​​​​   SIGEMT         -        Term    Emulator trap
​​​​   SIGFPE       P1990      Core    Floating-point exception
​​​​   SIGHUP       P1990      Term    Hangup detected on controlling terminal
​​​​                                   or death of controlling process
​​​​   SIGILL       P1990      Core    Illegal Instruction
​​​​   SIGINFO        -                A synonym for SIGPWR
​​​​   SIGINT       P1990      Term    Interrupt from keyboard
​​​​   SIGIO          -        Term    I/O now possible (4.2BSD)
​​​​   SIGIOT         -        Core    IOT trap. A synonym for SIGABRT
​​​​   SIGKILL      P1990      Term    Kill signal
​​​​   SIGLOST        -        Term    File lock lost (unused)
​​​​   SIGPIPE      P1990      Term    Broken pipe: write to pipe with no
​​​​                                   readers; see pipe(7)
​​​​   SIGPOLL      P2001      Term    Pollable event (Sys V).
​​​​                                   Synonym for SIGIO
​​​​   SIGPROF      P2001      Term    Profiling timer expired
​​​​   SIGPWR         -        Term    Power failure (System V)
​​​​   SIGQUIT      P1990      Core    Quit from keyboard
​​​​   SIGSEGV      P1990      Core    Invalid memory reference
​​​​   SIGSTKFLT      -        Term    Stack fault on coprocessor (unused)
​​​​   SIGSTOP      P1990      Stop    Stop process
​​​​   SIGTSTP      P1990      Stop    Stop typed at terminal
​​​​   SIGSYS       P2001      Core    Bad system call (SVr4);
​​​​                                   see also seccomp(2)
​​​​   SIGTERM      P1990      Term    Termination signal
​​​​   SIGTRAP      P2001      Core    Trace/breakpoint trap
​​​​   SIGTTIN      P1990      Stop    Terminal input for background process
​​​​   SIGTTOU      P1990      Stop    Terminal output for background process
​​​​   SIGUNUSED      -        Core    Synonymous with SIGSYS
​​​​   SIGURG       P2001      Ign     Urgent condition on socket (4.2BSD)
​​​​   SIGUSR1      P1990      Term    User-defined signal 1
​​​​   SIGUSR2      P1990      Term    User-defined signal 2
​​​​   SIGVTALRM    P2001      Term    Virtual alarm clock (4.2BSD)
​​​​   SIGXCPU      P2001      Core    CPU time limit exceeded (4.2BSD);
​​​​                                   see setrlimit(2)
​​​​   SIGXFSZ      P2001      Core    File size limit exceeded (4.2BSD);
​​​​                                   see setrlimit(2)

signal 運作原理 (Signal hander is sigterm())

/* *Copyright (C) GPL *Version: 1.0 2018/09/18 *Authors: WhoAmI gcc -Wall sigalarm.c -o sigalarm gcc -Wall -DTEST sigalarm.c -o sigalarm */ #include <stdio.h> #include <unistd.h> //system calls #include <stdlib.h> #include <signal.h> #define SECS 10 #ifdef TEST #define debugx(x) x #else #define debugx(x) #endif static void sigalarm(int signum) { debugx( printf("catch signal [%d]\n",signum)); } int main (int argc,char *argv[]) { unsigned int secs=SECS; signal(SIGALRM ,sigalarm); alarm(secs); printf("Wait %d secs...>",secs); getchar(); exit(EXIT_SUCCESS); }

fork, wait, signal

  1. Please man 2 wait
  2. Check int loop
  3. Check pid
  4. Check exit_status
  5. Check Virtual Address
  6. SIGCHLD
  7. pstree- display a tree of processes
  8. Linux ln Command Tutorial for Beginners (5 Examples)
  9. ldd - print shared object dependencies
  10. strace - trace system calls and signals
/* Usage of variables, fork(), wait(),signal*/ /* Copyright (c) 2018 * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * *Author: WhoAmI * */ //Purpose //Understand the operation of variables, fork, wait and signal // #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> //signal hander static void sig_handler(int sig) { int retval; if ( sig == SIGCHLD ){ //wait(&retval); //Be careful! Why? printf("CATCH SIGNAL PID=%d\n ret=%d\n",getpid(),retval); } } int main() { pid_t pid; int exit_status; int loop=0; signal(SIGCHLD,sig_handler); //register the signal table of the process exit_status=1; switch(pid=fork()) { case -1: perror("fork"); exit(EXIT_FAILURE); case 0: /* Child Process */ exit_status=10; printf("[Child] PID is %d exit_status=[%p]\n", getpid(),&exit_status); for(loop=0; loop <1000;loop++); //local variable: loop // &loop address printf("[Child]parent's PID is %d CHILD loop[%p]=%d\n", getppid(),&loop,loop); printf("[Child] Enter my exit status : "); scanf("%d", &exit_status); exit(exit_status); //change exit code default: printf("[PARENT] Child's PID is [%d] exit_status [%p]\n", pid,&exit_status); for(loop=0;loop>-1000;loop--); printf("[PARENT] loop[%p]=%d\n\n",&loop,loop); printf("[PARENT] I'm now waiting for my child to exit()...\n"); wait(&exit_status); //Be carefull printf("[PARENT] Child's exit status is [%d]\n", WEXITSTATUS(exit_status)); } exit(EXIT_SUCCESS); }

fork() 後 open file, lseek, copy-on-write 的特別效應 請注意聽課

Linux 有些CPU 有 Kernel page-table isolation 的功能

Memory Layout of C Programs

fork copy-on-write

Under Linux, fork() is implemented using copy-on-write pages, so the only penalty that it incurs is the time and memory required to duplicate the parent's page tables, and to create a unique task structure for the child.

基本 virtual address, physical address, MMU 的觀念 ( 一般單晶片或簡單 SoC 可能沒有)


例如

if(發生機率高的條件){

​​​​   程式碼A

}{

​​​​   程式碼B

{

效率就會比較好! 尤其是 kernel, driver, library 的設計

What every systems programmer should know about concurrency, by Matt Kline.

mmap mlock

mmap, munmap - map or unmap files or devices into memory

How to use mmap function in C language? by Bamdeb Ghosh

/* *interprocess communication via shared memory *See also mlock, munlock *Author: WhoAmI */ #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h> #include <sys/mman.h> #define ARRAYSIZE 10 /* void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); If addr is NULL, then the kernel chooses the (page-aligned) address at which to create the mapping; this is the most portable method of creating a new mapping. int munmap(void *addr, size_t length); */ int main(int argc,char *argv[]){ int err; int *pshared; pshared = mmap(NULL,ARRAYSIZE*sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0,0); /* On success, mmap() returns a pointer to the mapped area. On error, the value MAP_FAILED (that is, (void *) -1) is returned. */ if(pshared == MAP_FAILED){ fprintf(stderr, "mmap failed\n"); exit(EXIT_FAILURE); } for(int i=0; i < ARRAYSIZE; i++){ pshared[i] = i ; } printf("Initial values of the array elements :\n"); for (int i = 0; i < ARRAYSIZE; i++ ){ printf(" %d", pshared[i] ); } printf("\n"); pid_t child_pid = fork(); if ( child_pid == 0 ){ //child process updates array printf("--->Child process updates array:\n"); for (int i = 0; i < ARRAYSIZE; i++){ pshared[i] = pshared[i] % 3; } } else{ //parent waitpid ( child_pid, NULL, 0); printf("Parent Process:\n"); printf("Updated values of the array elements :\n"); for (int i = 0; i < ARRAYSIZE; i++ ){ printf(" %d", pshared[i] ); } printf("\n"); } err = munmap(pshared, ARRAYSIZE*sizeof(int)); if(err != 0){ fprintf(stderr,"UnMapping Failed\n"); exit(EXIT_FAILURE); } return 0; }
/* *mmap,munmap *pthread *mlock/munlock *author:WhoAmI */ #include <stdio.h> #include <unistd.h> #include <string.h> #include <pthread.h> #include <stdlib.h> #include <sys/wait.h> #include <sys/mman.h> void PANIC(char* msg); #define PANIC(msg) do { perror(msg); exit(EXIT_FAILURE); }while (0) #define ARRAYSIZE 10 static int threadret=0; void* updateThread(void* arg){ int *pshared=arg; mlock(pshared,ARRAYSIZE); for(int i=0; i < ARRAYSIZE; i++){ pshared[i] = i%3; } threadret=ARRAYSIZE; munlock(pshared,ARRAYSIZE); pthread_exit(&threadret); } int main(void) { pthread_t my_thread; int err; int *pshared; int *retval; pshared = mmap(NULL,ARRAYSIZE*sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0,0); for(int i=0; i < ARRAYSIZE; i++){ pshared[i] = i ; } if ( pthread_create(&my_thread, NULL, updateThread, pshared) != 0 ) { perror("Thread creation"); } pthread_join(my_thread,&retval); for(int i=0; i < ARRAYSIZE; i++){ printf("%d",pshared[i]); } printf("\n"); if(retval!=NULL) printf("retval=%d",*retval ); err = munmap(pshared, ARRAYSIZE*sizeof(int)); if(err != 0){ fprintf(stderr,"Unmapping Failed\n"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); }
/* *mmap,munmap *pthread *mlock/munlock *sleep *author:WhoAmI */ #include <stdio.h> #include <unistd.h> #include <string.h> #include <pthread.h> #include <stdlib.h> #include <sys/wait.h> #include <sys/mman.h> void PANIC(char* msg); #define PANIC(msg) do { perror(msg); exit(EXIT_FAILURE); }while (0) #define ARRAYSIZE 10 static int threadret=0; void* updateThread(void* arg){ int *pshared=arg; mlock(pshared,ARRAYSIZE); for(int i=0; i < ARRAYSIZE; i++){ pshared[i] = i%3; } threadret=ARRAYSIZE; sleep(5); munlock(pshared,ARRAYSIZE); pthread_exit(&threadret); } int main(void) { pthread_t my_thread; int err; int *pshared; int *retval; pshared = mmap(NULL,ARRAYSIZE*sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0,0); for(int i=0; i < ARRAYSIZE; i++){ pshared[i] = i ; } if ( pthread_create(&my_thread, NULL, updateThread, pshared) != 0 ) { perror("Thread creation"); } mlock(pshared,ARRAYSIZE); for(int i=0; i < ARRAYSIZE; i++){ printf(":%d",pshared[i]); } munlock(pshared,ARRAYSIZE); pthread_join(my_thread,&retval); for(int i=0; i < ARRAYSIZE; i++){ printf("%d",pshared[i]); } printf("\n"); if(retval!=NULL) printf("retval=%d",*retval ); err = munmap(pshared, ARRAYSIZE*sizeof(int)); if(err != 0){ fprintf(stderr,"UnMapping Failed\n"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); }

以下可能有兩種結果(故意設計的)~Why?

/* *mmap,munmap *pthread *mlock/munlock *sleep */ #include <stdio.h> #include <unistd.h> #include <string.h> #include <pthread.h> #include <stdlib.h> #include <sys/wait.h> #include <sys/mman.h> void PANIC(char* msg); #define PANIC(msg) do { perror(msg); exit(EXIT_FAILURE); }while (0) #define ARRAYSIZE 10 static int threadret=0; void* updateThread(void* arg){ int *pshared=arg; mlock(pshared,ARRAYSIZE); printf("thread!\n"); for(int i=0; i < ARRAYSIZE; i++){ pshared[i] = i%3; } threadret=ARRAYSIZE; sleep(5); munlock(pshared,ARRAYSIZE); pthread_exit(&threadret); } int main(void) { pthread_t my_thread; int err; int *pshared; int *retval; pshared = mmap(NULL,ARRAYSIZE*sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0,0); for(int i=0; i < ARRAYSIZE; i++){ pshared[i] = i ; } if ( pthread_create(&my_thread, NULL, updateThread, pshared) != 0 ) { perror("Thread creation"); } mlock(pshared,ARRAYSIZE); printf("main!\n"); for(int i=0; i < ARRAYSIZE; i++){ pshared[i]=i%2; } munlock(pshared,ARRAYSIZE); pthread_join(my_thread,&retval); for(int i=0; i < ARRAYSIZE; i++){ printf("%d",pshared[i]); } printf("\n"); if(retval!=NULL) printf("retval=%d",*retval ); err = munmap(pshared, ARRAYSIZE*sizeof(int)); if(err != 0){ fprintf(stderr,"UnMapping Failed\n"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); }


mmap

Linux 最有名的 mmap 應是 Framebuffer, 運用 user space 與 kernel space 溝通

另外 就是 運用 user space 控制與設定 硬體晶片 (大學部 要注意alignment 的問題, 有觀念想法就好)

Home Work: Add a system call
Ref. 增加一個 System Call 到 Linux Kernel (v4.x)