0%

起因

因为,我同事它对git不熟,所以,特意来找我来帮帮他解决问题,所以,我就去了,他要下载一个仓库代码,但是仓库的默认主分支不是他想要的,他想要的是一个xxxx&yyyy&zzzz的分支,所以,我然后直接

1
2
git branch -a
git checkout xxxx&yyyy&zzzz

问题来了,我确实看到了remote的 xxxx&yyyy&zzzz分支,但是,无法执行,它告诉我指令不存在。所以,一时间有点懵逼,我还特意使用

1
git clone -b xxxx&yyyy&zzzz https://.....

去下载特定分支,发现也失败了。我都懵了的时候,我尝试切换其他分支,成了,我就知道,是命令行输了呢。呜呜呜,竟然让GUI赢了一把。大家应该发现了问题了吧!没错就是&。

github管理 | 我要成为嵌入式高手(csmznbo.top)这是我上一期关于git的入门操作哦,有兴趣的可以看一下。

& | && ||

&:一旦shell指令识别到&就会把后面那些当成指令看待,所以,可以做一个测试,新建一个 ls&cat README.md 的文件夹。对这个文件夹执行ls,就会发现,它执行了cat操作了,注意力此时cat是占据当前进程的,所以,如果后面没有+文件的话,你的中断就会堵塞卡死。注意了,如果一个子线程,直接使用system没有+&,那么这个子线程就会堵死,但是不会影响其他线程哈哈哈。你懂我什么意思的,这确实是一个搞人的手段,尤其可以选择在md后面也加一个&。这样就是两个后台工作了。偷偷摸摸后台搞事情哦。

|:管道,就是前面数据给后面,注意力,文件夹和问价不能用|来命名,所以管道操作实现不了哦

&&:命令行的与操作,经典断路

||:命令行或操作,经典短路

解决&的方法就是+ “”双引号框起来就好了。

git仓库版本问题

我们可以把git粗略分为用户和系统两个部分,那些文件,代码,数据都是用户添加,属于用户层面,可以随意修改。但是.git文件夹里面就不行了,里面保存所有历史记录和操作,不能随意修改,如果只是本地无所谓的,但是要和云端同步的话,就麻烦了。

如果本地仓库没有远程仓库的所有历史记录,那么push就是失败,就是说提交的版本一定要大于而包含远程版本才能提交。所以,别人抢先push了,你记得得git pull之后才能提交,就是包含别人的版本。

如果要想修改提交的云端历史记录就没办法了。因为你修改之后,你是想要进行git pull之后才可以push的,此时就会强行修正过来,导致对历史提交修改全部被纠正。因为此时两个版本对不上,提交是错误的。

真的有不得不修改的数据,那就只能强行推送覆盖云端,但是这样会把别人的记录也可能覆盖了,这是一件非常不提倡的事情。而且非常危险。

git 版本和 git tag

这个git 版本就是一个时刻的仓库的切片,一些重要的版本级别的仓库,这些是可以选择进行版本设置,这样就快速保存可用的版本,也方便回溯,分别下载,方便使用等诸多好处。

tag就是标签,这个版本有啥,这个版本怎么样,都是可以打上标签的。

pr的评论和保护分支机制

我们可以在设置添加对分支的保护,添加管理员也不能违反,这样就可以杜绝直接本地就提交覆盖分支,而是只能通过pr来实现合并,在添加,评审人评论和同意才能合并方案,就可以有效防止恶意分支合并和覆盖。

git查看提交代码量和次数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#提交代码量
git log --author="csm@gmail.com" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -
#added lines: 7572, removed lines: 675, total lines: 6897

#定期代码量检测
git log --author="csm@gmail.com" --since==2024-03-01 --until=2024-04-01 --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -
#added lines: 2456, removed lines: 609, total lines: 1847
#从2024-03-01到2024-04-01 作者csm@gmail.com的提交量,后面是awk语法

#提交具体详情
git log --author="csm@gmail.com" --pretty=format:"%H|%an|%ae|%s" | while read line; do
echo "$line"
done
# 对每一行做相应处理,例如提取commit hash、作者名、邮箱和提交信息
# %H 表示commit hash, %an 表示作者名, %ae 表示作者电子邮件,%s 表示提交消息摘要。

#提交次数
git log --author=csm@gmail.com --since="2024-03-01" --no-merges | grep -e 'commit [a-zA-Z0-9]*' | wc -l
#21
git冲突

总有些家伙啊,意见不合,就导致了冲突,常见了的文件缺失,内存冲突,版本问题带来的冲突,此时就要进行合并才行。这些都是需要手动解决的,此时就去到冲突的文件中手动删除冲突部分,它会贴心的告诉用<<<<<<和>>>>>>来告诉那些是当前的分支,那些是要合并的分支。跟冲突的人协商之后,进行选择性的删除和添加就解决冲突了,保存提交推送就好了。

总结

git是一个好东西呢,确实分布式系统,一开始上手都是有点懵逼的,充分理解git的工作原理和运行逻辑,处理方法,真的香啊,linus是真的伟人,真的牛人,linux系统+git系统,真的是令人敬佩。

起因

今天刷到一个视频。我们学习linux的时候就学到了一句话”一切皆是文件“,仔细看了一下他的看法和视频。确实很严谨。”一切皆是文件“是源于unix的,linux继承下来了,但是,不能这样理解。linus曾经解释过,一切皆字节流。我们实际使用一般都是使用api,所以,在api层面就该理解为,一切皆是文件描述符。

文件描述符
文件类型
字符设备
块设备
网络设备
总线设备

别好奇,搞驱动不只是字符,块,网络。还有平台和总线设备驱动。比如usb,spi,iic。这些你说他们是什么设备?都不一定的。这个是开用途的,usb可以网口啊,usb可以u盘啊,usb可以键盘鼠标啊!所以,这些平台设备和总线设备是另一种驱动,这些是给上面三种驱动一个总线驱动和驱动方法,这些一般都是厂商写好的,usb只能通过调用linux内核中的api来实现usb驱动的编写,因为,高度封装了,想得到usb配置符,只能靠内核哦!

