Day07 Liunx高级系统设计8-线程

news2024/11/23 21:54:45

概述

进程与线程

进程 : 系统分配资源的基本单位 , 可以简单理解为一个正在进行的程序
线程 : 操作系统调度的最小单位 , 就是一段代码的执行顺序
注意:
1, 一个进程必须要有一个线程 , 该线程被称为主线程
2, 一个进程可以有多个线程 , 除主线程外的其他线程都是子线程
3, 进程被销毁时 , 其中的线程也将被销毁 .
4, 线程是轻量级的进程( LWP light weight process ),在 Linux 环境下线程的本
质仍是进程。
5, 进程所有线程都共享该进程的资源。

线程的特点

Unix 系统中,早期是没有 线程 概念的, 80 年代才引入,借助进程机制实现出了线程的概念。
因此在这类系统中,进程和线程关系密切:
1) 线程是轻量级进程 (light-weightprocess) ,也有 PCB ,创建线程使用的底层函数和进程一样,都是 clone
2) 从内核里看进程和线程是一样的,都有各自不同的 PCB.
3) 进程可以蜕变成线程
4) linux 下,线程最是小的执行单位;进程是最小的分配资源单位
实际上,无论是创建进程的 fork ,还是创建线程的 pthreadcreate ,底层实现都是调用同一个内核函数 clone
        Ø 如果复制对方的地址空间,那么就产出一个 进程
        Ø 如果共享对方的地址空间,就产生一个 线程
Linux 内核是不区分进程和线程的 , 只在用户层面上进行区分。所以,线程所有操作函数pthread* 是库函数,而非系统调用

线程共享与非共享的资源

共享的
1) 文件描述符表
2) 每种信号的处理方式
3) 当前工作目录
4) 用户 ID 和组 ID
5) 内存地址空间 (.text/.data/.bss/heap/ 共享库 )
非共享的
1) 线程 id
2) 处理器现场和栈指针 ( 内核栈 )
3) 独立的栈空间 ( 用户空间栈 )
4) errno 变量
5) 信号屏蔽字
6) 调度优先级

现成的优缺点

优点
提高程序并发性
开销小
数据通信、共享数据方便
缺点
库函数,不稳定
调试、编写困难、 gdb 不支持
对信号支持不好 优点相对突出,缺点均不是硬伤。 Linux 下由于实现方法导致进程、线程差别不是很大。

查看的指定的线程号(LWP)

ps - Lf  pid

pid : 进程号

注意:

由于线程库原本不是系统本身的 , 所以在链接时需要手动链接库文件 ,编译源文件时输入gcc *.c -l pthread

线程相关函数

获取当前线程号

简述

线程号只在它所属的进程环境中有效。
线程号则用 pthread_t 数据类型来表示, Linux 使用无符号长整数表示。
函数:
所需头文件
        #include <pthread.h>
函数
        pthread_t pthread_self(void);
功能:
        获取线程号。
参数:
        无
返回值:
        调用线程的线程 ID
示例:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
int main()
{
    pthread_t pthid = pthread_self();
    printf("pthid=%ld\n",pthid);
    return 0;
}

创建线程

函数

所需头文件
        #include <pthread.h>
函数
        int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
        void *(*start_routine)(void *), void *arg );
参数:
        thread:线程标识符地址。
        attr:线程属性结构体地址,通常设置为 NULL
        start_routine:线程函数的入口地址。
        arg:传给线程函数的参数。
返回值:
        成功:0
        失败:非 0
示例:
#include <stdio.h>
#include <pthread.h>
// 注意线程调用的函数返回值为任意指针类型
void *myfunc01()
{
    printf("线程%ld正在执行\n", pthread_self());
    return NULL;
}
void *myfunc02(void *arg)
{
    printf("线程%ld正在执行,参数为:%s\n", pthread_self(), (char *)arg);
    return NULL;
}
int main(int argc, char const *argv[])
{
    pthread_t p1, p2, p3;
    pthread_create(&p1, NULL, myfunc01, NULL);
    pthread_create(&p2, NULL, myfunc02, "Thread2");
    pthread_create(&p3, NULL, myfunc02, "Thread3");
    getchar();
    return 0;
}

