Linux中并发程序设计(进程的创建和回收、exec函数使用、守护进程创建和使用、GDB的父、子进程代码的调试、线程的创建和参数传递)

news2025/1/17 14:07:04

进程的创建和回收

进程概念

  • 概念
    程序
    存放在磁盘上的指令和数据的有序集合(文件)
    静态的
    进程
    执行一个程序所分配的资源的总称
    动态的
  • 进程和程序比较
    在这里插入图片描述
    注:进程是存在RAM中,程序是存放在ROM(flash)中的
  • 进程内容
    BSS段:存放程序中未初始化的全局变量
    数据段:已初始化的全局变量,static声明的变量
    代码段:程序执行代码
    堆(heap):malloc等函数分配内存
    栈(stack):局部变量,函数参数,函数的返回值
    进程控制块(pcb):PID, 进程优先级,文件描述符表
  • 进程控制块
    进程标识PID、进程用户、进程状态、优先级、文件描述符表
  • 进程类型
    交互进程:在shell下启动,在前台后台运行
    批处理进程:和在终端无关,被提交到一个作业队列中以便顺序执行
    守护进程:和终端无关,一直在后台运行
  • 进程状态
    运行态、等待态、停止态、死亡态:
    在这里插入图片描述

进程常用命令

  • 查看进程信息
    ps 查看系统进程快照
    top 查看进程动态信息
    /proc 查看进程详细信息
  • 执行命令如下:
    ps:当前shell显示
    在这里插入图片描述
    ps -e :所有进程显示
    在这里插入图片描述
    ps -el:是显示详细信息
  • 具体命令参数信息如下:
    ps 命令详细参数:
    -e:显示所有进程
    -l:长格式显示更加详细的信息
    -f 全部列出,通常和其他选项联用
    表头 含义
    F 进程标志,说明进程的权限,常见的标志有两个:
    1:进程可以被复制,但是不能被执行;
    4:进程使用超级用户权限;
    S 进程状态。进程状态。常见的状态有以下几种:
    1. -D:不可被唤醒的睡眠状态,通常用于 I/O 情况。
    2. -R:该进程正在运行。
    3. -S:该进程处于睡眠状态,可被唤醒。
    4. -T:停止状态,可能是在后台暂停或进程处于除错状态。
    5. -W:内存交互状态(从 2.6 内核开始无效)。
    6. -X:死掉的进程(应该不会出现)。
    7. -Z:僵尸进程。进程已经中止,但是部分程序还在内存当中。
    8. -<:高优先级(以下状态在 BSD 格式中出现)。
    9. -N:低优先级。
    10. -L:被锁入内存。
    11. -s:包含子进程。
    12. -l:多线程(小写 L)。
    13. -+:位于后台。
      UID 运行此进程的用户的 ID;
      PID 进程的 ID;
      PPID 父进程的 ID;
      C 该进程的 CPU 使用率,单位是百分比;
      PRI 进程的优先级,数值越小,该进程的优先级越高,越早被 CPU 执行;
      NI 进程的优先级,数值越小,该进程越早被执行;
      ADDR 该进程在内存的哪个位置;
      SZ 该进程占用多大内存;
      WCHAN 该进程是否运行。"-"代表正在运行;
      TTY 该进程由哪个终端产生;
      TIME 该进程占用 CPU 的运算时间,注意不是系统时间;
      CMD 产生此进程的命令名;
  • 实时查看进程命令如下:
    top 查看进程动态信息
    shift +> 后翻页
    shift +< 前翻页
    top -p PID 查看某个进程
  • 改变进程优先级
    nice 按用户指定的优先级运行进程
    nice [-n NI值] 命令
    NI 范围是 -20~19。数值越大优先级越低
    普通用户调整 NI 值的范围是 0~19,而且只能调整自己的进程。
    普通用户只能调高 NI 值,而不能降低。如原本 NI 值为 0,则只能调整为大于 0。
    只有 root 用户才能设定进程 NI 值为负值,而且可以调整任何用户的进程。
    renice 改变正在运行进程的优先级
    renice [优先级] PID
  • 设置优先级案例如下:
    在这里插入图片描述
  • 改变优先级案例如下:
    在这里插入图片描述
  • 进程相关命令
    jobs 查看后台进程
    bg 将挂起的进程在后台运行
    fg 把后台运行的进程放到前台运行
    ctrl + z 把运行的前台进程转为后台并停止
  • 案例代码如下:
    编写一个.c文件,然后调用一个简单的sleep函数后,执行test后在另一个端口查看进程如下:
    在这里插入图片描述
    注:上述是在运行函数后,按ctrl + Z使其进程进入停止状态T