总结

起因

我当时学这个文件io的时候,也是懵懵懂懂的,就知道,open read write close lseek fflush那些,其实也没明白是怎么回事。

数据流是指在计算机系统中,程序与文件之间进行数据交换的过程。当你在程序中执行文件操作,如读取、写入、修改等,这些操作都涉及到数据在程序和文件之间的传输,这个过程就是文件IO的数据流。

文件管理系统

自从学习了flash,我之前就好奇了,flash是可以作为一个管理空间,但是,对于一个文件来说,如果把数据存储在flash中,那么,如何进行精确的修改呢?这个flash肯定是要先刷新才能使用的啊,为什么好像使用这些文件io的时候,可以那么顺利呢?其实,如果是追加的话,其实还是可以理解的,就是通过系统去链接起来,调用系统的文件io就可以实现文件完整使用和读取,所以,哪怕文件不是连续地址,也可以实现文件追加。读就更容易了,写呢?如果在原有基础上写呢?所以,我就像这种情况肯定是要刷新flash的区块来实现的。恰好,我想起来了数据流的概念。

数据流

使用文件io的时候,就会到打开这个文件,同时把所有数据通过数据流的形式导出来,我们对着这个数据流进行修改。这个数据流,可以认为就是系统把这一段空间都读取出来了,我们对着这个字符串进行操作而已。

在Flash存储中,当你需要修改一个文件时,操作系统和文件系统会采取一些策略来确保不会影响到其他文件:

  1. 写入时复制(Copy-on-Write):在修改文件之前,系统会先将原始文件复制到另一个块,然后在新块上进行修改。这样,原始文件保持不变,新的修改内容存储在另一个块上。这种方法可以避免在修改过程中影响到其他文件。
  2. 垃圾回收(Garbage Collection):随着文件的修改和删除,Flash存储上可能会产生一些不再使用的空间。这些空间被称为垃圾。垃圾回收机制会定期运行,将这些垃圾空间整理并合并,以便于未来的文件存储和修改。
  3. 磨损平衡(Wear Leveling):Flash存储具有有限的写入次数,频繁写入同一块区域会导致磨损加速。为了避免这个问题,Flash存储控制器会实现磨损平衡策略,将写入操作分散到不同的块,以延长Flash存储的使用寿命。
  4. 日志结构(Log-Structured)文件系统:一些文件系统(如F2FS、Btrfs等)采用日志结构设计,将文件的元数据和实际数据分开存储。这种设计有助于减少对Flash存储的磨损,并提高文件系统的性能。

所以,我们不用担心回写导致flash错误的问题,这些都是通过文件管理系统区处理。

总结

数据流是一个非常迷糊的概念,只有使用open才会打开,使用close和fflush才会刷新保存,所以,如果在close和fflush之前电脑停电、蓝屏、死掉了等。哦吼,数据流就随着RAM一起没啦!此时就没了通过文件管理系统回写这个操作了。这个时候追悔莫及了,一无所有啦。

IPC

IPC是什么呢?网络摄像头?我们在linux中,所以只能使用进程间通信啦

IPC原理
IPC操作
IPC内核过程
IPC特性
总结

设备树

这个不是linus设计的,是linus借鉴别人的。这个是Open Firmware的设计,常见于powerPC等架构中,linus觉得很好,就把他引进到了linux中了,使用它的目的,也是为了统一管理和分配硬件,减少代码量。所以,遇到哪个of开头的文件和api,因为它来自Open Firmware哦。

使用设备树的时候,先看看uboot是否支持设备树,不支持就找打uboot的一些头文件增加#define CONFIG_OF_LIBFDT /* Device Tress support */试试

再看看菜单中是否开启了设备树功能,在cpu.c中引用了头文件 。比如kernel/arch/arm/mach-s5p6818/cpu.c。

#include <linux/of.h>

#include <linux/of_address.h>

#include <linux/of_fdt.h>

#include <linux/of_platform.h>

在cpu_init_machine添加“加载设备树函数of_platform_populate”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const struct of_device_id of_default_bus_match_table[] = {
{ .compatible = "simple-bus", },
#ifdef CONFIG_ARM_AMBA
{ .compatible = "arm,amba-bus", },
#endif /* CONFIG_ARM_AMBA */
{} /* Empty terminated list */
};

static void __init cpu_init_machine(void)
{
/* set shutdown */
pm_power_off = nxp_cpu_shutdown;
arm_pm_restart = nxp_cpu_reset;
/*
* register platform device
*/
nxp_cpu_devs_register();
nxp_board_devs_register();

//加载设备树
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}

在比较新的linux内核中,设备树dts已经取代了传统的machine board device description,dts

在内核中以各种device node的形式存在,而这些device node对于大部分的内核驱动模块

platform_driver来说,最终需要有对应的platform device来与他匹配才可以完成一次device和

driver的probe过程。

所有有必要将dts中需要加载为device的device node转为platform device,而这个过程是交给

of_platform_populate来完成的(dts相关的device node tree是在main.c中的start_kernel-

>setup_arch->unflatten_device_tree来加载dtb并解析)。

1
2
3
4
5
6
7
8
9
10
11
12
void __init unflatten_device_tree(void)

{
__unflatten_device_tree(initial_boot_params,

&allnodes,early_init_dt_alloc_memory_arch);

/* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */

of_alias_scan(early_init_dt_alloc_memory_arch);

}

MACHINE_START 改为DT_MACHINE_START,并在其中增加设备树匹配信息。

设备树文件

DTS 、DTC、DTB、

DTS是设备树源码,就是描绘了硬件的配置;如地址、大小、种类、外设配置等

DTC是编译设备树的编译工具,这个需要install下单编译器