线程的回收

作用
等待线程结束(此函数会阻塞),并回收线程资源,类似进程的 wait() 函数。如果线程已经结束,那么该函数会立即返回。
函数
        #include <pthread.h>
        int pthread_join(pthread_t thread, void **retval);
参数:
        thread:被等待的线程号。
        retval:用来存储线程退出状态的指针的地址 , 即回收的线程调用的函数的返回值
返回值:
        成功:0
        失败:非 0
示例:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *myfunc01(void *argv)
{
    int *time = (int *)argv;
    printf("线程A%ld将于%d秒后销毁\n", pthread_self(), *time);
    sleep(*time);
    return "线程A";
}
void *myfunc02(void *argv)
{
    int *time = (int *)argv;
    printf("线程B%ld将于%d秒后销毁\n", pthread_self(), *time);
    sleep(*time);
    return "线程B";
}
int main(int argc, char const *argv[])
{
    int n01 = 3, n02 = 5;
    pthread_t p1, p2;
    pthread_create(&p1, NULL, myfunc01, &n01);
    pthread_create(&p2, NULL, myfunc02, &n02);
    void *argv01, *argv02;
    pthread_join(p1, &argv01);
    pthread_join(p2, &argv02);
    printf("%s被回收\n", (char *)argv01);
    printf("%s被回收\n", (char *)argv02);
    return 0;
}

线程的分离

作用
使调用线程与当前进程分离,分离后不代表此线程不依赖与当前进程,线程分离的目的
是将线程资源的回收工作交由系统自动来完成,也就是说当被分离的线程结束之后,系
统会自动回收它的资源。所以,此函数不会阻塞。
函数
头文件
        #include <pthread.h>
函数
        void pthread_detach(patread_t thread);
参数:
        thread:线程号
示例:
 
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *fun1(void *arg)
{
    printf("线程1开始执行\n");
    sleep(3);
    return NULL;
}
void *fun2(void *arg)
{
    printf("线程2开始执行\n");
    sleep(1);
    return NULL;
}
int main(int argc, char const *argv[])
{
    pthread_t tid1;
    pthread_t tid2;
    pthread_create(&tid1,NULL,fun1,NULL);
    pthread_create(&tid2,NULL,fun2,NULL);
    pthread_detach(tid1);
    pthread_detach(tid2);
    getchar();
    return 0;
}

案例1:多线程遍历字符串

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
void *fun(void *arg)
{
    printf("len = %ld\n",strlen(arg));
    char *txt = (char *)arg;
    char buf[32] = {0};
    strcpy(buf,txt);
    printf("线程%ld开始执行\n",pthread_self);
    for(int i = 0;i < strlen(buf);i++)
    {
        printf("%c\n",buf[i]);
        sleep(1);
    }
    printf("线程%ld执行结束\n");
}
int main(int argc, char const *argv[])
{
    char buf[32] = {0};
    while(1)
    {
        // memset(buf,0,32);
        printf("请输入要打印的字符\n");
        fgets(buf,32,stdin);
        buf[strlen(buf)-1] = 0;
        if(strcmp(buf,"886")==0)
        {
            break;
        }    
        
        pthread_t t;
        pthread_create(&t,NULL,fun,buf);
    }
    
    return 0;
}

线程的退出

作用
退出当前线程。一个进程中的多个线程是共享该进程的数据段,因此,通常线程退出后
所占用的资源并不会释放。
函数
所需头文件
        #include <pthread.h>
函数
        void pthread_exit(void *retval);
参数:
        retval:存储线程退出状态的指针。
返回值:
        无
示例
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *fun(void *arg)
{
    for(int i = 0;i < 10;i++)
    {
        printf("%d\n",i);
        sleep(1);
        if(i == 3)
        {
            pthread_exit(NULL);
        }
    }
    return NULL;
}
int main(int argc, char const *argv[])
{
    pthread_t tid;
    pthread_create(&tid,NULL,fun,NULL);
    pthread_detach(tid);
    return 0;
}

