零碎知识点

符号优先级

代码行内的空行

初始化STM32里面的外部中断

void TEST_EXTI_Init(void)
{
    //测试外部中断初始化,开启PB14的外部中断
    //RCC AFIO GPIO EXIT
    //RCC   ----    配置时钟
    //AFIO  ----    外部中断引脚选择,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择
    //GPIO  ----    配置GPIO模式为输入模式,三种其中一个即可
    //EXIT  ----    设置中断
    //NVIC  ----    设置中断优先级
    
    //RCC配置GPIO时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    
    //RCC配置AFIO时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
    
    //初始化GPIO引脚
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB,&GPIO_InitStructure);
    
    //开启外部中断资源,在里面用到了AFIO寄存器
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
    
    //配置EXTI的模式
    EXTI_InitTypeDef EXTI_InitStructure;
    EXTI_InitStructure.EXTI_Line = EXTI_Line14;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_Init(&EXTI_InitStructure);
    
    //优先级分组
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
    //设置中断优先级
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_InitStructure);
    
}

void EXTI15_10_IRQHandler(void)
{
    //检测是否为通道14的中断,因为EXTI15_10为共享通道
    if(EXTI_GetITStatus(EXTI_Line14) == SET)
    {
        //清空标志位,否则中断会卡在这里
        EXTI_ClearITPendingBit(EXTI_Line14);
    }
    
}

初始化STM32里面的定时器中断

定时器选择内部时钟输入

void TEST_Timer_Init(void)
{
    //测试定时器中断,开启TIM2,通用定时器
    //定时中断功能、内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式
    //高级定时器、通用定时器、基本定时器
    //RCC、选择时基单元的时钟源、配置时基单元(分频器、自动重装器、计数模式)、配置输出中断控制、配置NVIC
    
    //开启RCC内部时钟
    RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM2,ENABLE);
    
    //选择定时器时钟模式:内部时钟模式
    TIM_InternalClockConfig(TIM2);
    
    //时基单元初始化:预分频器、计数器、自动重装器
    TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure;
    TIM_TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBase_InitStructure.TIM_Period = 10000-1;           //自动重装0-65536
    TIM_TimeBase_InitStructure.TIM_Prescaler = 7200-1;        //预分频0-65536      72000KHZ/7200/10000=1HZ=1s
    TIM_TimeBase_InitStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM2,&TIM_TimeBase_InitStructure);
    
    //防止重启自动进入中断,手动清除自动更新中断标志位
    TIM_ClearFlag(TIM2,TIM_IT_Update);
    
    //开启TIM2中断
    TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
    
    //NVIC优先级分组
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
    //设置中断优先级
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_InitStructure);
    
    //开启TIM2
    TIM_Cmd(TIM2,ENABLE);
}

void TIM2_IRQHandler(void)
{
    //判断是否为TIM2的中断
    if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
    {
        
        //清空标志位,否则程序会卡在这里
        TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
    }
    
}

定时器选择外部时钟输入

void TEST_Timer_Init(void)
{
    //测试定时器中断,开启TIM2,通用定时器
    //定时中断功能、内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式
    //高级定时器、通用定时器、基本定时器
    //RCC、选择时基单元的时钟源、配置时基单元(分频器、自动重装器、计数模式)、配置输出中断控制、配置NVIC
    
    
    //开启RCC内部时钟
    RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM2,ENABLE);
    
    //开启GPIOA的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
    //初始化GPIO,PA0为TIM2对应的引脚
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
    
    //ETR外部时钟实现定时器外部中断
    TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x00);
    
    //时基单元初始化:预分频器:1-1、自动重装器:10-1

    
    //时基单元初始化:预分频器、计数器、自动重装器
    TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure;
    TIM_TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBase_InitStructure.TIM_Period = 10-1          
    TIM_TimeBase_InitStructure.TIM_Prescaler = 1-1;       
    TIM_TimeBase_InitStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM2,&TIM_TimeBase_InitStructure);
    
    //防止重启自动进入中断,手动清除自动更新中断标志位
    TIM_ClearFlag(TIM2,TIM_IT_Update);
    
    //开启TIM2中断
    TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
    
    //NVIC优先级分组
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
    //设置中断优先级
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_InitStructure);
    
    //开启TIM2
    TIM_Cmd(TIM2,ENABLE);
}

void TIM2_IRQHandler(void)
{
    //判断是否为TIM2的中断
    if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
    {
        
        //清空标志位,否则程序会卡在这里
        TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
    }
    
}

fopen()模式字符串

Makefile预定义变量

静态规则

 

 多目标规则

CONFIG 变量的作用

  • -I./Include:指定头文件的搜索路径为 ./Include 目录。

  • -L./Library:指定库文件的搜索路径为 ./Library 目录。

  • CONFIG 变量用于集中管理编译和链接的配置选项。通过 -I 和 -L,可以指定头文件和库文件的搜索路径。