1
2
3
4
5
6
7
8
9
10
11
#dtc 工具安装:
sudo apt-get install device-tree-compiler

#编译设备树
dtc -I dts -O dtb -o xxx.dtb xxx.dts

#编译设备树插件
dtc -I dts -O dtb -o xxx.dtbo xxx.dts

#设备树反汇编
dtc -I dtb -O dts -o xxx.dts xxx.dtb

DTB是可执行文件,经过上面的编译之后,可以被编译器识别的文件。可以加到特定位置同内核一起运行,也可以加到内核所在设备树目录中一起编译到内核之中。

设备树语法

设备树该怎么写呢?首先,一个树肯定就是有主干,这个主干,不是我们这些小卡拉米写的,所以,顶多是在芯片原厂的基础上增加一些自己的设备。

枝叶这个该怎么写呢?首先,你得让编译器和主干识别到我这个枝叶吧,这就像一个叶子,一定有叶柄才能和主干连接,而我们修改不过是叶子的形状和功能,所以,开头一定要有一个和主干一模一样的开头。这个就是叶柄了。如下面这种结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
* http://www.samsung.com
** This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/

/dts-v1/;

/ {
model = "csm6818 based on Samsung s5p6818"; /*这一段就是叶柄了,有了它才能正常工作*/
compatible = "csm,s5p6818";
chosen {
compatible = "gec,chosen";
bootargs = "lcd=at070tn92 tp=gslx680-linux root=/dev/mmcblk0p2 rootfstype=ext4 rw ";
};

/*这下面此时就是叶子了,可以随意定义*/
myled{
compatible = "csm,myled"; /*这个compatible不要改,也算是开头和索引号,后面的随便改都行*/
led-num = <4>; /*这个led-num是自定义的,可以修改哦。比如led-nums*/
led-names ="led-d7","led-d8","led-d9","led-d10"; /*字符串*/
led-gpios = <141 81 72 71>; /*数据*/
default-state = "off"; /*字符串 表示状态,当然用数据也行*/
};

};

语法开始:这个就简单的说一下设备树语法,只有使用的时候再去仔细看一下需要的。

Linux设备树学习笔记(一、设备树语法规范)_reg = <0x00 0x80000000 0x00 0x60000000>;-CSDN博客

设备树5个使用方法
1.将dtb文件拷贝到根文件系统(推荐,利于调试)

/* 从根文件系统拷贝dtb文件,更方便,更利于调试 */

setenv bootcmd ext4load mmc 2:2 0x42000000 gec6818.dtb ;ext4load mmc 2:1 0x48000000

uImage ;bootm 0x48000000 - 0x42000000

saveenv

2.挂载开发板内核镜像分区,将dtb拷贝到该分区(安全性高,用户不能随意修改或删除)

mount -t ext4 /dev/mmcblk0p1 /mnt

cp gec6818.dtb /mnt

umount /mnt

/* 拷贝到内核镜像的分区 */

setenv bootcmd ext4load mmc 2:1 0x42000000 gec6818.dtb ;ext4load mmc 2:1 0x48000000

uImage ;bootm 0x48000000 - 0x42000000

saveenv

3.使用网络,配置uboot环境变量。

setenv bootcmd tftp 0x42000000 gec6818.dtb ; ext4load mmc 2:1 0x48000000 uImage ;

bootm 0x48000000 - 0x42000000

4.不使用网络,可以使用串口下载dtb文件到内存地址0x42000000。

loady 0x42000000

setenv bootcmd ext4load mmc 2:1 0x48000000 uImage ; bootm 0x48000000 - 0x42000000

saveenv

5.将dtb文件编译到boot.img,重新烧录内核。

在ubuntu中

将文件拷贝到一个/target/product/6818/boot的目录之下

./mk -k

在uboot中

setenv bootcmd ext4load mmc 2:1 0x42000000 gec6818.dtb ;ext4load mmc 2:1 0x48000000

uImage ;bootm 0x48000000 - 0x42000000

驱动使用设备树节点

驱动想要使用设备树,只能使用平台设备方法的,此时平台设备platform_driver中的dirver结构体中的of_match_table这个就要指向所满足需求的

1
2
3
4
5
6
7
8
9
10
11
12
13
static const struct of_device_id of_led_match[]={
{.compatible="gec,myled",}
}

static struct platform_driver led_plat_driver = {
.probe = myled_probe,
.remove = __devexit_p(myled_remove),
.driver = {
.name = "myled",
.onwer = THIS_MODULE,
.of_match_table = of_led_match, //指针指向
}
}

此时,这个驱动就知道,该找到compatible=”gec,myled”的要求的设备树节点,读取这个节点上面的数据(引脚号和介绍),此时驱动就知道有这个led设备了,就可以控制对应的GPIO引脚的高低电平。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct platform_device *pdev;//已经对应上的平台设备

/* init */

char Buff[30]={0};
//设置节点指向
struct device_node * pdevnode = pdev->dev.of_node;

//混杂设备注册

//申请gpio设备

//调用读取节点字符串函数
of_property_read_string(pdevnode,"目标",&Buff);
of_property_read_string_index(pdevnode,"目标",&Buff); //出现 "xxx","yyy","zzz"的时候
//Buff就是目标字符串了 目标可以是名字介绍,这个是设备树中自定义的

//调用读取节点数据函数
of_property_read_u8_array(pdevnode,"目标",Buff,Numsize);
//Buff就是目标数据了,Numsize就是个数
/*还有of_property_read_u16_array;of_property_read_u32_array;
of_property_read_u64_array*/

//得到了所需要的数据,就可以写对应的驱动了
//gpio操作

/* init结束 */
总结

设备树,就是省去了,对硬件的描述的复杂和多种写法,统一写法,节约空间和利于管理。

平台