线程的取消

作用
退出指定线程
注意 :
线程的取消并不是实时的,而有一定的延时。需要等待线程到达某个取消点 ( 检查点)
检测点:类似与游戏的存档,不是实时的,需要到特定的地方才会存档
函数
        #include <pthread.h>
         int pthread_cancel(pthread_t thread);
参数:
        thread :目标线程id
返回值:
        成功:0
        失败:出错编号
示例:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *fun(void *arg)
{
    for(int i = 0;i < 10;i++)
    {
        printf("%d\n",i);
        sleep(1);
        
    }
    return NULL;
}
int main(int argc, char const *argv[])
{
    pthread_t tid;
    pthread_create(&tid,NULL,fun,NULL);
    sleep(3);
    pthread_detach(tid);
    pthread_join(tid,NULL);
    return 0;
}

线程的属性(了解)

概述

Linux 下线程的属性是可以根据实际项目需要,进行设置,之前我们讨论的线程都是采
用线程的默认属性,默认属性已经可以解决绝大多数开发时遇到的问题。
如我们对程序的性能提出更高的要求那么需要设置线程属性,比如可以通过设置线程栈
的大小来降低内存的使用,增加最大线程个数。

线程树形结构体

typedef struct
{
int etachstate ; // 线程的分离状态
int schedpolicy ; // 线程调度策略
struct sched_param schedparam ; // 线程的调度参数
int inheritsched ; // 线程的继承性
int scope ; // 线程的作用域
size_t guardsize ; // 线程栈末尾的警戒缓冲区大小
int stackaddr_set ; // 线程的栈设置
void * stackaddr ; // 线程栈最低地址 , 即线程栈的地址 , 默认是从所
属的进程的栈空间划分
size_t stacksize ; // 线程栈的大小
} pthread_attr_t ;
注意:
1. 线程分离状态
2. 线程栈大小(默认平均分配)
3. 线程栈警戒缓冲区大小(位于栈末尾)
4. 线程栈最低地址
以上属性的属性值不能直接设置,须使用相关函数进行操作,初始化的函数为
pthread_attr_init ,这个函数必须在 pthread_create 函数之前调用。之后须用
pthread_attr_destroy 函数来释放资源。

线程属性相关函数

初始化

作用 :
        初始化线程属性
函数
        int pthread_attr_init(pthread_attr_t *attr);
返回值:
        成功:0
        失败:错误号

销毁

作用
销毁线程属性
函数
int pthread_attr_destroy(pthread_t *attr);
返回值:
        成功:0
        失败:错误号

分离状态

作用 : 设置分离状态
函数
int pthread_attr_setdetachstate(pthread_attr_t *attr,int detachstate);
参数:
        attr:已初始化的线程属性
detachstate:
        分离状态 PTHREAD_CREATE_DETACHED (分离线程)
        PTHREAD_CREATE_JOINABLE (非分离线程)
作用 : 获取分离状态
int pthread_attr_getdetachstate(pthread_attr_t *attr, int
*detachstat);
参数 :
        attr:已初始化的线程属性
        detachstate:
分离状态 PTHREAD_CREATE_DETACHED (分离线程)
PTHREAD_CREATE_JOINABLE (非分离线程)
示例:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *fun(void *arg)
{
    int i = 0;
    while(1)
    {
        printf("%lu正在运行i=%d\n",pthread_self(),i++);
        sleep(1);
    }
}
int main(int argc, char const *argv[])
{
    pthread_attr_t attr;
    pthread_attr_init (&attr);
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);//设置分离属性

    pthread_t tid;
    pthread_create(&tid,NULL,fun,NULL);
    printf("5秒后将结束线程\n");
    sleep(5);
    pthread_cancel(tid);
    sleep(1);
    printf("销毁线程\n");
    pthread_attr_destroy(&attr);
    return 0;
}

线程栈相关

概述:

// 设置栈的地址
int pthread_attr_setstack(pthreadattrt *attr, void *stackaddr, sizet stacksize);
参数:
        attr:指向一个线程属性的指针
        stackaddr:设置的栈的地址
        stacksize:设置的栈的大小