变量描述默认值示例说明
编译器相关
CCC 编译器的名称ccCC = gcc:指定使用 gcc 作为 C 编译器。
CXXC++ 编译器的名称g++CXX = g++:指定使用 g++ 作为 C++ 编译器。
FCFortran 编译器的名称f77FC = gfortran:指定使用 gfortran 作为 Fortran 编译器。
AS汇编器的名称asAS = as:指定使用 as 作为汇编器。
AR静态库打包工具的名称arAR = ar:指定使用 ar 打包静态库。
LD链接器的名称ldLD = ld:指定使用 ld 作为链接器。
编译选项
CFLAGSC 编译器的选项CFLAGS = -Wall -O2:启用所有警告并优化级别为 2。
CXXFLAGSC++ 编译器的选项CXXFLAGS = -std=c++11:指定使用 C++11 标准。
FFLAGSFortran 编译器的选项FFLAGS = -O3:启用 Fortran 编译器的最高优化级别。
CPPFLAGSC 预处理器选项(如 -I 包含目录,-D 定义宏)CPPFLAGS = -Iinclude -DDEBUG:添加 include 目录并定义 DEBUG 宏。
LDFLAGS链接器选项(如 -L 库目录,-l 库名称)LDFLAGS = -Llib -lm:添加 lib 目录并链接数学库。
ARFLAGS静态库打包工具的选项rvARFLAGS = rv:指定 ar 工具的选项为 rv(替换并显示详细信息)。
目标相关
@当前目标的名称$(CC) -o $@$@ 会被替换为目标文件名(如 program)。
<第一个依赖文件的名称$(CC) -c $<$< 会被替换为第一个依赖文件(如 main.c)。
^所有依赖文件的列表$(CC) -o $@ $^$^ 会被替换为所有依赖文件(如 main.c utils.c)。
?比目标更新的依赖文件列表$(CC) -o $@ $?$? 会被替换为比目标更新的依赖文件。
*当前目标的主文件名(不包含扩展名)$(CC) -o $@ $*.c$* 会被替换为目标的主文件名(如 main)。
文件和目录
MAKEmake 命令的名称make$(MAKE):调用 make 命令。
MAKEFILE_LIST当前正在处理的 Makefile 列表$(MAKEFILE_LIST):获取当前 Makefile 的文件名列表。
PWD当前工作目录的路径$(PWD):获取当前工作目录的路径。
VPATH指定搜索依赖文件的目录列表VPATH = src:在 src 目录中搜索依赖文件。
其他常用
SHELL使用的 shell/bin/shSHELL = /bin/bash:指定使用 /bin/bash 作为 shell。
MAKEFLAGSmake 命令的选项MAKEFLAGS += --jobs=4:启用并行编译,最多 4 个任务。
MAKELEVEL递归调用 make 时的嵌套级别$(MAKELEVEL):获取当前 make 的嵌套级别(如 0 表示顶层)。

Makfile自动化变量表格

变量描述示例
$@当前目标的名称如果目标是 program,则 $@ 替换为 program
$<第一个依赖文件的名称如果依赖是 main.c,则 $< 替换为 main.c
$^所有依赖文件的列表(去重)如果依赖是 main.c utils.c,则 $^ 替换为 main.c utils.c
$+所有依赖文件的列表(不去重)如果依赖是 main.c utils.c main.c,则 $+ 替换为 main.c utils.c main.c
$?比目标更新的依赖文件列表如果 main.c 比目标新,则 $? 替换为 main.c
$*当前目标的主文件名(不包含扩展名)如果目标是 main.o,则 $* 替换为 main
$%当目标是静态库时,表示库成员名称如果目标是 libfoo.a(bar.o),则 $% 替换为 bar.o

linux文件列表

Linux系统的目录结构中,不同的文件夹分属不同的功能,列表如下:

目录名

功能

/

根目录,所有的分区和文件的起点

/bin

普通用户可用的命令所在路径

/sbin

超级用户(root)可用的命令所在路径

/etc

各种配置文件所在路径

/home

所有普通用户的家目录所在路径

/lib

系统库文件所在路径

/boot

系统内核镜像文件所在路径

/dev

设备节点所在路径

/usr

第三方库和服务默认安装路径

/proc

系统进程数据在文件系统中的挂载路径

/sys

系统内核数据在文件系统中的挂载路径

/var

存放一些“易变”的文件,例如安装包、系统日志等

/mnt

默认的分区挂载路径

STM32各链接库与启动文件与芯片之间的联系