我们经常可以看到短视频平台,直播平台,购物平台,那么此时,这个平台的作用是什么?就是不论你是干什么的,你是卖什么的,我都给你一个店铺,你把你的服务和你的物品端上来,这是不是就是平台啊?没错,那对于我们消费者和管理者来说,我们是不是不需要就不需要去分类那么形形色色的货物了,我们还可以细分和归纳合并,这样可以分成平台中卖水果平台,这样是不是就把苹果香蕉梨那些都汇聚到一起了,而且他们用的都是一样的店铺。

驱动
平台设备
平台设备应用
总结

J-link全家桶

什么是j-link全家桶呢?我们下载好j-link的驱动之后,一般还会顺便下载j-link的sdk,这些sdk就是j-link全家桶啦这是干嘛的?说白了,就是为了方便用户下载和调试代码的。只是如果没用过,一开始会用点懵逼,啊!那么多sdk我怎么用啊?我拿头玩?所以,很多时候就没管它,其实,他是一个非常牛逼的东西哦!下面就大致说一下这个sdk的作用。

J-flash

这个是j-link其中的一种,一般可以看到它有啥j-flash-spi、j-flash反正都是一个目的就是下载代码。我们这里使用j-flash举例子,打开之后,就会显示一个操作界面,这个界面可以选择打开下载文件,可以选择烧录的mcu型号和初始地址,所以,这样还可以依靠bootloader跳转到其他地方执行我下载的这个函数。选择swd下载,连接,清空flash,任何下载即可。

J-mem

看名字大概就知道是干嘛得了吧,没错就是修改和查看内存的。J-Mem 是一个 GUI 应用程序,用于在目标运行时显示和修改目标系统的 RAM 和 SFR(特殊功能寄存器)。打开之后也是一个类似于j-flash的页面,可以选择读取特定地址的内容,查看和保持甚至是修改内容,这个还是不错的,可以用来后续的一些序列号的烧录呢,设置周期性刷新显示的内存内容。逆向的话这应该有可能吧。

看名字又知道是什么意思了,命令行操作,这个就有点像debug操作了,可以通过命令来进行pc指针和运行过程的跳转,hold内核、单步、全速、设置断点、查看内核和外设寄存器、读取flash代码等等,方便大家拥有最高的权限查看在运行中的MCU情况,查找非IDE仿真情况下,MCU运行异常的原因。

Jlink commander使用方法(附指令大全)-CSDN博客

可以参考这个博客哦。

GNU 项目调试器 (GDB) 是一个免费提供的开源调试器,根据 GPL 条款分发。 它可以在命令行模式下使用,这个原本是linux环境下的debug调试器,现在用了这个gdb,就可以调用和操作了,但是这个东西真的不如keil自身的debug,不够如果使用的其他ide装备了GBD,那没办法了,只能靠这个使用了,毕竟keil界面确实老了。

J-Scope

这个就是一个图形显示,可视化可在目标运行时实时分析和可视化微控制器上的数据。就是说,项目通过特定的函数来实现发送任何这个应用接受之后就会显示在UI上面,可以就是一个示波器的作用。具体怎么使用呢?找到Scope的安装位置,找到sample中就可以看到很多没有解压的包了,还包括下面的rtt的包,把它解压了,把c和h文件复制到项目中。进行一些配置和处理,在项目中使用这些c文件中的api就可以进行向这个应用发送波形啦。其实本质就是调用api,配置好对应的宏,J-Scope进行捕捉显示罢了。

工具 | 教你使用Jlink+JScope显示波形_j-scopev7.66a怎么打印正弦波-CSDN博客

可以参考这个博客哦。

RTT全称是Real Time Transmit(实时传输)是Segger公司推出的调试手段之一。它是一种用于嵌入式中与用户进行交互的技术。这个就是一个互通手段而已,因为要知道swd是串行调试接口,本来就是可以双向通信的,所以还是和上面一样的操作,进行一些ch添加到项目,配置config属性,调用api进行读取和发送就实现了。其实直接使用打印也是一个好办法呢!不过这个方法省了一个串口还是不错的哦。

详解J-Link RTT打印 - 知乎 (zhihu.com)

可以参考这个博客哦。

结语

下面这个是j-link官方文档的地址,有什么疑惑或者歧义,我i们都应该以官方文档为标准,以实际开发中的现象为依据。其实功能不止那么点,只是说了一下常用的,更多功能可以到官网去查看使用哦。官方有api调用实例,可以参考分析哦。

UM08001 J-Link / J-Trace User Guide - SEGGER Wiki

这个是一个很好用的工具,但是这个有和没有都不会对项目开发有什么致命影响,就是开发效率的作用,一个好的工程师,肯定会借助多个工具。正所谓,君子生非异也,善假于物也。

起因

​ 我最近确实在备战软考,其中发现许多的编程思路。本来就打算随便学学应付考试的,但是今天,学习rust的时候,学到一个面向函数编程,这让我一时间有了兴趣,仔细想想,如果可以系统级别学习一些编程思路,确实是可以增加我的眼见和对事物的理解,也方便我以为开创和使用更好的思路去编程,增加效率,所以还是写一篇来系统总结一下吧。

面向过程编程

面向过程是一种以事件为中心的编程思想,编程的时候把解决问题的步骤分析出来,然后用函数把这些步骤实现,在一步一步的具体步骤中再按顺序调用函数。我们都是有一个目标,然后不断设计多个函数,然后把一个一个过程组装起来,这就是面向过程编程。面向过程编程强调将程序功能分解成一系列可重用和有序执行的过程或函数。这种范式是从更早的结构化编程演变而来,侧重于明确的步骤和过程的顺序。

特点:
1.函数和过程:程序被分解成一组函数或过程,每个过程完成特定的任务。函数可以调用其他函数,实现复杂逻辑的分层和复用。
2.数据与逻辑分离:数据通常被定义为全局结构,而逻辑则在函数中处理。函数根据需要操作这些数据。
3.控制流:程序的执行流程通过结构化控制语句(如if条件语句、循环语句等)来管理。这种控制结构帮助管理复杂逻辑的执行顺序。
4.模块化:面向过程编程鼓励将程序分解成模块或单元,每个单元具有明确的功能。这些单元可以独立开发和测试,然后组合成完整的程序。
常见就是c语言。shell编程等