创建子进程

  • 子进程概念
    子进程为由另一个进程(对应称之为父进程)所创建的进程,实际上你在linux中写程序都是别人创建的,比如运行.test文件时,就是shell的子进程
  • 子进程创建-fork
    #include <unistd.h>
    pid_t fork(void);
    创建新的进程,失败时返回-1
    成功时父进程返回子进程的进程号,子进程返回0
    通过fork的返回值区分父进程和子进程
  • 创建子进程案例如下:
    在这里插入图片描述
    注:创建子进程时虽然会复制父进程的代码,但是不会从头开始执行,而是从创建fork函数下方的时候执行,所以子进程没有赋予相应的进程号,但是系统默认给了0,下面图片显示得很清楚
    在这里插入图片描述
    要点:1 子进程只执行fork之后的代码
    父子进程执行顺序是操作系统决定的。
  • 父子进程
    子进程继承了父进程的内容
    父子进程有独立的地址空间,互不影响
    若父进程先结束
    子进程成为孤儿进程,被init进程收养
    子进程变成后台进程
    若子进程先结束
    父进程如果没有及时回收,子进程变成僵尸进程
  • 父子进程案例如下:
    在这里插入图片描述
  • 运行如下:
    在这里插入图片描述
    注:可以得出父子进程没有什么特定的关系,是系统随机调用的
  • 通过父进程号看出子进程和父进程
    在这里插入图片描述
  • 在用kill -9 杀死父进程,然后可以发现子进程的父进程PPID变成1,也就是init进程中,然后另一个终端ctrl + c结束不掉,说明以及变为后台进程了
    在这里插入图片描述
    注:有些新版的ubuntu系统子进程可能被其他进程领养,例如:systemd作为最新的初始化系统(init)来提高系统的启动速度。这和进程1的init是一个道理不要疑惑。

子进程进阶例题

  • 一个父进程拥有五个子进程,代码如下: 在这里插入图片描述
  • 运行结果如下:
    在这里插入图片描述
    注:可以看出出现了孙进程的情形,因为上述代码中for循环语句,使子进程执行完fork函数下面的语句后,由于for内部是一个循环语句,因此子进程也执行了一次fork函数所以出现孙进程的情形
  • 解决办法如下:
    在这里插入图片描述
    注:在子进程语句中末尾加入break即可

进程的退出

  • 进程结束-exit/_exit
    #include <stdlib.h>
    #include <unistd.h>
    void exit(int status);
    void _exit(int status);//不刷新流缓冲区
    结束当前的进程并将status返回
    exit结束进程时会刷新(流)缓冲区
    return 和exit的区别
    main函数结束时会隐式地调用exit函数,普通函数return是返回上一级。
  • 案例如下:
    在这里插入图片描述
  • 执行如下:
    在这里插入图片描述
    注:这里要注意exit(0)相当于return 0,所以printf(“after exit”)不会被调用,因此终端上不显示,而且不要以为return在任何情况下都会隐式调用exit,只有main函数下才会调用exit,要牢记!