链接脚本文件启动文件芯片类型Flash大小RAM大小
STM32F100XB_FLASH.ldstartup_stm32f100xb.sSTM32F100xB 系列(如 STM32F100C4, STM32F100R6)16KB - 128KB4KB - 8KB
STM32F100XE_FLASH.ldstartup_stm32f100xe.sSTM32F100xE 系列(如 STM32F100RC, STM32F100ZE)256KB - 512KB24KB - 32KB
STM32F101X6_FLASH.ldstartup_stm32f101x6.sSTM32F101x6 系列(如 STM32F101C4, STM32F101R6)16KB - 32KB4KB - 6KB
STM32F101XB_FLASH.ldstartup_stm32f101xb.sSTM32F101xB 系列(如 STM32F101C8, STM32F101RB)64KB - 128KB10KB - 16KB
STM32F101XE_FLASH.ldstartup_stm32f101xe.sSTM32F101xE 系列(如 STM32F101RC, STM32F101RE)256KB - 512KB32KB - 48KB
STM32F101XG_FLASH.ldstartup_stm32f101xg.sSTM32F101xG 系列(如 STM32F101RG, STM32F101VG)512KB - 1MB48KB - 80KB
STM32F102X6_FLASH.ldstartup_stm32f102x6.sSTM32F102x6 系列(如 STM32F102C4, STM32F102R6)16KB - 32KB4KB - 6KB
STM32F102XB_FLASH.ldstartup_stm32f102xb.sSTM32F102xB 系列(如 STM32F102C8, STM32F102RB)64KB - 128KB10KB - 16KB
STM32F103X6_FLASH.ldstartup_stm32f103x6.sSTM32F103x6 系列(如 STM32F103C6, STM32F103R6)32KB6KB
STM32F103XB_FLASH.ldstartup_stm32f103xb.sSTM32F103xB 系列(如 STM32F103C8, STM32F103RB)
(包含 STM32F103C8T6)
64KB - 128KB20KB
STM32F103XE_FLASH.ldstartup_stm32f103xe.sSTM32F103xE 系列(如 STM32F103RC, STM32F103RE)256KB - 512KB48KB - 64KB
STM32F103XG_FLASH.ldstartup_stm32f103xg.sSTM32F103xG 系列(如 STM32F103RG, STM32F103VG)512KB - 1MB64KB - 96KB
STM32F105XC_FLASH.ldstartup_stm32f105xc.sSTM32F105xC 系列(如 STM32F105R8, STM32F105VC)
(互联型,带 USB OTG)
64KB - 256KB64KB
STM32F107XC_FLASH.ldstartup_stm32f107xc.sSTM32F107xC 系列(如 STM32F107RB, STM32F107VC)
(互联型,带以太网)
256KB64KB

标准库与HALI库 

标准库与HAL库的主要差异