面向过程编程的优点:
1.简单性:面向过程的方法由于其直接性和透明性,通常更易于理解和实现,特别是对于简单的问题。
2.性能:过程调用通常比面向对象编程中的方法调用更快,因为它们操作的数据结构较少,且内存使用更直接。
3.复用性:通过函数,可以复用代码,减少重复代码的编写。

面向过程编程的缺点:
1.可扩展性问题:对于大型系统,面向过程编程可能导致代码难以管理和维护。随着系统复杂度的增加,修改程序可能会变得更加困难。
2.数据安全:由于数据通常是全局访问的,这可能会引起数据访问的问题,尤其是在多线程环境中。
3.代码重复:在大型项目中,可能会发现相似的代码块在不同的地方重复,增加了维护的负担。

应用:
面向过程编程适用于相对简单的、过程驱动的任务,如脚本编写、小型项目、或者在性能要求较高且逻辑较为直接的应用中。它在嵌入式系统、操作系统的开发和其他需要高性能、低资源消耗的环境中也非常普遍。

面向对象编程

这个就是模拟现实生活中的种种事物,每一个都可以看做一个对象,而每个对象都有自己的属性和行为,对象与对象之间通过方法来交互。面向对象是一种以“对象”为中心的编程思想,把要解决的问题分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个对象在整个解决问题的步骤中的属性和行为。好处就是可以多次复用,而且封装和使用方便。

特点:
1.封装:将数据(属性)和操作这些数据的代码(方法)捆绑在一起形成对象。这样做的主要优点是隐藏内部细节,只对
外提供有限的接口进行交互。
2.继承:允许新创建的对象继承现有对象的属性和方法。这促进了代码的重用,并可以建立一个层次化的对象模型。
3.多态性:允许不同的对象对同一消息作出响应。例如,父类有一个方法,通过不同的子类可以有不同的实现,但调用接
口(方法名)保持一致。
4.抽象:允许程序员使用类的实例创建具体的对象,并且使用接口定义非具体的操作。这样可以操作具有某些共同特征的对
象,而不需要考虑对象的具体类型。

优点:
1.提高软件开发效率:通过继承和重用已有的代码,可以减少重复代码的编写。
2.提高软件可维护性:封装使得代码更容易被理解,也更容易被修改,不会影响到无关的系统部分。
3.增强软件的灵活性:多态性允许系统更容易地扩展,可以在不改变现有系统的情况下引入新的对象类型。

缺点:
1.性能问题:面向对象系统可能会因为抽象和动态特性而牺牲一些性能。
2.过度设计:错误的或过度的使用面向对象设计可能导致系统复杂度过高,难以理解和维护。
3.学习曲线:对于初学者来说,理解所有OOP概念及其正确应用可能相对困难。

应用:面向对象编程由于其模块化和直观性,成为了现代软件工程中的一个基石。尽管有一些挑战和限制,但它在处理复杂
系统设计和团队协作方面的优势使其成为企业和大型项目的首选编程范式。

常见就是cpp python java rust那些的编程思路,其实也可以使用c语言的结构体里面使用函数指针,也可以实现面向对
象的存在,看linux的驱动开发就知道了,很多都是有操作集。

函数式编程

函数式编程(Functional Programming, FP)是一种编程范式,它将计算视为数学函数的评估,并避免使用程序状态以及可变对象。与面向对象编程侧重于对象(数据及其相关行为)不同,函数式编程强调的是无副作用的函数应用。

特点:
1.不可变性:在函数式编程中,变量一旦被赋值后就不会改变。任何修改都会产生新的数据结构而不是改变已有的。
2.函数是一等公民:函数可以作为参数传递,可以作为返回值,也可以被赋值给变量,这意味着函数的使用方式与其他数据类型无异。
3.纯函数:纯函数的输出只依赖于输入,并且不产生外部可观察的副作用(如修改全局变量或进行输入/输出操作)。
4.递归:由于函数式编程通常不使用循环(for 或 while),递归是实现循环逻辑的一种重要手段。
5.高阶函数:这些是接受其他函数作为参数或将函数作为结果返回的函数。

应用:
函数式编程语言通常提供一系列便利的功能来支持这些概念,如高阶函数、匿名函数(lambda 表达式)、惰性计算和模式匹配等。

许多现代编程语言,如 JavaScript、Python、Ruby、rust

面向数据结构

Jackson Structured Programming(JSP),这是一种系统的软件开发方法,主要用于结构化编程。Jackson Structured Programming由Michael A. Jackson在1970年代开发,用于设计高质量的结构化程序。JSP特别强调数据结构在软件设计中的重要性,并提供了一套方案来分析数据和根据这些数据的结构来设计程序。

特点:
1.数据驱动:在JSP中,设计过程从对数据结构的分析开始。这包括数据的组织、时序和处理方式。数据结构的理解直接影响程序结构的设计。
2.结构图:使用结构图(也称为Jackson图)来可视化数据和程序结构。这些图帮助设计者清晰地理解和规划程序的逻辑流程。
3.模块化设计:JSP鼓励将大的程序分解为小的、可管理的模块。每个模块都处理数据结构的一部分,并可以被单独开发和测试。
4.同步与变换:JSP中重要的概念是程序结构应该映射(或同步)到数据结构。如果数据结构发生变化,相应的程序结构也需要调整。

应用:广泛应用于数据密集型的应用领域,如金融服务、电信和企业数据处理等。
它特别适用于处理复杂的数据流和大规模数据集。
JSP是一种比较传统的方法,随着面向对象和其他现代编程范式的兴起,它的使用频率有所下降。
然而,对于一些特定的应用场景,特别是那些依赖于复杂数据处理的领域,JSP依然是一个有效的工具。