进程的回收

  • 进程回收-wait
    #include <unistd.h>
    pid_t wait(int *status);
    成功时返回回收的子进程的进程号;失败时返回EOF
    若子进程没有结束,父进程一直阻塞
    若有多个子进程,哪个先结束就先回收
    status 指定保存子进程返回值和结束方式的地址
    status为NULL表示直接释放子进程PCB,不接收返回值
  • 例子代码如下:
    在这里插入图片描述
    注:因为status里面包含地址和进程返回值,不能直接输出,需要通过宏来判断
  • 执行后使用ps查看进程的状态,看是否由僵尸进程,也就是判断exit是否收回子进程,如下:
    在这里插入图片描述
  • 查看注释wait那两行后的ps状态如下:
    在这里插入图片描述
    注:可以看出多出一个wait僵尸进程(即僵尸进程)
  • 进程回收-waitpid
    #include <unistd.h>
    pid_t waitpid(pid_t pid, int *status, int option);
    参数:
    pid:
    pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
    pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
    pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
    pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。
    options:
    options提供了一些额外的选项来控制waitpid,目前在Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用"|"运算符把它们连接起来使用
    WNOHANG :若由pid指定的子进程未发生状态改变(没有结束),则waitpid()不阻塞,立即返回0
    WUNTRACED: 返回终止子进程信息和因信号停止的子进程信息
    wait(wait_stat) 等价于waitpid(-1,wait_stat,0)
  • 案例如下:
    在这里插入图片描述
    注:ps查看进程后也是回收的,也可以将-1改为pid,因为上面代码只有一个子进程
  • 有一个案例要注意下如下:
    在这里插入图片描述
  • 执行如下:
    在这里插入图片描述
    注:可以得出在休息1s后下面的waitpid先执行,然后WNOHANG的话上述也有解释,若pid进程未发生状态改变返回0,状态值也返回异常,那么如何解决这个问题呢,如下
  • 修改后代码代码如下:
    在这里插入图片描述
  • 修改如下:
    在这里插入图片描述
    注:可以发现修改完成

exec函数族

exec函数组的执行过程

进程调用exec函数族执行某个程序(exec test可以执行第三方程序)
进程当前内容被指定的程序替换(当你执行exec 后面的程序,那么exec当前程序后面的部分都会被替换)
实现让父子进程执行不同的程序(shell就是执行下面的步骤,可以不被替换)
* 父进程创建子进程
* 子进程调用exec函数族
* 父进程不受影响
  • 进程-execl/execlp
    #include <unistd.h>
    int execl(const char *path, const char *arg, …);
    int execlp(const char *file, const char *arg, …);
    • 成功时执行指定的程序;失败时返回EOF
    • path 执行的程序名称,包含路径
    • arg… 传递给执行的程序的参数列表
    • file 执行的程序的名称,在PATH中查找
      注意:两个函数区别execlp不需要写文件名全路径,在PATH查找
      最后一个参数必须用空指针(NULL)作结束
      进程当前内容被指定的程序替换,但进程号不变
      第0个参数必须要写,虽然它没有使用
  • 案例演示:
    在这里插入图片描述
    注:execl的”ls“为第0个参数,不参与实际的操作,但必须要有
  • 执行如下:
    在这里插入图片描述
  • 也可以改为execlp函数调用如下:
    在这里插入图片描述
  • 执行结果如下:
    在这里插入图片描述
  • 进程-execv
    在这里插入图片描述
  • 函数代码如下:
    在这里插入图片描述
  • 进程-system代码使用如下:
    在这里插入图片描述

守护进程

守护进程概念

概念:
守护进程又叫精灵进程(Daemon Process),它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。
特点:
* 始终在后台运行,独立于任何终端,周期性的执行某种任务或等待处理特定事件。(前台就是类似./test,而后台进程则是./test &)
* 它是个特殊的孤儿进程,这种进程脱离终端,为什么要脱离终端呢?之所以脱离于终端是为了避免进程被任何终端所产生的信息所打断,其在执行过程中的信息也不在任何终端上显示。由于在 Linux 中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。

