Notes
main
main
  • Introduction
  • linuxKernel
    • tips
    • make_help
    • old linux
      • compile_linux0.11
      • TestEnvironment
      • load_setup
      • get_hard_data
    • list
    • plist
    • fifo
    • idr
    • xarray
    • rbtree
    • maple_tree
    • syscall
    • bitmap
    • page
    • page_flags
    • page_size
    • page mapcount
    • page refcount
    • folio
    • slub
      • proc_slabinfo
      • slub_theory
      • kmalloc_kfree
      • kmem_cache
      • slab_alloc
      • slab_free
      • proc_meminfo_SReclaimable_SReclaimable
    • vmalloc
    • brk
    • mmap
    • mremap
    • mprotect
    • madvise
    • read
    • write
    • shmem
    • huge_page
    • page_fault
    • rmap
    • lru
    • multi-gen-LRU
    • page_reclaim
    • page_cache
    • page_table
    • rcu
    • kvm
    • aarch64_boot
    • tracing_system
    • cache_coherence_and_memory_consistency
    • cpu_speculates
    • mmap_lock
    • per-vma_lock
    • cgroup
    • symbol
    • campact
    • page_ext
    • mempool
    • kernelstack
    • filesystem
    • io_stack
    • workingset
    • ioremap
    • sched_period
  • linuxDebug
    • openocd_openjtag
    • i2c_tools
    • objdump
    • addr2line
    • gdb_useage
    • debug_linux_kernel_via_gdb
    • debug_linux_module_via_gdb
    • early_boot
    • sequentially_execute
    • dynamic_debug
    • research_linuxKernel_by_patch
    • tracefs
    • ebpf
    • bpftrace
    • perf
    • flame_graph
    • crash
    • ASAN_HWASAN_MTE_check_mem_bug
    • page_owner
    • vmtouch
    • fio
    • benchmark
  • linuxSystem
    • common
      • system_version
      • procfs
      • proc_sys_vm
      • cmd_ps
      • makefile
      • file_descriptor
      • psi
      • ulimit
      • top
      • delay_accounting
    • ubuntu
      • custom_kernel
      • get_cmd_src
      • record_ssh_info
      • log
      • run_custom_script
      • repo
      • cockpit
      • nfs
      • tftp
      • misc
    • fedora
      • system_upgrade
      • custom_kernel
      • lvextend
      • yt-dlp
      • jellyfin
  • linuxDriver
    • i2c_peripherals_driver
    • spi_peripherals_driver
    • gpio_subsystem
    • IRQ_driver
    • blockIO_unblockIO_async
    • linux_own_driver
    • misc_device
    • input_device
    • timer
    • atomic_spinlock_semaphore_mutex
    • lcd
    • touch_screen
    • debugfs
    • v4l2
    • mmap
  • hardware
    • paging_mmu_pt
    • iommu
  • process_thread_scheduler
    • scheduler01
    • scheduler02
    • scheduler03
    • scheduler04
    • scheduler05
    • scheduler06
  • memory_management
    • mm1
    • mm2
    • mm3
    • mm4
    • mm5
  • input_output_filesystem
    • io_fs_01
    • io_fs_02
    • io_fs_03
    • io_fs_04
  • lock_and_lockup_detector
    • general_lock
    • hung_task
    • softLockup_hardLockup
    • crash_experiment
  • MIT_6.S081
    • 6.S081_Operating_System_Engineering
    • Schedule.md
    • Class
      • Overview
      • Administrivia
    • Labs
      • Tools
      • Guidance
      • startup
      • syscall
      • page_table
      • Calling_Convention
      • traps
    • xv6
      • xv6
    • References.md
  • qemu
    • qemu_buildroot
    • qemu_busybox.md
    • Serial.md
    • demo_mini2440
      • 0_compilation_error_summary
      • 1_compilation_steps
      • 2_operation_mode
      • 3_transplant_tools_libraries
      • 4_tools_use
      • reference_website
  • tools
    • getKernelSourceCodeList
    • nat
    • shell
    • translating
    • YouCompleteMe
    • cscope
    • global
    • vscode
    • vim
    • binary
    • markdown
    • draw
    • git
    • tig
    • tmux
    • mail_client
    • download_patchset_from_LKML
    • minicom
    • clash
  • other
    • interview
    • interview_c_base
    • know_dontknow
    • Stop-Ask-Questions-The-Stupid-Ways
    • How-To-Ask-Questions-The-Smart-Way
    • docker
    • buildroot
    • rv32_to_rv64
Powered by GitBook
On this page
  • fork()、vfork()、pthread_create()
  • 为什么 线程称为轻量级进程?
  • 进程1和进程0
  • 孤儿进程
  • 进程睡眠

Was this helpful?

  1. process_thread_scheduler

scheduler02

fork()、vfork()、pthread_create()

  • 测试环境:

ubuntu 10.04 ( linux 2.6.32 )

  • 测试:

$ cat test.c
#include <sched.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int data = 10;

int child_process()
{
    printf("Child process %d, data %d\n",getpid(),data);
    data = 20;
    printf("Child process %d, data %d\n",getpid(),data);
    _exit(0);
}

int main(int argc, char* argv[])
{
    int pid;
    // pid = fork();
    // pid = vfork();

    if(pid==0) {
        child_process();
    }
    else{
        sleep(1);
        printf("Parent process %d, data %d\n",getpid(), data);
        exit(0);
    }
}

$ gcc test.c -o test

# 当pid = fork();时
$ ./test
Child process 2646, data 10
Child process 2646, data 20
Parent process 2645, data 10
$ strace ./test
...
clone(child_stack=x, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=xxx)
...

# 当pid = vfork();时
$ ./test
Child process 2702, data 10
Child process 2702, data 20
Parent process 2701, data 20
$ strace ./test
xxxxx

$ cat thread.c 
#include <stdio.h>
#include <pthread.h>
#include <stdio.h>
#include <linux/unistd.h>
#include <sys/syscall.h>

int data = 10;

static pid_t gettid( void )
{
    return syscall(__NR_gettid);
}

static void *thread_fun(void *param)
{
    data = 20;

    printf("thread pid:%d, tid:%d pthread_self:%lu\n", getpid(), gettid(),pthread_self());
    printf("data %d\n", data);
    return NULL;
}

int main(void)
{
    pthread_t tid;
    int ret;

    printf("thread pid:%d, tid:%d pthread_self:%lu\n", getpid(), gettid(),pthread_self());
    printf("data %d\n", data);

    ret = pthread_create(&tid, NULL, thread_fun, NULL);
    if (ret == -1) {
        perror("cannot create new thread");
        return -1;
    }

    if (pthread_join(tid, NULL) != 0) {
        perror("call pthread_join function fail");
        return -1;
    }

    printf("thread pid:%d, tid:%d pthread_self:%lu\n", getpid(), gettid(),pthread_self());
    printf("data %d\n", data);

    return 0;
}
$ gcc thread.c -pthread -o thread
$ ./thread
thread pid:1878, tid:1878 pthread_self:140648953210624
data 10
thread pid:1878, tid:1879 pthread_self:140648945157904
data 20
thread pid:1878, tid:1878 pthread_self:140648953210624
data 20
$ strace ./thread
...
clone(child_stack=xxx,flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=xxx,tls=xxx, child_tidptr=xxx) = xxx
...
  • 总结:

用户空间 -------------------------->  内核空间
fork()           -> clone(flag) -> do_fork(flag)
vfork()          -> clone(flag) -> do_fork(flag)
pthread_create() -> clone(flag) -> do_fork(flag)

如上图,无论创建进程还是线程,都是调用系统调用clone (),再调用内核函数do_fork()创建struct task_strust,唯一不同是它们传递flag标志不同,以区分哪些资源是否共享等等。

linux使用 fork() 创建进程时,父进程与子进程共享同一个资源,即struct task_strust 指向同一个资源,并此时所有资源都以只读方式共享。只有 父进程或子进程 其中一个对资源进行写操作时,linux才会为 父进程或子进程 创建复制一份一模一样的资源,即 此时 父子进程 才各自拥有一份完全不同的struct task_strust,对资源都拥有读写权限。这就是进程写时拷贝(Copy-on-Write)技术。

linux使用 vfork() 创建进程时,跟fork() 创建进程基本是一样的,唯一不同是task_strust->mm成员是共享,如 进程中全局变量是共享。

linux使用pthread_create()创建线程时,进程内所有线程共享同一个资源,即 struct task_strust 指向同一个资源,所有资源都以读写方式共享。

为什么 线程称为轻量级进程?

从上述可知,在linux kernel中,进程与线程都是用struct task_strust 表示,唯一不同是 struct task_strust 指向资源是否共享。

进程1和进程0

  • 测试环境:

ubuntu 10.04 ( linux 2.6.32 )

ubuntu 20.04 ( linux 5.4.0)

  • 测试:

$ uname -r
2.6.32-21-generic
$ ls -l /sbin/init
-rwxr-xr-x 1 root root 125704 2010-04-02 08:13 /sbin/init
$ cat /proc/1/status
Name:    init
Tgid:    1
Pid:    1
PPid:    0
...

vernon@vernon-VB:/proc$ pstree
init─┬─NetworkManager─┬─dhclient
     │                └─{NetworkManager}
     ├─acpid
     ├─atd
     ├...

#####################################################

