符号优先级
代码行内的空行
初始化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
,可以指定头文件和库文件的搜索路径。
变量 | 描述 | 默认值 | 示例说明 |
---|---|---|---|
编译器相关 | |||
CC | C 编译器的名称 | cc | CC = gcc :指定使用 gcc 作为 C 编译器。 |
CXX | C++ 编译器的名称 | g++ | CXX = g++ :指定使用 g++ 作为 C++ 编译器。 |
FC | Fortran 编译器的名称 | f77 | FC = gfortran :指定使用 gfortran 作为 Fortran 编译器。 |
AS | 汇编器的名称 | as | AS = as :指定使用 as 作为汇编器。 |
AR | 静态库打包工具的名称 | ar | AR = ar :指定使用 ar 打包静态库。 |
LD | 链接器的名称 | ld | LD = ld :指定使用 ld 作为链接器。 |
编译选项 | |||
CFLAGS | C 编译器的选项 | 无 | CFLAGS = -Wall -O2 :启用所有警告并优化级别为 2。 |
CXXFLAGS | C++ 编译器的选项 | 无 | CXXFLAGS = -std=c++11 :指定使用 C++11 标准。 |
FFLAGS | Fortran 编译器的选项 | 无 | FFLAGS = -O3 :启用 Fortran 编译器的最高优化级别。 |
CPPFLAGS | C 预处理器选项(如 -I 包含目录,-D 定义宏) | 无 | CPPFLAGS = -Iinclude -DDEBUG :添加 include 目录并定义 DEBUG 宏。 |
LDFLAGS | 链接器选项(如 -L 库目录,-l 库名称) | 无 | LDFLAGS = -Llib -lm :添加 lib 目录并链接数学库。 |
ARFLAGS | 静态库打包工具的选项 | rv | ARFLAGS = rv :指定 ar 工具的选项为 rv (替换并显示详细信息)。 |
目标相关 | |||
@ | 当前目标的名称 | 无 | $(CC) -o $@ :$@ 会被替换为目标文件名(如 program )。 |
< | 第一个依赖文件的名称 | 无 | $(CC) -c $< :$< 会被替换为第一个依赖文件(如 main.c )。 |
^ | 所有依赖文件的列表 | 无 | $(CC) -o $@ $^ :$^ 会被替换为所有依赖文件(如 main.c utils.c )。 |
? | 比目标更新的依赖文件列表 | 无 | $(CC) -o $@ $? :$? 会被替换为比目标更新的依赖文件。 |
* | 当前目标的主文件名(不包含扩展名) | 无 | $(CC) -o $@ $*.c :$* 会被替换为目标的主文件名(如 main )。 |
文件和目录 | |||
MAKE | make 命令的名称 | make | $(MAKE) :调用 make 命令。 |
MAKEFILE_LIST | 当前正在处理的 Makefile 列表 | 无 | $(MAKEFILE_LIST) :获取当前 Makefile 的文件名列表。 |
PWD | 当前工作目录的路径 | 无 | $(PWD) :获取当前工作目录的路径。 |
VPATH | 指定搜索依赖文件的目录列表 | 无 | VPATH = src :在 src 目录中搜索依赖文件。 |
其他常用 | |||
SHELL | 使用的 shell | /bin/sh | SHELL = /bin/bash :指定使用 /bin/bash 作为 shell。 |
MAKEFLAGS | make 命令的选项 | 无 | 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.ld | startup_stm32f100xb.s | STM32F100xB 系列(如 STM32F100C4, STM32F100R6) | 16KB - 128KB | 4KB - 8KB |
STM32F100XE_FLASH.ld | startup_stm32f100xe.s | STM32F100xE 系列(如 STM32F100RC, STM32F100ZE) | 256KB - 512KB | 24KB - 32KB |
STM32F101X6_FLASH.ld | startup_stm32f101x6.s | STM32F101x6 系列(如 STM32F101C4, STM32F101R6) | 16KB - 32KB | 4KB - 6KB |
STM32F101XB_FLASH.ld | startup_stm32f101xb.s | STM32F101xB 系列(如 STM32F101C8, STM32F101RB) | 64KB - 128KB | 10KB - 16KB |
STM32F101XE_FLASH.ld | startup_stm32f101xe.s | STM32F101xE 系列(如 STM32F101RC, STM32F101RE) | 256KB - 512KB | 32KB - 48KB |
STM32F101XG_FLASH.ld | startup_stm32f101xg.s | STM32F101xG 系列(如 STM32F101RG, STM32F101VG) | 512KB - 1MB | 48KB - 80KB |
STM32F102X6_FLASH.ld | startup_stm32f102x6.s | STM32F102x6 系列(如 STM32F102C4, STM32F102R6) | 16KB - 32KB | 4KB - 6KB |
STM32F102XB_FLASH.ld | startup_stm32f102xb.s | STM32F102xB 系列(如 STM32F102C8, STM32F102RB) | 64KB - 128KB | 10KB - 16KB |
STM32F103X6_FLASH.ld | startup_stm32f103x6.s | STM32F103x6 系列(如 STM32F103C6, STM32F103R6) | 32KB | 6KB |
STM32F103XB_FLASH.ld | startup_stm32f103xb.s | STM32F103xB 系列(如 STM32F103C8, STM32F103RB) (包含 STM32F103C8T6) | 64KB - 128KB | 20KB |
STM32F103XE_FLASH.ld | startup_stm32f103xe.s | STM32F103xE 系列(如 STM32F103RC, STM32F103RE) | 256KB - 512KB | 48KB - 64KB |
STM32F103XG_FLASH.ld | startup_stm32f103xg.s | STM32F103xG 系列(如 STM32F103RG, STM32F103VG) | 512KB - 1MB | 64KB - 96KB |
STM32F105XC_FLASH.ld | startup_stm32f105xc.s | STM32F105xC 系列(如 STM32F105R8, STM32F105VC) (互联型,带 USB OTG) | 64KB - 256KB | 64KB |
STM32F107XC_FLASH.ld | startup_stm32f107xc.s | STM32F107xC 系列(如 STM32F107RB, STM32F107VC) (互联型,带以太网) | 256KB | 64KB |
标准库与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种,他们分别是:
- 普通文件(-):存在于外部存储器中,用于存储普通数据。
- 目录文件(d):用于存放目录项,是文件系统管理的重要文件类型。
- 管道文件(p):一种用于进程间通信的特殊文件,也称为命名管道FIFO。
- 套接字文件(s):一种用于网络间通信的特殊文件。
- 链接文件(l):用于间接访问另外一个目标文件,相当于Windows快捷方式。
- 字符设备文件(c):字符设备在应用层的访问接口。
- 块设备文件(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(互联网编号分配机构) 的划分:
-
知名端口(0-1023)
-
分配给系统核心服务(如HTTP 80、SSH 22)。
-
需管理员权限才能使用。
-
-
注册端口(1024-49151)
-
分配给用户级应用(如MySQL 3306、Redis 6379)。
-
需向IANA注册以避免冲突。
-
-
动态/私有端口(49152-65535)
-
临时分配给客户端或私有服务(如P2P下载、临时会话)。
-
Service Name and Transport Protocol Port Number Registry
端口号 | 协议 | 服务/功能 | 说明 |
---|---|---|---|
20/21 | TCP | FTP(文件传输协议) | 20端口用于数据传输,21端口用于控制连接 |
22 | TCP | SSH(安全外壳协议) | 加密的远程登录和安全文件传输 |
23 | TCP | Telnet | 明文远程登录(不安全,逐渐被SSH替代) |
25 | TCP | SMTP(简单邮件传输协议) | 邮件服务器之间的邮件发送 |
53 | TCP/UDP | DNS(域名系统) | UDP用于快速查询,TCP用于区域传输或长响应 |
67/68 | UDP | DHCP(动态主机配置协议) | 67端口为服务器,68端口为客户端 |
80 | TCP | HTTP(超文本传输协议) | 网页浏览标准端口 |
110 | TCP | POP3(邮局协议第3版) | 从邮件服务器接收邮件 |
123 | UDP | NTP(网络时间协议) | 系统时间同步 |
143 | TCP | IMAP(互联网邮件访问协议) | 远程管理邮件服务器上的邮件 |
161/162 | UDP | SNMP(简单网络管理协议) | 161用于监控设备,162用于告警通知 |
443 | TCP | HTTPS(安全超文本传输协议) | 加密的网页通信(HTTP over TLS/SSL) |
465 | TCP | SMTPS(安全SMTP) | 加密的邮件传输(SMTP over SSL) |
587 | TCP | SMTP Submission | 邮件客户端提交邮件到服务器的端口(支持加密) |
636 | TCP | LDAPS(安全LDAP) | 加密的目录访问协议 |
993 | TCP | IMAPS(安全IMAP) | 加密的IMAP协议 |
995 | TCP | POP3S(安全POP3) | 加密的POP3协议 |
1433 | TCP | Microsoft SQL Server | 微软数据库默认端口 |
1521 | TCP | Oracle数据库 | Oracle数据库监听端口 |
1723 | TCP | PPTP(点对点隧道协议) | VPN连接协议(安全性较低,逐渐被替代) |
1900 | UDP | UPnP(通用即插即用) | 设备自动发现和配置 |
2082 | TCP | cPanel默认端口 | 网站管理控制台 |
2083 | TCP | cPanel HTTPS | 加密的cPanel访问 |
3306 | TCP | MySQL数据库 | MySQL数据库默认端口 |
3389 | TCP | RDP(远程桌面协议) | Windows远程桌面连接 |
5432 | TCP | PostgreSQL数据库 | PostgreSQL默认端口 |
5900 | TCP | VNC(虚拟网络控制台) | 远程桌面共享(可扩展至5901、5902等) |
6379 | TCP | Redis数据库 | 内存键值数据库 |
8080 | TCP | HTTP备用端口 | 常用于代理服务器或替代HTTP 80端口 |
8443 | TCP | HTTPS备用端口 | 替代HTTPS 443端口(如Tomcat管理界面) |
27017 | TCP | MongoDB数据库 | 默认数据库端口 |
子进程与父进程的继承
- 一个进程复刻一个子进程的时候,会将自身几乎所有的资源复制一份,具体如下:
父子进程的以下属性在创建之初完全一样:
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;
}