守护进程-相关概念

进程组:进程集合,每次进程组有一个组长,其进程ID就是该进程组的ID
会话:进程组集合,每个会话有一个组长,其进程ID就是该会话组ID
控制终端:每个会话可以有一个单独的控制终端,与控制终端连接的Leader就是控制进程
  • 举例:
    http 服务的守护进程叫 httpd,mysql 服务的守护进程叫 mysqld。
    更简便地创建守护进程: nohup 命令
    nohup xxxx &
    setsid函数:
    pid_t setsid(void);
    成功:返回调用进程的会话ID;失败:-1,设置errno。
    调用了setsid函数的进程,既是新的会长,也是新的组长
    getsid函数
    pid_t getsid(pid_t pid)
    成功:返回调用进程的会话ID;失败:-1,设置errno
    1.pid为0表示察看当前进程session ID
    2.ps ajx命令查看系统中的进程。参数a表示不仅列当前用户的进程,也列出所有其他用户的进程,参数x表示不仅列有控制终端的进程,也列出所有无控制终端的进程,参数j表示列出与作业控制相关的信息。
    3.组长进程不能成为新会话首进程,新会话首进程必定会成为组长进程。
    getpid:pid_t getpid(void); 获取进程id
    getpgid:pid_t getpgid(pid_t pid); 获取进程组id

守护进程创建

首先,创建子进程,父进程退出,子进程变成孤儿进程,被init进程收养,前面都有提到,然后子进程在后台运行
其次,子进程创建新会话,通过调用setsid函数,来成为新会话组长,子进程脱离原先的终端
然后,改变当前工作目录,调用chdir函数,但不是必须的,根据你一开始的目录是否稳定的来决定,守护进程一直在后台运行,所以工作目录不能被删除
之后,重设文件权限掩码,使用umask(0)将文件权限掩码设置为0,这一步也不是必须的
最后,关闭打开的文件描述符,使用close函数关闭所有从父进程继承的打开文件,而且守护进程已脱离终端,所以stdin/stdout/stderr无法再使用
  • 上述第一步父进程退出相关代码的实现如下:
    在这里插入图片描述
    注:此时执行后无法ctrl + c结束它,已经变成init后台进程下,因此只能kill杀死
  • 使用nohup函数代码如下:
    在这里插入图片描述
  • 执行如下代码:
    在这里插入图片描述
    注:执行该函数可以将其转换成后台程序运行
  • 创建子进程会话代码
    在这里插入图片描述
  • 执行下:
    在这里插入图片描述
    注:可以看出这个子进程自己当家做主了,三个都是它的pid了
  • 下面的几步可选的,做不做都行,下面将演示下
    在这里插入图片描述
    注:关闭文件描述符,后面的printf就不显示了

GDB调试多进程程序

  • 进入GDB模式:
    在这里插入图片描述
  • 调试命令如下:
    在这里插入图片描述
    输入start:运行到第九行,然后输入n进行下一步,进入到fork函数中
  • 其中关键的部分就是如何进入到子进程中的函数命令如下:
    在这里插入图片描述
  • 如何同时跟踪父进程和子进程如下:
    在这里插入图片描述
    注:set detach-on-fork on 这是默认打开的,即只能跟踪一个进程,所以后面要设置为off,info inferiors是用来查看几个进程的,然后切换进程就执行inferiors Num值

线程的创建和参数传递

线程的基本特点

  • 通常线程指的是共享相同地址空间的多个任务
  • 使用多线程的好处就是大大提高了任务切换的效率,避免了额外的TLB&cache的刷新

线程共享资源

  • 一个进程中的多个线程共享以下资源:
    可执行的指令
    静态数据(定义的全局变量都是互相可以访问的)
    进程中打开的文件描述符
    当前工作目录
    用户ID
    用户组ID