返回值
        成功:0
;失败:错误号
// 得到栈的地址
int pthread_attr_getstack(pthreadattrt *attr, void **stackaddr, sizet*stacksize);
参数:
        attr:指向一个线程属性的指针
        stackaddr:返回获取的栈地址
        stacksize:返回获取的栈大小
成功: 0 ;失败:错误号
// 设置线程所使用的栈空间大小
int pthread_attr_setstacksize(pthreadattrt *attr, sizet stacksize);
参数:
        attr:指向一个线程属性的指针
        stacksize:设置的栈大小
成功: 0 ;失败:错误号
// 得到线程所使用的栈空间大小
int pthread_attr_getstacksize(pthreadattrt*attr, sizet *stacksize);
参数:
        attr:指向一个线程属性的指针
        stacksize:获取线程的栈大小
成功: 0 ;失败:错误号
示例:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <
void *fun(void *arg)
{
    int i = 0;
    while(1)
    {
        printf("%lu线程正在执行i=%d\n",pthread_self(),i++);
        sleep(1);
    }
    return NULL;
}
int main(int argc, char const *argv[])
{
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
    void *stackaddr = calloc(1,128);
    if(stackaddr == NULL)
    {
        perror("null");
        return 0;
    }
    pthread_attr_setstack(&attr,&stackaddr,128);
    pthread_t tid;
    printf("线程在5秒后结束\n");
    pthread_create(&tid,&attr,fun,NULL);
    sleep(5);
    pthread_cancel(tid);
    sleep(1);
    pthread_attr_destroy(&attr);
    free(stackaddr);
    return 0;
}

注意事项

1) 主线程退出其他线程不退出,主线程应调用 pthread_exit
2) 避免僵尸线程
        a) pthread_join
        b) pthread_detach
        c) pthread_create 指定分离属性
join 的线程可能在 join 函数返回前就释放完自己的所有内存资源,所以不应当返回被回收线程栈中的值; 3) malloc mmap 申请的内存可以被其他线程释放
4) 应避免在多线程模型中调用 fork ,除非马上 exec ,子进程中只有调用 fork 的线程存在,其他线程 t 在子进程中均 pthread_exit
5)信号的复杂语义很难和多线程共存,应避免在多线程引入信号机制

案例

火车票售票问题
定义一个记录火车票的剩余数量
4 个窗口同时销售该火车票
代码:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
int ticket = 100;
void *sale(void *name)
{
    char buf[32] = {0};
    strcpy(buf,(char*) name);
    while(ticket > 0)
    {
        ticket--;
        sleep(0.5);
        printf("%s售卖了一张船票,还剩%d张船票\n",buf,ticket);

    }
}
int main(int argc, char const *argv[])
{
    pthread_t tid1,tid2,tid3,tid4;
    pthread_create(&tid1,NULL,sale,"一号窗口");
    pthread_create(&tid2,NULL,sale,"二号窗口");
    pthread_create(&tid3,NULL,sale,"三号窗口");
    pthread_create(&tid3,NULL,sale,"四号窗口");
    pthread_detach(tid1);
    pthread_detach(tid2);
    pthread_detach(tid3);
    pthread_detach(tid4);
    getchar();
    return 0;
}

作业

1, 整理笔记
2, 完成以下情况
        一个线程打印1~52
        一个线程打印a~z
3, 使用代码模拟龟兔赛跑
        乌龟每秒1
        兔子每秒5
        赛程100
        兔子在临近终点睡觉了, 导致乌龟赢了
4, 完成售票案例 , 查看并分析结果 , 说明原因
代码参考上面案例
原因:
是因为卖了第一张票后,其他三个窗口在抢cpu执行权,在第一张票卖出去,提示的信息还未执行的时候,其他三个线程有一个线程有一个将cpu执行权抢了过去,这是票数实际是99,同理以此类推,其他线程将cpu执行权抢了过去,所以将卖了四张票后才第一次打印提示信息,这时实际票数已经是96张,所以,这时提示的信息中将之前的四次卖票纪律一次性打出来了,所以都是剩余96张。总之,这是个异步,在栈中,每个线程都将有各自独立的ticket,都是100张。
解决办法:
是要解决其中异步的问题,所以要解决这一问题,就是要实现同步,让四个线程只对一个ticket操作就可以。
简单的可以用将ticket设置为全局变量

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

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