$ uname -r
5.4.0-47-generic
$ ls -l /sbin/init 
lrwxrwxrwx 1 root root 20 7月   7 05:38 /sbin/init -> /lib/systemd/systemd
$ cat /proc/1/status 
Name:    systemd
Tgid:    1
Pid:    1
PPid:    0
...
$ pstree
systemd─┬─ModemManager───2*[{ModemManager}]
        ├─NetworkManager───2*[{NetworkManager}]
        ├─accounts-daemon───2*[{accounts-daemon}]
        ├─acpid
        ├...
  • 总结:

init进程是整个系统的第一个进程,也就是1号进程。

1号进程有父进程 0号进程,即 idle进程

孤儿进程

  • 测试环境:

ubuntu 10.04 ( linux 2.6.32 )

ubuntu 20.04 ( linux 5.4.0)

  • 测试:

$ cat life_period.c 
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    pid_t pid,wait_pid;
    int status;

    pid = fork();

    if (pid==-1)    {
        perror("Cannot create new process");
        exit(1);
    } else if (pid==0) {
        printf("child process id: %ld\n", (long) getpid());
        pause();
        _exit(0);
    } else {
        printf("parent process id: %ld\n", (long) getpid());
        wait_pid=waitpid(pid, &status, WUNTRACED | WCONTINUED);
        if (wait_pid == -1) {
            perror("cannot using waitpid function");
            exit(1);
        }

        if(WIFSIGNALED(status))
            printf("child process is killed by signal %d\n", WTERMSIG(status));

        exit(0);
    }
}

#####################################################

$ uname -r
2.6.32-21-generic
$ gcc life_period.c
$ ./a.out
parent process id: 1795
child process id: 1796
$ pstree
init─┬─NetworkManager─┬─dhclient
     │                └─{NetworkManager}
     ├─acpid
     |.......
     ├─gnome-terminal─┬─bash───a.out───a.out
     │                ├─2*[bash]
     │                ├─gnome-pty-helpe
     │                └─{gnome-terminal}
     ├─gvfs-afc-volume───{gvfs-afc-volum}

$ kill 1795
$ pstree
init─┬─NetworkManager─┬─dhclient
     │                └─{NetworkManager}
     ├─a.out
     ├─acpid
     |.......
     ├─gnome-terminal─┬─3*[bash]
     │                ├─gnome-pty-helpe
     │                └─{gnome-terminal}
     ├─gvfs-afc-volume───{gvfs-afc-volum}

#####################################################

$ uname -r
5.4.0-47-generic
$ ./a.out 
parent process id: 6323
child process id: 6324
$ pstree
systemd─┬─ModemManager───2*[{ModemManager}]
        ├....
        ├─systemd─┬─(sd-pam)
        │         ├......
        │         ├─gnome-terminal-─┬─bash───a.out───a.out
        │         │                 ├─bash───pstree
        │         │                 └─5*[{gnome-terminal-}]
$ kill 6323
$ pstree
systemd─┬─ModemManager───2*[{ModemManager}]
        ├....
        ├─systemd─┬─(sd-pam)
        │         ├......
        │         ├─a.out
        │         ├─gnome-terminal-─┬─2*[bash]
        │         │                 ├─bash───pstree
        │         │                 └─5*[{gnome-terminal-}]
  • 总结:

当子进程先死亡时,父进程负责得到子进程死亡原因以及清除struct task_strust

当父进程先死亡时,子进程变成孤儿进程,此时linux系统需要给子进程找一个养父进程。在linux 3.4之前,养父进程一般都是init进程;在linux 3.4之后,养父进程一般都是SUBREAPER标志的进程,如上图 每一个用户的systemd进程就是用SUBREAPER标志的进程。

进程睡眠

  • 测试环境:

宋宝华老师的驱动开发书 -- globalfifo.c

  • 测试:

static ssize_t globalfifo_write(struct file *filp, const char __user *buf,
                    size_t count, loff_t *ppos)
{
    DECLARE_WAITQUEUE(wait, current);

    add_wait_queue(&dev->w_wait, &wait);
    ...
    __set_current_state(TASK_INTERRUPTIBLE);
    schedule();
    ...
}
  • 总结:

进程进入睡眠状态,可分为两种:深度睡眠与浅度睡眠。

深度睡眠,即UNINTERRUPTIBLE_SLEEP,只有当所需资源到达时,进程才被唤醒。

浅度睡眠,即INTERRUPTIBLE_SLEEP,当所需资源或信号到达时,进程才被唤醒。

进程进入睡眠状态,即调用add_wait_queue()将进程放入等待队列中,设置为__set_current_state(UNINTERRUPTIBLE_SLEEP)或__set_current_state(INTERRUPTIBLE_SLEEP)后,调用schedule()。此时进程放弃CPU,CPU调度另一个进程继续执行。当所需资源或信号到达时,通过调用wake_up()唤醒进入睡眠状态的进程,从schedule()下一行源码继续执行。

Previousscheduler01Nextscheduler03

Last updated 4 years ago

Was this helpful?