线程的私有资源

  • 每个线程私有的资源包括:
    线程ID
    PC程序计数器和相关寄存器
    堆栈
    错误号(errno)
    优先级
    执行状态和属性

Linux线程库

  • pthread线程库中提供了如下基本操作
    创建线程
    回收线程
    结束线程
  • 同步和互斥机制
    信号量
    互斥锁
  • 线程创建
    #include <pthread.h>
    int pthread_create(pthread_t *thread, const
    pthread_attr_t *attr, void *(*routine)(void *), void *arg);
    成功返回0,失败时返回错误码
    thread 线程对象
    attr 线程属性,NULL代表默认属性
    routine 线程执行的函数
    arg 传递给routine的参数 ,参数是void * ,注意传递参数格式,
  • 具体操作代码如下:
    在这里插入图片描述
    运行如下:
    在这里插入图片描述
    注:为什么加sleep(1)给上面程序一个喘息的机会,创建线程也需要时间
  • 上述编译错误分析:
    1.createP_t.c:14:36: warning: passing argument 3 of ‘pthread_create’ from incompatible pointer type [-Wincompatible-pointer-types]
    ret = pthread_create(&tid,NULL,testThread,NULL);
    In file included from createP_t.c:1:0:
    /usr/include/pthread.h:233:12: note: expected ‘void * (*)(void )’ but argument is of type ‘int * ()(char )’
    意义:表示pthread_create参数3的定义和实际代码不符合,期望的是void * (
    )(void ) ,实际的代码是int * ()(char )
    解决方法:改为pthread_create(&tid,NULL,(void
    )testThread,NULL);
    2.createP_t.c:(.text+0x4b):对‘pthread_create’未定义的引用
    collect2: error: ld returned 1 exit status --------这个链接错误,
    表示pthread_create这个函数没有实现
    解决方法:编译时候加 -lpthread
    注意事项:1. 主进程的退出,它创建的线程也会退出。
    线程创建需要时间,如果主进程马上退出,那线程不能得到执行