相关文章

玻色量子袁为出席中国移动第四届科技周量子计算算法与应用分论坛

9月12日&#xff0c;中国移动第四届科技周“量子计算算法与应用”分论坛在北京成功举办&#xff0c;中国移动研究院院长黄宇红发表致辞&#xff0c;中国移动未来研究院院长崔春风全程主持。玻色量子作为光量子计算领域真机测试与场景应用的标杆企业应邀出席&#xff0c;玻色量子…

散点图,何须图,折线图混放在一个echarts

散点图&#xff0c;何须图&#xff0c;折线图混放在一个echarts option {tooltip: {trigger: axis,axisPointer: {type: cross,crossStyle: {color: #999}}},legend: {data:[盒须图1,盒须图2,折线图,散点图]},xAxis: [{type: category,data: [周一,周二,周三,周四,周五,周六…

智能优化算法应用:基于花授粉算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于花授粉算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于花授粉算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.花授粉算法4.实验参数设定5.算法结果6.参考文…

vue3 + ts 防抖指令,节流指令,复制指令

vue3 ts 自定义指令 防抖指令&#xff0c;节流指令&#xff0c;复制指令 本文使用了 element-ui , element-plus 官网 源文件 https://admin.spicyboy.cn/#/directives/debounceDirect 新建 copy.ts 文件 &#xff08;复制指令&#xff09; import type { Directive, Di…

重新思考U-Net在医学超声图像分割中的应用(NU-net)

Rethinking the Unpretentious U-net for Medical Ultrasound Image Segmentation 摘要&#xff1a; 乳腺肿瘤分割是帮助我们描述和定位肿瘤区域的关键步骤之一。然而&#xff0c;乳腺肿瘤形态多变、边界模糊、强度分布相似&#xff0c;给乳腺肿瘤的准确分割带来了挑战。近年…

java系列-LinkedHashMap

1.插入新节点时&#xff0c;会将该节点加到链表尾部 public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>{/*** The head (eldest) of the doubly linked list.*/transient LinkedHashMapEntry<K,V> head;/*** The tail (young…

C语言数据结构-栈和队列

文章目录 1 栈的表示与实践1.1 栈的概念及结构1.2 定义数据结构1.3 初始化与销毁1.4 入栈1.5 出栈1.6 栈顶的数据1.7 栈的个数1.8 栈是否是空1.9 打印栈 2 队列的表示与实现2.1 队列的概念与结构2.2 队列的数据结构定义2.3 队列的初始化与销毁2.4 入队2.5 出队2.6 取队头数据2.…

enumerate()函数讲解+同时获取索引和对应的元素值+实例

enumerate()函数是Python内置函数&#xff0c;用于在遍历可迭代对象&#xff08;如列表、字符串、元组等&#xff09;时&#xff0c;同时获取索引和对应的元素值。 它的主要作用是在循环过程中方便地获取索引和元素。 下面以两个例子来进行介绍理解。 目录 一、例子1 二、例…

打造‘产业大数据综合服务平台’,助力智慧园区建设!

随着大数据、人工智能、云计算、物联网等新一代信息技术的发展与应用&#xff0c;我国各类型园区正在向“智慧园区”转型升级&#xff0c;逐步开启数字化、智能化的运营管理模式。智慧园区的建设不仅需要基础设施的智慧化&#xff0c;更要实现园区规划、运营、管理、服务的智慧…

plf::list原理分析

plf::list是一个比std::list性能要好的另外一种实现&#xff0c;根据作者的性能测试&#xff1a; 293% faster insertion 57% faster erasure 17% faster iteration 77% faster sorting 70% faster reversal 91% faster remove/remove_if 63% faster unique 811% faster clear …