特性标准库(SPL)HAL库(CubeMX生成)
启动文件通常使用.s汇编文件,但可能需适配GCC语法提供预适配GCC的启动文件(如startup_xxx.s
链接脚本需要手动编写或修改.ld文件自动生成匹配芯片型号的.ld文件
外设驱动接口基于寄存器操作的函数库基于硬件抽象层(HAL),兼容性更高
工程模板通常针对MDK-ARM/IAR设计,需自行移植到GCC直接支持GCC/Makefile

linux signals

Signal Name    Number    Description
SIGHUP    1    Hangup (POSIX)
SIGINT    2    Terminal interrupt (ANSI)
SIGQUIT    3    Terminal quit (POSIX)
SIGILL    4    Illegal instruction (ANSI)
SIGTRAP    5    Trace trap (POSIX)
SIGIOT    6    IOT Trap (4.2 BSD)
SIGBUS    7    BUS error (4.2 BSD)
SIGFPE    8    Floating point exception (ANSI)
SIGKILL    9    Kill(can't be caught or ignored) (POSIX)
SIGUSR1    10    User defined signal 1 (POSIX)
SIGSEGV    11    Invalid memory segment access (ANSI)
SIGUSR2    12    User defined signal 2 (POSIX)
SIGPIPE    13    Write on a pipe with no reader, Broken pipe (POSIX)
SIGALRM    14    Alarm clock (POSIX)
SIGTERM    15    Termination (ANSI)
SIGSTKFLT    16    Stack fault
SIGCHLD    17    Child process has stopped or exited, changed (POSIX)
SIGCONT    18    Continue executing, if stopped (POSIX)
SIGSTOP    19    Stop executing(can't be caught or ignored) (POSIX)
SIGTSTP    20    Terminal stop signal (POSIX)
SIGTTIN    21    Background process trying to read, from TTY (POSIX)
SIGTTOU    22    Background process trying to write, to TTY (POSIX)
SIGURG    23    Urgent condition on socket (4.2 BSD)
SIGXCPU    24    CPU limit exceeded (4.2 BSD)
SIGXFSZ    25    File size limit exceeded (4.2 BSD)
SIGVTALRM    26    Virtual alarm clock (4.2 BSD)
SIGPROF    27    Profiling alarm clock (4.2 BSD)
SIGWINCH    28    Window size change (4.3 BSD, Sun)
SIGIO    29    I/O now possible (4.2 BSD)
SIGPWR    30    Power failure restart (System V)

Linux系统中文件的分类

在Linux中,文件总共被分成了7种,他们分别是:

  1. 普通文件(-):存在于外部存储器中,用于存储普通数据。
  2. 目录文件(d):用于存放目录项,是文件系统管理的重要文件类型。
  3. 管道文件(p):一种用于进程间通信的特殊文件,也称为命名管道FIFO。
  4. 套接字文件(s):一种用于网络间通信的特殊文件。
  5. 链接文件(l):用于间接访问另外一个目标文件,相当于Windows快捷方式。
  6. 字符设备文件(c):字符设备在应用层的访问接口。
  7. 块设备文件(b):块设备在应用层的访问接口。
  • -(regular)普通文件
  • d(directory)目录文件
  • p(pipe)管道文件(命名管道)
  • s(socket)套接字文件(Unix域/本地域套接字)
  • l(link)链接文件(软链接)
  • c(character)字符设备文件
  • b(block)块设备文件

系统函数与标准库函数

https://zhuanlan.zhihu.com/p/392588996

git命令速查 

在Git中删除分支的操作分为两种:删除本地分支删除远程分支。以下是各自的操作方法:

删除本地分支

        将分支名替换为您想要删除的远程分支名称。

        确保您不在要删除的分支上:

        不能在当前激活的分支上执行删除操作。首先切换到不同的分支,例如切换到master或main分支:

git checkout master

        删除本地分支:

git branch -d 分支名

        替换分支名为您想要删除的分支名称。如果该分支有尚未合并的更改,这个命令会失败。

        如果您确定要删除一个未完全合并的分支,可以使用-D选项强制删除:

git branch -D 分支名

        删除远程分支:
        要删除远程仓库的分支,使用以下命令:

git push origin --delete 分支名

git pull origin master --allow-unrelated-histories

 一个比较万能的Makefile脚本

#定义一个CC 编译器变量
CC=gcc
#目标变量
TARGET=main
#搜索所有.c文件
#SRC=$(wildcard  *.c)
#搜索所有.c文件
SRC= $(shell find ./  -name '*.c')
#把.c 转换为 .o 
OBJ=$(SRC:%.c=%.o) 
#编译命令  
$(TARGET):$(OBJ)
    $(CC)  $(OBJ)  -o $(TARGET)
#修改探测规则  
$(OBJ):%.o:%.c
    $(CC) $^ -o $@  -c
#执行shell命令
test:
    @echo $(OBJ)   
    echo $(SRC)

#增加 clean 编译规则 ,当输入make clean 时会执行该规则 
clean:
    rm main
    rm *.o

需要修改版

# 定义编译器变量
CC = gcc

# 定义目标可执行文件
TARGET = main

# 搜索当前目录下的所有 .c 文件
SRC = $(wildcard *.c)

#集中将需要生成的.o文件放入obj中
OBJS_DIR = ./obj

# 将 .c 文件转换为对应的 .o 文件
OBJ = $(SRC:%.c=$(OBJS_DIR)%.o)

# 定义头文件目录(如果有多个目录,用空格分隔)
INCLUDE_DIRS = -I ./

# 定义需要链接的库(如果有)
LIBS = -lm

# 编译选项(包括调试信息、优化级别等)
CFLAGS = -g -Wall -O2 $(INCLUDE_DIRS)

# 链接选项
LDFLAGS = $(LIBS)

# 默认目标
all: $(TARGET)

# 生成可执行文件的规则
$(TARGET): $(OBJ)
    $(CC) $(OBJ) $(LDFLAGS) -o $(TARGET)
    @echo "编译成功!生成 $(TARGET)"

# 生成 .o 文件的通用规则
$(OBJS_DIR)/%.o: %.c
    $(CC) $(CFLAGS) -c $< -o $@
    @echo "编译 $< 生成 $@"

# 生成依赖文件的规则(使用自动依赖生成)
$(OBJS_DIR)%.d: %.c
    $(CC) $(CFLAGS) -M $< > $@
    @echo "生成依赖文件 $@"

# 生成依赖文件并编译
deps: $(SRC:%.c=%.d)
    @echo "生成所有依赖文件"

# 清理编译生成的文件
clean:
    rm -f $(TARGET) $(OBJ) *.d
    @echo "清理完成!"

# 格式化代码
format:
    clang-format -i $(SRC)
    @echo "代码格式化完成!"

# 调试
debug:
    $(CC) $(CFLAGS) -g $(SRC) $(LDFLAGS) -o $(TARGET)
    gdb $(TARGET)

# 帮助信息
help:
    @echo "可用命令:"
    @echo "  make          编译项目"
    @echo "  make clean    清理生成文件"
    @echo "  make format   格式化代码"
    @echo "  make debug    编译并启动调试器"
    @echo "  make deps     生成依赖文件"

如何让管道编程非阻塞

还别说,下图还蛮重要的。值得一提的点还有,open管道文件时,必须读者与写者同时存在。 

方法一:在打开管道文件时直接添加非阻塞选项。

// 直接打开
// 设置管道文件为非阻塞模式 O_NONBLOCK
int fd_fifo = open( FIFO_PATH , O_RDONLY | O_NONBLOCK );
if (fd_fifo < 0)
{
    perror ("open fifo error");
    exit(0);
}

方法二:通过 fcntl 后期设置为非阻塞或阻塞。

// 通过fcntl 获取指定描述符的属性标记
long flag = fcntl(fd, F_GETFL);

// 通过位或把非阻塞属性设置到获取到的标记Flag 中
flag |= O_NONBLOCK;

// 通过 fcntl 把添加了非阻塞选项的flag 设置回文件中
fcntl(fd , F_SETFL, flag);

信号

1.主要信号

 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX
  • 标准信号:前31个信号,它们是从Unix系统继承下来的经典系统元素,他们有如下的特点:
    • 不排队,信号的响应会相互嵌套(正在响应某一个信号时收到新的信号则会嵌套)。
    • 如果目标进程没有及时响应,那么随后到达的相同信号将会被丢弃。
    • 每个信号都对应一个系统事件(除了SIGUSR1和SIGUSR2),当这个事件发生时,将产生这个信号。
    • 在进程的挂起信号中,进程会优先响应实时信号。
  • 实时信号:后31个信号,它们是Linux系统新增的实时信号,也被称为“可靠信号”,这些信号的特征是:
    • 实时信号的响应次序按接收顺序排队,不嵌套。
    • 即使相同的实时信号被同时发送多次,也不会被丢弃,而会依次挨个响应。
    • 实时信号没有特殊的系统事件与之对应。
    • 实时信号的优先级按值的大小来定。

2.信号的生命周期

3. 信号的默认处理

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)
       SIGWINCH       -        Ign     Window resize signal (4.3BSD, Sun)

       The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.
  • Term:中断目标进程。
  • Core:中断目标进程,且产生核心转储文件core。
  • Stop:暂停目标进程,直到收到信号SIGCONT
  • Cont:恢复目标进程运行
  • Ign:忽略信号

 4.信号的屏蔽 

 屏蔽信号实际上就是暂缓对信号的响应,采用如下函数进行对信号的屏蔽:

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
参数分析:
    how --> 操作命令、选项
        SIG_BLOCK
              The set of blocked signals is the union of the current set and the set
              argument.
              在当前阻塞状态的集合中添加新的阻塞目标set (文件中的追加信息)

       SIG_UNBLOCK
              The signals in set are removed from the current set  of  blocked  sig‐
              nals.   It  is permissible to attempt to unblock a signal which is not
              blocked.
              解除set中所有被阻塞的信号

       SIG_SETMASK
              The set of blocked signals is set to the argument set.
              把当前阻塞的信号集合替换为set (文件中的清空后写入)

参数简析:

  • how:操作命令字,比如阻塞、解除阻塞等
  • set:当前要操作的信号集
  • oldset:若为非空,则将原有阻塞信号集保留到该oldset中,方便后期进行恢复
// 信号集操作函数组
#include <signal.h>
int sigemptyset(sigset_t *set);   // 清空信号集set
int sigfillset(sigset_t *set);    // 将所有信号加入信号集set中
int sigaddset(sigset_t *set, int signum);         // 将信号signum添加到信号集set中
int sigdelset(sigset_t *set, int signum);         // 将信号signum从信号集set中剔除
int sigismember(const sigset_t *set, int signum); // 测试信号signum是否在信号集set中

 如何阻塞某一个或某一些信号:

    • 创建一个新的信号集
    • 清空信号集合sigemptyset
    • 把指定需要阻塞的信号或多个信号添加到集合中
    • 通过sigprocmask进行设置阻塞
    • 当不需要阻塞时通过sigprocmask进行接触阻塞

 TCP端口号

端口号分类

根据 IANA(互联网编号分配机构) 的划分:

  1. 知名端口(0-1023)

    • 分配给系统核心服务(如HTTP 80、SSH 22)。

    • 需管理员权限才能使用。

  2. 注册端口(1024-49151)

    • 分配给用户级应用(如MySQL 3306、Redis 6379)。

    • 需向IANA注册以避免冲突。

  3. 动态/私有端口(49152-65535)

    • 临时分配给客户端或私有服务(如P2P下载、临时会话)。

Service Name and Transport Protocol Port Number Registry

端口号协议服务/功能说明
20/21TCPFTP(文件传输协议)20端口用于数据传输,21端口用于控制连接
22TCPSSH(安全外壳协议)加密的远程登录和安全文件传输
23TCPTelnet明文远程登录(不安全,逐渐被SSH替代)
25TCPSMTP(简单邮件传输协议)邮件服务器之间的邮件发送
53TCP/UDPDNS(域名系统)UDP用于快速查询,TCP用于区域传输或长响应
67/68UDPDHCP(动态主机配置协议)67端口为服务器,68端口为客户端
80TCPHTTP(超文本传输协议)网页浏览标准端口
110TCPPOP3(邮局协议第3版)从邮件服务器接收邮件
123UDPNTP(网络时间协议)系统时间同步
143TCPIMAP(互联网邮件访问协议)远程管理邮件服务器上的邮件
161/162UDPSNMP(简单网络管理协议)161用于监控设备,162用于告警通知
443TCPHTTPS(安全超文本传输协议)加密的网页通信(HTTP over TLS/SSL)
465TCPSMTPS(安全SMTP)加密的邮件传输(SMTP over SSL)
587TCPSMTP Submission邮件客户端提交邮件到服务器的端口(支持加密)
636TCPLDAPS(安全LDAP)加密的目录访问协议
993TCPIMAPS(安全IMAP)加密的IMAP协议
995TCPPOP3S(安全POP3)加密的POP3协议
1433TCPMicrosoft SQL Server微软数据库默认端口
1521TCPOracle数据库Oracle数据库监听端口
1723TCPPPTP(点对点隧道协议)VPN连接协议(安全性较低,逐渐被替代)
1900UDPUPnP(通用即插即用)设备自动发现和配置
2082TCPcPanel默认端口网站管理控制台
2083TCPcPanel HTTPS加密的cPanel访问
3306TCPMySQL数据库MySQL数据库默认端口
3389TCPRDP(远程桌面协议)Windows远程桌面连接
5432TCPPostgreSQL数据库PostgreSQL默认端口
5900TCPVNC(虚拟网络控制台)远程桌面共享(可扩展至5901、5902等)
6379TCPRedis数据库内存键值数据库
8080TCPHTTP备用端口常用于代理服务器或替代HTTP 80端口
8443TCPHTTPS备用端口替代HTTPS 443端口(如Tomcat管理界面)
27017TCPMongoDB数据库默认数据库端口

子进程与父进程的继承 

  • 一个进程复刻一个子进程的时候,会将自身几乎所有的资源复制一份,具体如下:
    • 父子进程的以下属性在创建之初完全一样:
      A) 实际UID和GID,以及有效UID和GID。
      B) 所有环境变量。
      C) 进程组ID和会话ID。
      D) 当前工作路径。
      E) 打开的文件。
      F) 信号响应函数。
      G) 整个内存空间,包括栈、堆、数据段、代码段、标准IO的缓冲区等等。

    • 而以下属性,父子进程是不一样的:
      A) 进程号PID。PID是身份证号码,哪怕亲如父子,也要区分开。
      B) 记录锁。父进程对某文件加了把锁,子进程不会继承这把锁。
      C) 挂起的信号。这是所谓“悬而未决”的信号,等待着进程的响应,子进程不会继承这些信号。