线程ID的获取

  • 线程结束-phread_exit
    void pthread_exit(void *retval);
    结束当前线程
    retval可被其他线程通过pthread_join获取
    线程私有资源被释放
  • 代码如下:
    在这里插入图片描述
  • 线程查看tid函数
    pthread_t pthread_self(void)
  • 如何使用如下,直接调用函数即可,注意pthread_t为lu类型,无符号长整型:
    在这里插入图片描述
  • 执行如下:
    在这里插入图片描述
  • 线程的参数传递函数调用如下:
    在这里插入图片描述
  • 执行如下:
    在这里插入图片描述
    编译错误:
    createP_t.c:8:34: warning: dereferencing ‘void *’ pointer
    printf(“input arg=%d\n”,(int)*arg);
    createP_t.c:8:5: error: invalid use of void expression
    printf(“input arg=%d\n”,(int)arg);
    错误原因是void 类型指针不能直接用取值(arg),因为编译不知道数据类型。
    解决方法:转换为指定的指针类型后再用
    取值 比如:
    (int *)arg
    • 通过地址传递参数,注意类型的转换
    • 值传递,这时候编译器会告警,需要程序员自己保证数据长度正确。这个方式见如下:
      在这里插入图片描述
      注:虽然运行后会报警,但不影响正常执行
  • 创建多个线程
    在这里插入图片描述
  • 执行如下:
    在这里插入图片描述
    注:这时候出现了一个问题,还是和之前一样,就是在for循环后,线程还没执行完,就会根据i的地址去找值,但此时mian已经访问到第四个值了,因此输出结果可能异常,所以在for循环里的创建线程函数后面添加一个sleep(1)函数,这时候效率可能不高,可以采用值传递
  • 线程的回收:
    使用pthread_join 函数:
    #include <pthread.h>
    int pthread_join(pthread_t thread, void **retval);
    注意:pthread_join 是阻塞函数,如果回收的线程没有结束,则一直等待
    编译错误:
    pjoin.c:13:5: error: unknown type name ‘pthead_t’
    pthead_t tid;
    错误类型:未知的类型pthead_t
    错误可能:1拼写错误,2对应的头文件没有包含
    pjoin.c:18:12: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘void ’ [-Wformat=]
    printf(“thread ret=%s\n”,retv);
    错误类型:参数不匹配,期望的是char * ,但参数retv是void *
    解决:在参数前面加强制类型转换(char
    )retv
  • 使用线程的分离:
    两种方式:
    1 使用pthread_detach
    2 创建线程时候设置为分离属性
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
  • 线程的取消:
    意义:随时杀掉一个线程
    int pthread_cancel(pthread_t thread);
    注意:线程的取消要有取消点才可以,不是说取消就取消,线程的取消点主要是阻塞的系统调用
    运行段错误调试:
    可以使用gdb调试
    使用gdb 运行代码,gdb ./youapp
    (gdb) run
    等待出现Thread 1 “pcancel” received signal SIGSEGV, Segmentation fault.
    输入命令bt(打印调用栈)
    (gdb) bt
    #0 0x00007ffff783ecd0 in vfprintf () from /lib/x86_64-linux-gnu/libc.so.6
    #1 0x00007ffff78458a9 in printf () from /lib/x86_64-linux-gnu/libc.so.6
    #2 0x00000000004007f9 in main () at pcancel.c:21
    确定段错误位置是pcancel.c 21行
    如果没有取消点,手动设置一个
    void pthread_testcancel(void);
    设置取消使能或禁止
    int pthread_setcancelstate(int state, int *oldstate);
    PTHREAD_CANCEL_ENABLE
    PTHREAD_CANCEL_DISABLE
    设置取消类型
    int pthread_setcanceltype(int type, int *oldtype);
    PTHREAD_CANCEL_DEFERRED 等到取消点才取消
    PTHREAD_CANCEL_ASYNCHRONOUS 目标线程会立即取消

线程的清理

  • 函数介绍(这两个函数必须成对使用)
    必要性: 当线程非正常终止,需要清理一些资源。
    void pthread_cleanup_push(void(*routine)(void *),void *arg)
    void pthread_cleanup_pop(int execute)
    routine 函数被执行的条件:
    • 被pthread_cancel取消掉。
    • 执行pthread_exit
    • 非0参数执行pthread_cleanup_pop()
      注意:
    • 必须成对使用,即使pthread_cleanup_pop不会被执行到也必须写上,否则编译错误。
    • pthread_cleanup_pop()被执行且参数为0,pthread_cleanup_push回调函数routine不会被执行.
    • pthread_cleanup_push 和pthread_cleanup_pop可以写多对,routine执行顺序正好相反
    • 线程内的return 可以结束线程,也可以给pthread_join返回值,但不能触发pthread_cleanup_push里面的回调函数,所以我们结束线程尽量使用pthread_exit退出线程。
  • 代码如下:
    在这里插入图片描述
  • 执行后发现编译出错,原因是这两个函数需要成对使用,错误如下:
    在这里插入图片描述
  • 如何修改代码如下:
    在这里插入图片描述
  • pthread_cleanup_pop函数的用法
    在这里插入图片描述
  • 执行如下:
    在这里插入图片描述
    注:如果pop(0)改为pop(1)时候则会调用cleanup函数,输出字符串,而且如果调用多个清理函数时,遵循栈的特点,后进先出来执行回调函数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1416623.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RK3588平台开发系列讲解(视频篇)RKMedia框架