Vue 3 + Tailwind CSS:打造现代化项目的完美组合

Vue 3 Tailwind CSS&#xff1a;打造现代化项目的完美组合 本篇教程将向你介绍如何将 Tailwind CSS 与 Vue 3 项目搭配使用&#xff0c;为你的项目提供现代化的 UI 呈现和开发体验。通过本文的逐步演示和示例代码&#xff0c;你将很快掌握在 Vue 3 中集成和使用 Tailwind CSS…

YOLOv7改进实验:一文了解YOLOv7如何打印FPS指标

💡该教程为改进YOLOv7指南,属于《芒果书》📚系列,包含大量的原创首发改进方式🚀 💡🚀🚀🚀本博客内含改进源代码,按步骤操作运行改进后的代码即可 💡更方便的统计更多实验数据,方便写作 新增YOLOv7打印FPS指标 完善(一键YOLOv7打印FPS指标) 文章目录 完善…

【Tomcat】java.net.BindException “Address already in use: NET_Bind“

问题 17:37 Error running Tomcat 7.0.76: Unable to open debugger port (127.0.0.1:14255): java.net.BindException "Address already in use: NET_Bind"调整 把14255 改成 49963就正常了 附件 netstat -aon|findstr "49963" taskkill -f -pid xxx…

机械中常用的一些术语

目录 一、OEMSOP:SOP编写指南 WI(标准作业指导书):标准作业程序 &#xff08;SOP&#xff09;:SOP和WI的区别&#xff1a;一、PFC、FMEA、PCP、WIPPAP、PSW&#xff1a;APQP&#xff1a;BOM&#xff08;Bill of Material&#xff09;物料清单DV&#xff08;设计验证&#xff09…

C++STL的vector模拟实现

文章目录 前言成员变量成员函数构造函数push_backpop_backinserterase析构函数拷贝构造 前言 成员变量 namespace but {template<class T>class vector{public:typedef T* iterator;private:iterator _start;iterator _finish;iterator _end_of_storage;}; }我们之前实…

《opencv实用探索·十八》Camshift进行目标追踪流程

CamShift&#xff08;Continuously Adaptive Mean Shift&#xff09;是一种用于目标跟踪的方法&#xff0c;它是均值漂移&#xff08;Mean Shift&#xff09;的扩展&#xff0c;支持对目标的旋转跟踪&#xff0c;能够对目标的大小和形状进行自适应调整。 cv::CamShift和cv::me…

【CANopen进阶日记】③ CANopen对象字典工具

【CANopen进阶日记】专栏目录 第一章 CAN协议栈详解 第二章 CANopen协议栈详解 第三章 CANopen对象字典工具 文章目录 【CANopen进阶日记】专栏目录前言一、对象字典简介二、CANopen协议栈框架三、NMT主从四、SDO客户端服务器五、PDO生产者消费者5.1 RPDO5.2 TPDO同步周期5.3 …

python代码200行左右,python100行代码案例

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;python代码200行左右&#xff0c;python100行代码案例&#xff0c;今天让我们一起来看看吧&#xff01; 有用的 Python 单行代码片段&#xff0c;只需一行代码即可解决特定编码问题&#xff01; 在本文中&#xff0c;我…

CTF V8 pwn入门(一)

仍然是因为某些原因&#xff0c;需要学学浏览器pwn 环境 depot_tools建议直接去gitlab里下&#xff0c;github上这个我用魔法都没下下来 下完之后执行 echo export PATH$PATH:"/root/depot_tools" >> ~/.bashrc路径换成自己的就ok了 然后是ninja git clo…

DSP28335之CAN通信易错点总结

一、CAN初始化流程 ①接收发送引脚初始化&#xff1b; ②接收和发送邮箱设置&#xff1b; ③邮箱使能&#xff1b; ④波特率配置&#xff1b; ⑤接收掩码设置&#xff1b; ⑥中断配置。 二、几个重要的知识点 ①影子寄存器 因为ECanbRegs是不可以单独对位操作&#xf…