原型化编程

原型化编程(Prototype-based programming)是一种编程范式,它不依赖于类的概念,而是通过克隆现有的对象来创建新对象。这种范式是面向对象编程的一种变体,它消除了类和实例之间的区别,取而代之的是原型对象的概念。

特点:
1.无类(Classless):在原型编程中,没有类的概念。所有对象都是通过复制现有对象(称为原型)来创建的。
2.对象的克隆:新对象是通过复制一个原型对象生成的。这个过程可以包括对原型对象的属性和方法的复制。
3.动态性:对象可以在运行时动态地添加或修改属性和方法。这种灵活性是原型编程的一大优势,允许动态调整系统的行为。
4.直接的继承机制:一个对象可以作为其他对象的原型,允许属性和方法的直接继承。这与基于类的编程中通过类继承来实现的继承机制不同。

应用:
原型编程常用于那些需要高度灵活性和动态性的应用中,例如快速原型开发、动态环境(如Web开发)、游戏开发等领域。由于其在对象创建和继承方面的灵活性,它可以用来快速试验和迭代开发新的功能和对象模型。原型化编程提供了一种与基于类的编程截然不同的思考和实现面向对象编程的方法,通过实际操作和应用可以进一步理解和掌握它的概念和技术细节。

JavaScript:是最著名的支持原型编程的语言。在JavaScript中,几乎所有的对象都是从其他对象克隆得来的,并且可以随时修改对象的结构。
Self:是原型编程的先驱之一,它完全基于原型的概念,放弃了传统的类和实例的区分。
Lua:虽然是一种多范式的脚本语言,但它也支持原型继承。
Io:这是一种小巧的脚本语言,同样采用原型编程范式。

泛型编程

泛型编程是一种编程范式,主要在编程语言中用来实现算法和数据结构的通用性。它允许程序员编写代码以操作任意数据类型,从而使得代码更加模块化、可重用,并减少重复。

特点:
1.类型抽象:泛型编程允许开发者定义操作任意类型(Type-agnostic)的函数和数据结构,而不需要在编写时指定具体的数据类型。
2.代码复用:通过使用泛型,可以创建可应用于多种数据类型的单一实现,从而提高代码的复用率。
3.类型安全:泛型提供了一种类型安全的方式来处理不同的数据类型,可以在编译时捕获类型错误,减少运行时错误。
4.性能优化:泛型编程可以在编译时解析类型,这意味着无需运行时的类型检查,可以提高程序的执行效率。

应用:泛型编程非常适用于需要处理多种数据类型但又要保持代码简洁和安全的场景。
1.集合库:如列表、映射、集等数据结构,它们可以存储任何类型的元素。
2.算法库:可以操作各种类型的数据结构,如排序、搜索等。
3.工具和框架:在开发框架和库时使用泛型可以极大地增强其灵活性和可用性。

C++:通过模板机制支持泛型。模板允许在类和函数级别上操作泛型类型,非常灵活但有时可能导致编译时间较长。
Java:在Java中,泛型是通过使用类型参数来实现的,例如 List。Java的泛型在编译时进行类型擦除,以确保向后兼容性。
C#:提供了强类型的泛型支持,不仅在集合类库中广泛使用,还能够用于自定义的数据结构和函数。
Python:泛型在Python中通常是通过鸭子类型(Duck Typing)实现的,不过最近的版本通过类型提示(Type Hints)和模块如typing,提供了更明确的泛型支持。
Rust:提供了非常强大的泛型系统,允许高度的类型安全和性能优化,通过特性(Traits)和生命周期标注进一步增强了其表达能力

事件驱动编程

事件驱动编程是一种编程范式,它强调的是在软件系统中以事件为核心来设计和处理程序流程。在事件驱动的系统中,程序的执行流程主要是由外部事件(如用户操作、系统消息或其他条件触发的事件)来决定。这种模式特别适合于处理异步或非连续交互的应用程序,例如用户界面、网络服务和任何需要响应外部活动的场景。

特点:
1.基于事件的逻辑:程序的主要逻辑围绕事件的发生和处理构建。事件可以是用户的点击、系统的通知或是时间的推移。
2.非阻塞设计:在等待事件的同时,程序可以继续执行其他任务,这是通过非阻塞操作或多线程实现的。
3.回调函数:事件处理通常通过回调函数实现,即一个事件触发时,相应的处理函数被调用。
4.解耦:事件驱动编程通常可以降低程序组件之间的耦合度,因为组件不需要知道其他组件的具体实现,只需关注事件的发布和订阅。
5.可扩展性:事件驱动的架构使得增加新的事件处理器或修改现有逻辑变得相对简单,有助于应对不断变化的需求。

应用:事件驱动编程适用于多种场景,尤其是那些需要快速响应外部事件的应用
1.用户界面:图形用户界面(GUI)应用程序广泛采用事件驱动模式来响应用户交互。
2.网络应用:服务器需要处理大量并发请求,例如HTTP请求,事件驱动模型能够有效地管理这些并发操作。
3.游戏开发:游戏程序需要响应用户的输入和内部事件,如碰撞检测和时间触发器。
4.实时系统:需要快速响应外部信号的系统,如交易系统、实时监控和自动化控制系统。
5.系统和驱动开发:进行注册和注销的的设计思路

JavaScript:在Web开发中,JavaScript是实现客户端事件驱动编程的主要语言,广泛用于处理用户界面事件如点击、滚动、键盘输入等。
Node.js:利用JavaScript的异步特性,Node.js 是一个服务器端平台,它使用事件驱动的非阻塞I/O模型,非常适合于处理大量并发连接。
C#:在.NET框架中,C# 支持事件和委托(Delegates),使得编写响应系统事件或自定义事件的程序变得简洁明了。
Java:使用监听器(Listeners)和事件对象来处理事件,常见于GUI应用程序(如Swing库)和Android开发。
cpp和c也可以利用这种思想,比如注册函数指针等。linux底层的设计和开发等