文章目录 一、 RKMedia框架介绍二、 RKMedia框架API三、 视频处理流程四、venc 测试案例沉淀、分享、成长,让自己和他人都能有所收获!😄 📢RKMedia是RK提供的一种多媒体处理方案,可实现音视频捕获、音视频输出、音视频编解码等功能。 一、 RKMedia框架介绍 功能: VI(输…

2024.1.28每日一题

LeetCode 水壶问题 365. 水壶问题 - 力扣&#xff08;LeetCode&#xff09; 题目描述 有两个水壶&#xff0c;容量分别为 jug1Capacity 和 jug2Capacity 升。水的供应是无限的。确定是否有可能使用这两个壶准确得到 targetCapacity 升。 如果可以得到 targetCapacity 升水…

CSS 之 图片九宫格变幻效果

一、简介 ​ 本篇博客用于讲解如何实现图片九宫格变幻的样式效果&#xff0c;将图片分为九块填充在33的的九宫格子元素中&#xff0c;并结合grid、hover、transition等CSS属性&#xff0c;实现元素hover时&#xff0c;九宫格子元素合并为一张完整图片的动画效果。 ​ 为了简化…

嵌入式——实时时钟(RTC)

目录 一、初识RTC 1.简介 2.特性 3.后备寄存器和RTC寄存器特性 二、RTC组成 1.相关寄存器 &#xff08;1&#xff09;控制寄存器高位&#xff08;RTC_CRH&#xff09; &#xff08;2&#xff09;控制寄存器低位&#xff08;RTC_CRL&#xff09; &#xff08;3&#xf…

【Linux】分区向左扩容的方法

文章目录 为什么是向左扩容操作前的备份方法&#xff1a;启动盘试用Ubuntu后进行操作 为什么是向左扩容 Linux向右扩容非常简单&#xff0c;无论是系统自带的disks工具还是apt安装的gparted工具&#xff0c;都有图像化的界面可以操作。但是&#xff0c;都不支持向左扩容。笔者…

从 React 到 Qwik:开启高效前端开发的新篇章

1. Qwik Qwik 是一个为构建高性能的 Web 应用程序而设计的前端 JavaScript 框架,它专注于提供即时启动性能,即使是在移动设备上。Qwik 的关键特性是它采用了称为“恢复性”的技术,该技术消除了传统前端框架中常见的 hydration 过程。 恢复性是一种序列化和恢复应用程序状态…

PyTorch深度学习实战(33)——条件生成对抗网络(Conditional Generative Adversarial Network, CGAN)

PyTorch深度学习实战&#xff08;33&#xff09;——条件生成对抗网络 0. 前言1. 条件生成对抗网络1.1 模型介绍1.2 模型与数据集分析 2. 实现条件生成对抗网络小结系列链接 0. 前言 条件生成对抗网络 (Conditional Generative Adversarial Network, CGAN) 是一种生成对抗网络…

C#,最小生成树(MST)普里姆(Prim)算法的源代码

Vojtěch Jarnk 一、Prim算法简史 Prim算法&#xff08;普里姆算法&#xff09;&#xff0c;是1930年捷克数学家算法沃伊捷赫亚尔尼克&#xff08;Vojtěch Jarnk&#xff09;最早设计&#xff1b; 1957年&#xff0c;由美国计算机科学家罗伯特普里姆独立实现&#xff1b; 19…

Spring Boot 项目配置文件

文章目录 配置文件的作用properties基本语法读取文件信息缺点 yml基本语法优点配置不同数据类型字符串类型的写法 配置对象配置集合 读取配置文件的几种方法EnvironmentPropertySource使用原生方式读取 设置不同环境的配置文件 配置文件的作用 整个项目中重要的数据都是在配置…

2000-2022年上市公司全要素生产率测算数据OLS法(含原始数据+测算代码do文档+计算结果)

2000-2022年上市公司全要素生产率测算数据OLS法&#xff08;含原始数据测算代码do文档计算结果&#xff09; 1、时间&#xff1a;2000-2022年 2、范围&#xff1a;上市公司 3、指标&#xff1a;证券代码、证券简称、统计截止日期、固定资产净额、year、股票简称、报表类型编…

【Axure教程0基础入门】00Axure9汉化版下载、安装、汉化、注册+01制作线框图

写在前面&#xff1a;在哔哩哔哩上面找到的Axure自学教程0基础入门课程&#xff0c;播放量最高&#xff0c;5个多小时。课程主要分为4个部分&#xff0c;快速入门、动态面板、常用动效、项目设计。UP主账号【Song老师产品经理课堂】。做个有素质的白嫖er&#xff0c;一键三连必…

【Spark系列3】RDD源码解析实战

本文主要讲 1、什么是RDD 2、RDD是如何从数据中构建 一、什么是RDD&#xff1f; RDD&#xff1a;弹性分布式数据集&#xff0c;Resillient Distributed Dataset的缩写。 个人理解&#xff1a;RDD是一个容错的、并行的数据结构&#xff0c;可以让用户显式的将数据存储到磁盘…

FPGA HDMI IP之DDC(本质I2C协议)通道学习

目的&#xff1a; 使用KingstVIS逻辑分析仪软件分析HDMI的DDC通道传输的SCDC数据&#xff08;遵循I2C协议&#xff09;&#xff0c;同时学习了解SCDC的寄存器与I2C通信协议。 部分英文缩写&#xff1a; HDMIHigh Definition Multi-media Interface高清多媒体接口DDCDisplay Dat…

css文本水波效果

<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>文本水波效果</title><style>* {mar…

网际协议 IP、IP地址

目录 网际协议 IP 虚拟互连网络 使用中间设备进行互连 IP 地址 IP 地址及其表示方法 ​编辑 IP 地址采用 2 级结构 IP 地址的编址方法 分类的 IP 地址 各类 IP 地址的指派范围 一般不使用的特殊的 IP 地址 IPv4网络中的地址类型 分类的 IP 地址的优点和缺点 划分子网…

unity学习笔记----游戏练习05

一、阳光的收集和搜集动画开发 1.收集阳光的思路&#xff1a;当鼠标点击到阳光的时候&#xff0c;就可以进行收集了。可以通过为添加一个碰撞器来检测Circle Collider 2D 编写脚本&#xff1a; 在SunManager中写一个增加阳光的方法 //增加阳光 public void AddSubSun(in…

C++笔记之奇异递归模板模式CRTP(Curiously Recurring Template Pattern)和静态多态

C++笔记之奇异递归模板模式CRTP(Curiously Recurring Template Pattern)和静态多态 —— 杭州 2024-01-28 code review! 文章目录 C++笔记之奇异递归模板模式CRTP(Curiously Recurring Template Pattern)和静态多态一.CRTP二.CRTP 的基本特征表现:基类是一个模板类;派生…

(免费领源码)java#Springboot#mysql旅游景点订票系统68524-计算机毕业设计项目选题推荐

摘 要 科技进步的飞速发展引起人们日常生活的巨大变化&#xff0c;电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流&#xff0c;人类发展的历史正进入一个新时代。在现实运用中&#xff0c;应用软件的工作…

java eazyexcel 实现excel的动态多级联动下拉列表(2)使用MATCH+OFFSET函数

原理 同样是将数据源放到一个新建的隐藏的sheet中&#xff0c;第一行是第一个列表的数据&#xff0c;第二行是每一个有下级菜单的菜单&#xff0c;他下面的行就是他下级菜单的每一值使用MATCH函数从第二行找到上级菜单对应的列根据OFFSET函数从2中获取的列&#xff0c;取得下级…

Linux 系统相关的命令

参考资料 Linux之chmod使用【linux】chmod命令详细用法 目录 一. 系统用户相关1.1 查看当前访问的主机和用户1.2 切换用户1.2.1 设置root用户密码1.2.2 普通用户和root用户切换 1.4 系统状态1.4.1 vmstat 查看当前系统的状态1.4.2 history 查看系统中输入过的命令 二. 系统文件…