进程的从生到死

 

 

sqlite3的apt安装与源文件使用

1.apt安装

apt安装的是二进制文件,如果要写到代码(c语言)中还需要头文件,因此完整安装应该是:

sudo apt install sqlite3            #安装二进制文件
sudo apt install libsqlite3-dev     #安装头文件,在/usr/include可查看

调用使用#include<sqlite3.h>

2.源文件使用

下载源码https://sqlite.org/2025/sqlite-autoconf-3500200.tar.gz

直接将源文件(sqlite3.c)和头文件(sqlite3.h)加入的项目中,然后调用#include "sqlite.h" 

一个比较通用的CMakeLists.txt文件

# 指定 CMake 最低版本要求
cmake_minimum_required(VERSION 3.10)

# 指定项目名称、版本和语言
project(main VERSION 1.0.0 LANGUAGES C CXX)

# 设置 C 和 C++ 编译器
#set(CMAKE_C_COMPILER "arm-linux-gcc")
#set(CMAKE_CXX_COMPILER "arm-linux-g++")
set(CMAKE_C_COMPILER "arm-linux-gcc")
set(CMAKE_CXX_COMPILER "arm-linux-g++")


# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 使用 CMake 内置变量获取项目根目录
set(PROJECT_DIR ${CMAKE_SOURCE_DIR})