面向切面编程

面向切面编程(Aspect-Oriented Programming,简称AOP)是一种编程范式,旨在通过将横切关注点(cross-cutting concerns)从业务主体逻辑中分离出来,增强模块化。这种方法特别适用于处理那些与主要业务逻辑正交但在多处应用中重复出现的问题,如日志记录、事务管理、权限检查、异常处理等。

特点:
1.切面(Aspect):一个模块化的组件,封装了一个跨多个点的关注点(横切关注点)。
2.连接点(Join point):程序执行过程中的某个特定点,比如方法的调用或特定的字段被访问。
3.通知(Advice):在连接点上执行的动作。通知定义了在目标方法执行之前、之后或周围执行的代码。
4.切入点(Pointcut):匹配一个或多个连接点的表达式。它告诉AOP框架在哪里应用通知(即在哪些连接点执行某个通知)。
5.目标对象(Target Object):被一个或多个切面所通知的对象。
6.织入(Weaving):是将切面与其他应用类型或对象链接起来,可以在编译时、加载时或运行时进行。12.3.4.5.6..

应用:面向切面编程适用于处理各种跨应用程序多个部分的问题
1.日志记录:自动记录方法的调用,这在调试和监控中非常有用。
2.事务管理:在数据库操作前后自动开始和提交事务。
3.安全性:在方法执行前检查权限,确保只有具有适当权限的用户才能执行操作。
4.异常处理:为方法提供统一的异常处理逻辑,避免代码重复。
5.性能监控:自动记录方法的执行时间,用于性能分析。

AspectJ:是Java语言中最知名的AOP实现,提供了语言扩展来支持AOP。
Spring AOP:是Spring框架的一部分,提供了AOP支持,但它不像AspectJ那样强大,主要支持基于代理的运行时织入。

元编程

元编程是一种编程技术,它涉及编写可以操纵、生成或变形代码的程序。简单来说,元编程是“编写代码的代码”。这种方法允许开发者编写更灵活、更智能的程序,通过动态地创建或修改代码,或者在编译时或运行时改变程序的行为。

特点:
1.代码生成:编写能够生成其他代码的程序。这种方式可以用于自动化重复的编程任务,提高开发效率和减少错误。
2.反射:程序在运行时访问、检测和修改其自身结构的能力,如类、方法和属性等。这使得程序可以根据自身的状态或行为动态地做出决策。
3.宏(Macro):在编译时扩展的代码片段。宏可以在代码编译之前进行代码的插入或修改,使得可以在编译器层面改变代码的行为。
4.模板元编程:尤其在C++中,利用模板来生成编译时计算的代码。模板元编程可以用于创建编译时决定的高效代码。

优势:
1.灵活性:程序可以根据运行时的数据和条件动态地改变其行为。
2.代码重用和抽象:通过生成代码,可以避免编写重复的代码,提高抽象层级。
3.性能优化:在编译时计算复杂的表达式或决策,运行时性能得到提升。

挑战:
1.可读性和维护性:高度的动态性可能使代码难以理解和维护。
2.复杂性:错误可能难以调试,尤其是当代码在编译时或运行时生成和修改时。
3.性能开销:运行时的元编程可能引入性能开销,尤其是在使用大量反射或动态代码生成时。
元编程是一种强大的工具,适用于需要高度抽象和灵活性的场景,但它也需要谨慎使用,以避免过度复杂化代码和降低程序的可维护性。

C++:提供了强大的模板功能,允许进行高度复杂的编译时计算和类型操作。
Python:通过装饰器、元类(metaclass)和其他反射机制,支持运行时的元编程。
Lisp:其宏系统允许开发者写出可以在编译时展开和转换为其他Lisp代码的程序,非常强大和灵活。
Ruby:提供了开放类和元编程能力,允许在运行时动态地添加或改变对象的方法和属性。
JavaScript:通过原型链和高阶函数,允许在运行时修改和扩展对象和类的行为。

面向响应编程

面向响应编程(Reactive Programming)是一种编程范式,关注于异步数据流和变化的传播。这种编程模式允许程序在数据变化时自动传递这些变化,从而响应地更新相关的处理逻辑。面向响应编程特别适用于需要处理实时数据、高并发请求、大规模分布式系统以及响应用户界面的变化。

特点:
1.数据流和变化传播:在响应式编程中,数据流被视为一系列随时间推移的事件。这些数据流可以被创建、组合、转换或消费。
2.异步和非阻塞:响应式编程鼓励使用异步和非阻塞的操作,这有助于提高应用的可伸缩性和响应性。
3.观察者模式:响应式编程经常使用观察者模式,其中数据流的订阅者会在观察到数据变化时自动接收通知并做出响应。
4.函数式编程技术:响应式编程常常与函数式编程技术结合使用,利用函数式的无副作用和数据不可变性原则来处理数据流。

优点:
1.提高性能:通过非阻塞操作和异步处理,可以更高效地利用系统资源,特别是在IO密集型操作中。
2.易于处理复杂的动态系统:响应式编程可以简化动态和事件驱动系统的处理,如用户界面响应、实时数据处理等。
3.提高错误处理能力:响应式编程支持在数据流中进行错误处理,可以集中控制错误并进行适当的反应。

挑战:
1.学习曲线:响应式编程引入了许多新的概念和抽象,对于新手来说可能较难掌握。
2.调试困难:由于异步和非阻塞的特性,调试响应式编程中的问题可能比传统模式更复杂。
3.资源管理:管理异步操作和数据流需要精心设计,以避免资源泄露和性能问题。

应用场景:
1.用户界面开发:如JavaScript的Web应用,响应式编程可以用于管理DOM事件和状态变化。
2.实时数据处理:如金融市场的实时报价系统,响应式编程可以用于高效处理和传输大量动态数据。
3.网络应用:例如在Spring WebFlux中,使用响应式编程来处理和优化Web请求的处理。

