• Linux的一些细节记录


    1. malloc的实现方式

    uclibc中,用户空间的malloc提供了三种实现方式:

    malloc

    malloc-simple

    malloc-standard 

    具体使用何种方式,取决于.config文件定义(projectxxxconfig ormalconfig.uClibc),文件中分别对应几个配置符:

    MALLOC

    MALLOC_SIMPLE

    MALLOC_STANDARD

    malloc-standard :

    This is a version (aka dlmalloc) of malloc/free/realloc written byDoug Lea,通过算法在用户空间实现内存管理

    malloc-simple:

    malloc/free通过简单mmap/munmap实现,代价比较大

    malloc:

    使用sbrk-->brk 系统调用实现,速度比mmap快

    注意:malloc具体实现与其libc版本有关,例如对于glibc,最新版本只有一种实现方式:ptmalloc

    2. execve使用方法

    execve为系统调用函数,功能为启动新的程序:

    函数定义 int execve(const char *filename, char *const argv[ ], char *const envp[ ]);

    返回值 函数执行成功时没有返回值,执行失败时的返回值为-1.

    函数说明 execve()用来执行参数filename字符串所代表的文件路径,第二个参数是利用数组指针来传递给执行文件,并且需要以空指针(NULL)结束,最后一个参数则为传递给执行文件的新环境变量数组。

     1 #include<stdio.h>
     2 #include<stdlib.h>
     3 
     4 int main(int arg, char **args)
     5 {
     6     //char *argv[]={"ls","-al","/home", NULL};
     7     char *argv[]={"free",NULL};
     8     //char *envp[]={0,NULL}; //传递给执行文件新的环境变量数组
     9 
    10     execve("/usr/bin/free",argv,NULL);
    11     //execve("/bin/ls",argv,envp);
    12 }

    argv:必须是数组指针,传入NULL或者字符串会出错(例如execve("/usr/bin/free","free",NULL);)

    envp:可以设置为NULL

    fork()和execve()的区别

    fork是分身,execve是变身。

    exec系列的系统调用是把当前程序替换成要执行的程序,而fork用来产生一个和当前进程一样的进程(虽然通常执行不同的代码流)。exec系列的系统调用已经是变成别的程序了,已经和本程序无关了。通常运行另一个程序,而同时保留原程序运行的方法是,fork+exec。

    fork调clone, clone调do_fork(do_fork是内核函数, 不是系统调用). 当然fork也可以直接调do_fork, 具体怎么做的, 请参看内核代码sys_fork的实现.
    pthread_create是调的clone.

    简单的说clone就是fork的"泛化"版. 通过clone创建一个新进程时可以指定很多参数, 比如是否共享内存空间等等. Linux内核并没有对线程的实现, 一切都是进程, 线程简单地说不过是共享内存空间的进程而已(当然可能还有点别的细节, 比如是否共享信号处理, 文件描述符之类的).内核中一个task_struct对象代表一个进程/task/调度对象.

     对Linux系统来说, 创建一个新线程和创建一个新进程开销是基本一样的(简单的说内核的眼里只有进程, 或者只有任务). 线程切换的开销和进程切换的开销, 主要是切换时是否刷新页表(MMU TLB), 也就是是否切换虚拟内存空间所对应的物理内存页. 别的几乎一致. 嗯, 线程切换是要快一些.

    fork<app>  --> __libc_fork  <libc> -->  pid = ARCH_FORK ()<与处理器架构相关> -->  

    INLINE_SYSCALL (clone, 5,      
        CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, 
        NULL, NULL, NULL, &THREAD_SELF->tid)

    < (linuxuclibclibpthread ptlsysdepsunixsysvlinuxarmfork.c>

    execve<app> -->

    _syscall3(int, execve, const char *, filename,
        char *const *, argv, char *const *, envp)   <libc>

    system<app> --> __libc_system <libc>  ->  vfork+ execl

    execl --> execve

    系统调用服务例程sys_clone, sys_fork, sys_vfork三者最终都是调用do_fork函数完成:

    linux-3.4.xarcharmkernelsys_arm.c

     1 /* Fork a new task - this creates a new program thread.
     2  * This is called indirectly via a small wrapper
     3  */
     4 asmlinkage int sys_fork(struct pt_regs *regs)
     5 {
     6     return do_fork(SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);
     7 
     8 }
     9 
    10 /* Clone a task - this clones the calling program thread.
    11  * This is called indirectly via a small wrapper
    12  */
    13 asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
    14              int __user *parent_tidptr, int tls_val,
    15              int __user *child_tidptr, struct pt_regs *regs)
    16 {
    17     if (!newsp)
    18         newsp = regs->ARM_sp;
    19 
    20     return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr);
    21 }
    22 
    23 asmlinkage int sys_vfork(struct pt_regs *regs)
    24 {
    25     return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);
    26 }
    27 
    28 /* sys_execve() executes a new program.
    29  * This is called indirectly via a small wrapper
    30  */
    31 asmlinkage int sys_execve(const char __user *filenamei,
    32               const char __user *const __user *argv,
    33               const char __user *const __user *envp, struct pt_regs *regs)
    34 {
    35     int error;
    36     char * filename;
    37 
    38     filename = getname(filenamei);
    39     error = PTR_ERR(filename);
    40     if (IS_ERR(filename))
    41         goto out;
    42     error = do_execve(filename, argv, envp, regs);
    43     putname(filename);
    44 out:
    45     return error;
    46 }

    clone 和fork,vfork系统调用在实现时都是调用核心函数do_fork:

    do_fork(unsigned long clone_flag, unsigned long usp, struct pt_regs);

     

    do_fork的第一个参数是clone_flag,该参数可以是:

    CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_SIGHAND,CLONE_PID,CLONE_VFORK等等标志位的组合。任何一位被置1了则表明创建的子进程和父进程共享该位对应的资源。

     

    fork时clone_flag = SIGCHLD;

    vfork时clone_flag = CLONE_VM | CLONE_VFORK | SIGCHLD;

    而在clone中,clone_flag由用户给出。

    在vfork的实现中,cloneflags = CLONE_VFORK | CLONE_VM | SIGCHLD,这表示子进程和父进程共享地址空间,同时

    do_fork会检查CLONE_VFORK,如果该位被置1了,子进程会把父进程的地址空间锁住,直到子进程退出或执行exec时才释放该锁。

     pthread_create

    __pthread_create_2_1   ->    create_thread   ->  

    clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL
           | CLONE_SETTLS | CLONE_PARENT_SETTID
           | CLONE_CHILD_CLEARTID | CLONE_SYSVSEM

    do_clone (pd, attr, clone_flags, start_thread,
             STACK_VARIABLES_ARGS, 1);

    -->

    ARCH_CLONE (fct, STACK_VARIABLES_ARGS, clone_flags,
        pd, &pd->tid, TLS_VALUE, &pd->tid) == -1)

    -->

    DO_CALL (clone)

    3. 线程/进程

    0号进程 swapper:  有一个专门的task_struct结构体类型变量init_task,以它为头系统每个线程的task_struct字段都链接在一起形成一个双向循环链表;它是系统创建的第一个进程,也是唯一一个没有通过fork|kernel_thread产生的进程,完成最开始的初始化后最终会转化为Idle进程。

    1号进程 init:  进程0最终会通过调用kernel_thread创建一个内核线程去执行init函数,init函数继续完成剩余的内核初始化,并在函数的最后调用execve系统调用装入用户空间的可执行程序/sbin/init,这时进程1就拥有了自己的属性资源,成为一个普通进程,至此,内核初始化和启动过程结束。下面就进入了用户空间的初始化,init也将变为守护进程监视系统其他进程,收集孤立的进程:当一个进程启动了一个子进程并且在子进程之前终止了,这个子进程立刻成为init的子进程。由init进程领养的进程终止时init就会调用一个wait函数取得其终止状态。

    2号进程kthreadd:kthreadd进程由idle通过kernel_thread创建,并始终运行在内核空间, 负责所有内核线程的调度和管理

    进程 = 一段程序+堆栈空间+任务控制块+独立存储空间

    线程 = 一段程序+堆栈空间+任务控制块

    对内核空间来说,线程和进程没有任何区别,因为所有内核线程都使用同一块地址空间(3GB--4GB),对应同一个页表(init_mm.pgd);

    对用户空间来说,进程拥有独立的页表(task_struct->mm_struct->pgd),而线程没有独立页表,与进程共享同一个mm_struct;

    4. log_buf

     (1)配置

    内核的日志存在log_buf指向的内存缓冲区中,大小在新的内核是可配置的:

    defconfig
    CONFIG_LOG_BUF_SHIFT=20  (默认是17,即2的17次方)
     
    或者:kernel/init/Kconfig:
    config LOG_BUF_SHIFT
        int "Kernel log buffer size (16 => 64KB, 17 => 128KB)"
        range 12 21
        default 17   ----------->将default值调大
        help
          Select kernel log buffer size as a power of 2.
          Examples:
                   17 => 128 KB
                   16 => 64 KB
                   15 => 32 KB
                   14 => 16 KB
                   13 =>  8 KB
                   12 =>  4 KB

     (2)使用

    API

    (1) printk:内核代码中常见的日志输出方式

    (2)do_syslog/syslog: 内核提供的系统调用,用来操作log_buf

    (3)klogctl:C库中提供的函数,用来使用上面的系统调用。

    syslog系统调用支持下面的命令:

           *      0 -- Close the log.  Currently a NOP.

           *      1 -- Open the log.Currently a NOP.

           *      2 -- Read from the log.

            *     3 -- Read all messages remaining in the ring buffer.

           *      4 -- Read and clear allmessages remaining in the ring buffer

           *      5 -- Clear ring buffer.

           *      6 -- Disable printk toconsole

           *      7 -- Enable printk toconsole

           *      8 -- Set level of messagesprinted to console

           *      9 -- Return number ofunread characters in the log buffer

           *     10 -- Return size of the logbuffer

    Tool

    在shell中可以使用下面的方法直接操作内核log日志

      (1)  dmesg:用来查看log_buf中的数据;

      (2)  cat /proc/kmsg :也可用来读取log_buf中的数据

    (3)data.save.b d:/log.txt  __log_buf++0xffff

    5. 一些调试命令

      (1)  cat  /proc/meminfo ==>查看内存信息

      (2)  top

           cat /proc/<pid>/status   ==>查看进程状态

    (3)sync

             echo 3 > /poc/sys/vm/drop_caches  ==> 释放系统cache占用的内存

             free -m        

      To free pagecache, use

         echo 1 > /proc/sys/vm/drop_caches

       to free dentries and inodes, use

        echo 2 > /proc/sys/vm/drop_caches

      to free pagecache, dentries and inodes, use

         echo 3 >/proc/sys/vm/drop_caches

    (4)echo scan > /sys/kernel/debug/kmemleak  ==> 扫描内存泄漏信息

             cat /sys/kernel/debug/kmemleak

    使用前需要把内核配置打开

    CONFIG_HAVE_DEBUG_KMEMLEAK=y

    CONFIG_DEBUG_KMEMLEAK=y

    CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=400

      (5) vm_stat变量是一个用于记录内存信息的大数组,数组第一项意义为NR_FREE_PAGES,即系统当前空闲页表,该值*4即为当前空闲内存。数组其他值的意义可以参阅<mmzone.h>。

  • 相关阅读:
    1
    Dockerfile概述
    协议
    部署环境
    协议与数据2
    【笔记】刚到公司,初识dll
    C#Winform学习笔记
    VS2019添加VisionPro控件(此方法是大师分享的)
    VisionPro脚本
    Visionpro相机取像操作,.NET4.0
  • 原文地址:https://www.cnblogs.com/DF11G/p/10115811.html
Copyright © 2020-2023  润新知