# 检索源文件
file(GLOB_RECURSE C_SOURCES CONFIGURE_DEPENDS
    ${PROJECT_DIR}/src/*.c
)

file(GLOB_RECURSE CXX_SOURCES CONFIGURE_DEPENDS
    ${PROJECT_DIR}/src/*.cpp
)

# 添加可执行文件
add_executable(${PROJECT_NAME} 
    ${PROJECT_DIR}/main.cpp
    ${C_SOURCES}
    ${CXX_SOURCES}
)

# 指定头文件路径
target_include_directories(${PROJECT_NAME} PRIVATE
    ${PROJECT_DIR}/inc
    ${PROJECT_DIR}/lib
)

# 添加子目录(如果有)
# add_subdirectory(lib)

# 链接库文件
# target_link_directories(${PROJECT_NAME} PRIVATE ${PROJECT_DIR}/lib)
target_link_libraries(${PROJECT_NAME} PRIVATE pthread)

# 安装目标(可选)
#install(TARGETS ${PROJECT_NAME} DESTINATION bin)

线程池C++实现

threadpool.h

#ifndef THREAD_POOL_H
#define THREAD_POOL_H

#include <pthread.h>

class threadpool
{
private:
    struct Task
    {
        void *(*function)(void *);
        void *arg;
        Task *next;
    };

    pthread_mutex_t mutex;
    pthread_cond_t cond;
    bool shutdown;
    int thread_count;
    int task_count;
    pthread_t *threads;
    Task *task_queue_head;
    Task *task_queue_tail;

    static void *thread_worker(void *arg);

public:
    threadpool(int thread_count = 10);

    // 声明这两个函数并标记为 = delete 的目的就是为了让它们无法被使用。
    // 防止编译器自动生成拷贝操作
    // 这个类设计上就是不可拷贝的,任何拷贝尝试都是错误的
    threadpool(const threadpool &other) = delete;
    threadpool &operator=(const threadpool &other) = delete;

    void shutdown_pool();
    bool add_task(void *(*function)(void *), void *arg);

    virtual ~threadpool();
};

#endif // THREAD_POOL_H

threadpool.cpp

#include "threadpool.h"

threadpool::threadpool(int thread_count) : shutdown(false),
                                           thread_count(thread_count),
                                           task_count(0),
                                           threads(nullptr),
                                           task_queue_head(nullptr),
                                           task_queue_tail(nullptr)
{
    pthread_mutex_init(&mutex, nullptr);
    pthread_cond_init(&cond, nullptr);

    threads = new pthread_t[thread_count];
    for (int i = 0; i < thread_count; i++)
    {
        pthread_create(&threads[i], nullptr, thread_worker, this);
    }
}

threadpool::~threadpool()
{
    shutdown_pool();

    // 清理未完成的任务
    Task *current = task_queue_head;
    while (current)
    {
        Task *next = current->next;
        delete current;
        current = next;
    }

    delete[] threads;
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
}

void threadpool::shutdown_pool()
{
    pthread_mutex_lock(&mutex);
    shutdown = true;
    pthread_cond_broadcast(&cond);
    pthread_mutex_unlock(&mutex);

    // 等待所有线程退出
    for (int i = 0; i < thread_count; i++)
    {
        pthread_join(threads[i], nullptr);
    }
}

void *threadpool::thread_worker(void *arg)
{
    threadpool *pool = static_cast<threadpool *>(arg);

    while (true)
    {
        pthread_mutex_lock(&pool->mutex);

        // 等待任务或关闭信号
        while (pool->task_count == 0 && !pool->shutdown)
        {
            pthread_cond_wait(&pool->cond, &pool->mutex);
        }

        // 检查是否需要退出
        if (pool->shutdown && pool->task_count == 0)
        {
            pthread_mutex_unlock(&pool->mutex);
            pthread_exit(nullptr);
        }

        // 获取任务
        Task *task = pool->task_queue_head;
        if (task)
        {
            pool->task_queue_head = task->next;
            pool->task_count--;

            if (!pool->task_queue_head)
            {
                pool->task_queue_tail = nullptr;
            }
        }
        pthread_mutex_unlock(&pool->mutex);

        // 执行任务
        if (task)
        {
            task->function(task->arg);
            delete task;
        }
    }
    return nullptr;
}

bool threadpool::add_task(void *(*function)(void *), void *arg)
{
    if (shutdown)
    {
        return false;
    }

    Task *new_task = new Task();
    new_task->function = function;
    new_task->arg = arg;
    new_task->next = nullptr;

    pthread_mutex_lock(&mutex);

    if (!task_queue_tail)
    {
        task_queue_head = task_queue_tail = new_task;
    }
    else
    {
        task_queue_tail->next = new_task;
        task_queue_tail = new_task;
    }

    task_count++;
    pthread_cond_signal(&cond); // 通知等待的线程
    pthread_mutex_unlock(&mutex);

    return true;
}

main.cpp

#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <pthread.h>
#include "threadpool.h"

//测试任务线程
void *task(void *arg)
{
    long int num = (long int)arg;
    sleep(1);
    printf("[%ld]---->%ld\n", pthread_self(), num);
    // std::cout << "["<<pthread_self()<<"]"<<"---->"<<num << std::endl;
    return NULL;
}

int main(int argc, char const *argv[])
{
    threadpool pool(25);
    for (long int i = 0; i < 100; i++)
    {
        pool.add_task(task, (void *)i);
    }

    return 0;
}

线程池C实现

threadpool.h

// threadpool.h
#ifndef THREAD_POOL_H
#define THREAD_POOL_H

#include <pthread.h>
#include <stdbool.h>

typedef struct task_struct
{
    void *(*function)(void *);
    void *arg;
    struct task_struct *next;
} Task_t;

typedef struct threadpool_struct
{
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    pthread_t *threads;
    Task_t *task_queue_head;
    Task_t *task_queue_tail;
    bool shutdown;
    int thread_count;
    int task_count; // 新增任务计数器
} ThreadPool;

ThreadPool *threadpool_create(int thread_count);
bool threadpool_add_task(ThreadPool *pool, void *(*task)(void *), void *arg);
void threadpool_destroy(ThreadPool *pool); // 修正函数名拼写

#endif // THREAD_POOL_H

threadpool.c

// threadpool.c
#include "threadpool.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

static void *thread_worker(void *arg)
{
    ThreadPool *pool = (ThreadPool *)arg;
    while (1)
    {
        pthread_mutex_lock(&pool->mutex);

        // 等待条件:线程池未关闭且任务队列为空
        while (!pool->shutdown && pool->task_queue_head == NULL)
        {
            pthread_cond_wait(&pool->cond, &pool->mutex);
        }

        // 检查是否需要终止线程
        if (pool->shutdown && pool->task_queue_head == NULL)
        {
            pthread_mutex_unlock(&pool->mutex);
            pthread_exit(NULL);
        }

        // 提取任务
        Task_t *task = pool->task_queue_head;
        pool->task_queue_head = task->next;
        pool->task_count--;

        // 更新尾指针(如果队列变空)
        if (pool->task_queue_head == NULL)
        {
            pool->task_queue_tail = NULL;
        }
        pthread_mutex_unlock(&pool->mutex);

        // 执行任务并释放资源
        task->function(task->arg);
        free(task);
    }
    return NULL;
}

ThreadPool *threadpool_create(int thread_count)
{
    if (thread_count <= 0)
    {
        fprintf(stderr, "Invalid thread count\n");
        return NULL;
    }

    ThreadPool *pool = (ThreadPool *)calloc(1, sizeof(ThreadPool));
    if (!pool)
    {
        perror("calloc pool");
        return NULL;
    }

    // 初始化同步原语
    if (pthread_mutex_init(&pool->mutex, NULL) != 0 ||
        pthread_cond_init(&pool->cond, NULL) != 0)
    {
        perror("mutex/cond init");
        free(pool);
        return NULL;
    }

    // 创建线程数组
    pool->threads = (pthread_t *)calloc(thread_count, sizeof(pthread_t));
    if (!pool->threads)
    {
        perror("calloc threads");
        pthread_mutex_destroy(&pool->mutex);
        pthread_cond_destroy(&pool->cond);
        free(pool);
        return NULL;
    }

    // 创建工作线程
    for (int i = 0; i < thread_count; i++)
    {
        if (pthread_create(&pool->threads[i], NULL, thread_worker, pool) != 0)
        {
            perror("pthread_create");
            // 创建失败时清理已创建的线程
            pool->shutdown = true;
            pthread_cond_broadcast(&pool->cond);
            for (int j = 0; j < i; j++)
            {
                pthread_join(pool->threads[j], NULL);
            }
            free(pool->threads);
            pthread_mutex_destroy(&pool->mutex);
            pthread_cond_destroy(&pool->cond);
            free(pool);
            return NULL;
        }
    }

    // 初始化状态
    pool->thread_count = thread_count;
    pool->task_count = 0;
    return pool;
}

bool threadpool_add_task(ThreadPool *pool, void *(*task)(void *), void *arg)
{
    if (!pool || !task)
    {
        return false;
    }

    pthread_mutex_lock(&pool->mutex);

    // 检查线程池是否已关闭
    if (pool->shutdown)
    {
        pthread_mutex_unlock(&pool->mutex);
        return false;
    }

    // 创建新任务
    Task_t *new_task = (Task_t *)calloc(1, sizeof(Task_t));
    if (!new_task)
    {
        perror("calloc new_task");
        pthread_mutex_unlock(&pool->mutex);
        return false;
    }
    new_task->function = task;
    new_task->arg = arg;
    new_task->next = NULL;

    // 添加到任务队列
    if (pool->task_queue_tail)
    {
        pool->task_queue_tail->next = new_task;
    }
    else
    {
        pool->task_queue_head = new_task;
    }
    pool->task_queue_tail = new_task;
    pool->task_count++;

    // 通知工作线程
    pthread_cond_signal(&pool->cond);
    pthread_mutex_unlock(&pool->mutex);
    return true;
}

void threadpool_destroy(ThreadPool *pool)
{
    if (!pool)
        return;

    pthread_mutex_lock(&pool->mutex);
    // 设置关闭标志
    if (!pool->shutdown)
    {
        pool->shutdown = true;
        // 唤醒所有等待线程
        pthread_cond_broadcast(&pool->cond);
    }
    pthread_mutex_unlock(&pool->mutex);

    // 等待所有线程退出
    for (int i = 0; i < pool->thread_count; i++)
    {
        pthread_join(pool->threads[i], NULL);
    }

    // 释放剩余任务
    Task_t *task = pool->task_queue_head;
    while (task)
    {
        Task_t *next = task->next;
        free(task);
        task = next;
    }

    // 清理资源
    free(pool->threads);
    pthread_mutex_destroy(&pool->mutex);
    pthread_cond_destroy(&pool->cond);
    free(pool);
}

main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <pthread.h>
#include "threadpool.h"

void *task(void *arg)
{
    long int num = (long int)arg;
    printf("[%ld]-->%ld\n",pthread_self(),num);
    sleep(1);
}

int main(int argc, char const *argv[])
{
    ThreadPool *pool = threadpool_create(25);
    for(long int i = 0;i < 100;i++)
    {
        threadpool_add_task(pool,(void *)task,(void *)i);
    }
    
    getchar();
    threadpool_destroy(pool);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值