常用的响应式编程工具和库:
RxJava/RxJS/Rx.NET:一系列的库,支持多种编程语言,用于实现响应式编程。
Reactor:Java的响应式编程库,常用于Spring框架。
Akka:用于构建并行、分布式和容错系统的工具集,支持Scala和Java语言

并发编程

并发编程是指在同一时间内处理多个任务的编程方法。在现代计算环境中,这通常涉及到同时执行的线程或进程,以提高程序的性能和响应性。并发编程对于充分利用多核处理器以及处理多任务操作如网络请求、数据库操作和用户界面管理等非常重要。

特点:
1.线程和进程:
进程:操作系统分配资源的基本单位,拥有独立的内存空间。
线程:操作系统调度执行的基本单位,多个线程可以共享同一个进程的资源。线程比进程更轻量,切换成本更低。
2.任务并行性:同时执行多个任务,每个任务可以是一段代码的执行流。
3.数据并行性:将数据分割,同时在多个核心或处理器上进行处理。
4.同步与异步:同步操作需要等待任务完成才能继续,而异步操作可以在等待任务完成的同时继续执行其他任务。
5.死锁、活锁和饥饿:
死锁:两个或更多的执行线程互相等待对方持有的资源,导致无法继续执行。
活锁:线程不断重试一个总是失败的操作,没有停滞,但也无法前进。
饥饿:一个或多个线程无法获得必需的资源,因而无法执行。
6.线程安全:代码在多线程环境下能够正确执行,不会因为线程调度或时间顺序的不同而产生错误。

并发编程的技术和工具:
1.锁(例如互斥锁、读写锁):用于控制对共享资源的访问,保证一次只有一个线程可以使用资源。
2.信号量:限制对资源访问的线程数量,用于实现资源的公平分配。
3.原子操作:不可被线程调度机制中断的操作,用以保证其完整性,常用于更新共享状态。
4.线程局部存储:为每个线程提供独立的变量副本,以避免共享。
5.并发集合:如Java中的ConcurrentHashMap,提供线程安全的数据结构。
6.协程:轻量级的线程,如在Python、Go和Kotlin中实现,提供更高效的任务调度。

应用场景:
1.Web服务器:同时处理多个网络请求。
2.数据库系统:处理多个查询和事务。
3.实时系统:如游戏服务器或交易系统,需要快速响应外部事件。
4.科学计算和数据分析:处理大规模数据集,利用多核性能优化计算。

常用的并发编程语言:
Java:提供了广泛的并发编程工具,包括java.util.concurrent库。
C++:支持多线程库,如C++11以后标准中的线程支持。
Python:通过线程和asyncio库支持并发,虽然受全局解释器锁(GIL)影响。
Go:内建支持并发,通过Goroutines和Channels实现。
Rust:提供数据竞争安全保证,支持无锁并发。

网络编程

网络编程是计算机编程的一个分支,它涉及编写可以在网络中进行通信的软件。这种通信可以在本地网络(如局域网)或跨越广阔的网络(如互联网)。网络编程使得不同计算机上的程序能够互相发送和接收数据,从而执行分布式计算和信息共享。

特点:
1.套接字(Sockets):
套接字是网络通信的基本构建块,它提供了程序间的通信端点。套接字可以是面向连接的(通常是TCP套接字),确保数据正确、完整地传 输;也可以是无连接的(通常是UDP套接字),传输速度快,但不保证数据的完整性或顺序。
协议:
2.TCP/IP(传输控制协议/互联网协议)是最常用的网络通信协议。TCP负责确保数据包的正确顺序和完整性,而IP处理数据包的路由。
HTTP(超文本传输协议)是用于从Web服务器传输网页的协议。
FTP(文件传输协议)、SMTP(简单邮件传输协议)等其他协议支持文件传输和电子邮件发送等功能。
3.IP地址和端口号:
IP地址用于标识网络中的设备,而端口号用于标识设备上的特定应用程序。在网络编程中,一个套接字通过IP地址和端口号的组合来标识通 信的端点。
4.客户端-服务器模型:
在这个模型中,服务器程序在网络中的某台机器上运行,等待和处理来自客户端的请求。客户端程序生成请求发送到服务器,然后接收服务器的响应。
5.非阻塞和异步编程:
在处理网络请求时,非阻塞和异步编程技术可以帮助程序继续执行其他任务,而不是等待网络操作完成,从而提高应用程序的性能和响应性。

常用的网络编程工具和库:
Berkeley套接字(BSD Sockets):几乎所有操作系统都支持的一种编程接口,用于访问底层网络服务。
Java网络编程:Java提供了java.net包,用于实现包括套接字通信在内的网络应用。
Python的socket模块:Python标准库中的socket模块简化了网络通信的实现。
.NET Framework:C#和其他.NET语言使用System.Net和System.Net.Sockets命名空间来进行网络编程。
Node.js:使用JavaScript进行网络编程,特别是异步网络应用。

应用场景:
Web应用开发:服务器和客户端(浏览器)之间的交互。
远程服务:如远程数据库访问、云服务和在线游戏服务器。
物联网(IoT):设备通过网络相互通信,进行数据收集和远程控制。
分布式计算:通过网络分布式处理大规模数据处理任务。

结语

​ 选择不同侧重方案是有利于整体的维护和开发的,世界上不是只有面向过程和面向对象,这些都只是一些好的编程思路和编程规范,没人强制你这样编程,你甚至写汇编都没人说你。但是一个好的思路,一个正确的切入点带来的收益是巨大的,它带来的思想和理解,可以帮助你克服更大的困难。比如说侧重于多任务,那么就是使用rtos或者并发编程的那些思路去设计和开发。因为此时底层那些软件都是已经写好的了,此时想要解决的是多个任务的运行设计,所以,此时要转换编程思路才能事半功倍哦。