在做项目的过程中,遇上的问题,记录一下。有一个程序getCode.exe需要开机自启动,这个程序是有一些用户交互的。
一开始的时候,我这边直接使用nssm创建服务,开机自启动这个服务,拉起来getCode.exe,但是发现,getCode.exe开机自启动之后,居然对用户的输入毫无反应;但是如果是电脑开机之后,手动启动getCode.exe程序,就可以正常接收用户输入并作出后续操作。
于是开始从网上找各种信息排查,发现网友提到了会话ID,于是打开任务管理器去确认,发现如果是通过nssm直接创建服务拉起来的getCode.exe进程的会话ID是0,但是如果在开机后手动启动getCode.exe的会话ID是1,而开机后的当前用户的会话ID就是1,经过确认,就是会话ID导致的问题。
于是我们需要再写一个程序setExeSID.exe,这个setExeSID.exe主要负责的就是以指定的会话ID来启动getCode.exe程序,然后将setExeSID.exe通过nssm创建服务开机自启动,当服务开机自启后,拉起来setExeSID.exe,此时setExeSID.exe进程的SID为0,然后setExeSID.exe这个进程再指定以SID为1启动getCode.exe,这样的话,getCode.exe既实现了开机自启动,又实现了开机启动后SID是1(和当前登录的用户的SID)保持一致,可以和用户有输入输出的交互。
简单的说,用户登陆到windows系统之后,不管该用户是本地登陆的,还是远程登陆,系统都会为这个用户分配一个新的会话ID(SID)。也就是说会话与用户的登录是相关连的,没有用户登录就不存在会话。因此,会话的含义是指用户登录之后的一种运行的环境。
打开“任务管理器”–》“查看”–》“选择列”–》“会话ID”前的方框打钩,即可在任务管理器查看到每个进程的会话ID
主要使用到的函数如下:
1 |
|
session 0会话隔离的问题,在这个项目之前,虽然有一点耳闻,但是在一开始排查这个问题的时候,确实没有往这个方向上想,后来也是查了很多资料之后,才把问题的方向转向这个思路。经过这次,可以说,当windows上开发应用程序时,如果需要这个应用程序开机自启动,而且这个程序又在启动后需要有用户交互,那么就需要考虑会话隔离的问题。
关于突破SESSION 0隔离创建进程
https://cloud.tencent.com/developer/article/1424933
这个链接下总结了用户权限设置和进程权限提升,讲的很细致,提权demo也可参考
https://blog.csdn.net/yockie/article/details/17029293
这个链接下,总结了window API
http://yfvb.com/help/win32sdk/index.htm?page=html/_qx5ll.htm
在代码开发过程中,内存泄露问题是一个让人排查起来很头疼的问题,这种bug有时候会非常隐蔽,当代码量很大的时候,有时候直接推理排查会花费大量时间精力,还有可能漏掉,此时可以通过一些小工具来排查代码里的内存泄露问题,linux下,可以通过valgrind工具来检查代码里的内存泄露问题
官网下载地址: https://www.valgrind.org/downloads/current.html
1 | //解压 |
至此,valgrind的安装及环境配置已完成,操作过程如下图:
main.cpp如下
1 |
|
直接编译即可:
1 | g++ main.cpp |
调试指令如下:
1 | valgrind --log-file=./valgrind_report.log --leak-check=full --show-leak-kinds=all --show-reachable=no --track-origins=yes ./a.out |
–log-file
指定报告输出文件
–track-origins=yes
是否显示未定义的变量,在堆、栈中被定义没有被initialised的变量都被定义成origins。默认是关闭这个option的。
–show-leak-kinds=all
这里可以支持的选项有[definite|possible],一般只需要去关注definite(必须关注),possible是可能会存在。
–leak-check=full
当服务器退出时是否收集输出内存泄漏,选项有[no|summary|full]这个地方我们将其设置成全输出,默认将会使用summary方式。
之后就可以查看生成的报告valgrind_report.log,分析是否有内存泄露,及问题定位
本次代码检查如下:
在linux下排查内存泄露问题时,如果项目复杂,代码量大,可以考虑使用valgrind工具来协助排查
做一个系统的总结,之前没有做过详细的整理,这里以一个示例代码先放出来,然后结合这个代码进行一些总结和分析。
http://c.biancheng.net/view/2228.html
http://c.biancheng.net/view/2227.html
统计学生人数和平均分,总分
1 | #include <iostream> |
运行结果:
1 | 小明的年龄是15,成绩是90.6 |
初步分析:
总人数 m_total 和总成绩 m_points 由各个对象累加得到,必须声明为 static 才能共享;
getTotal()、getPoints() 分别用来获取总人数和总成绩,为了访问 static 成员变量,我们将这两个函数也声明为 static。getTotal()、getPoints() 当然也可以声明为普通成员函数,但是它们都只对静态成员进行操作,加上 static 语义更加明确。
静态成员变量、静态成员函数可以通过类来调用(一般都是这样做),也可以通过对象来调用。
之所以使用匿名对象,是因为每次创建对象后只会使用它的 show() 函数,不再进行其他操作。不过使用匿名对象无法回收内存,会导致内存泄露,在中大型程序中不建议使用。
以下详细介绍一下静态成员变量和静态成员函数。
1.对象的内存中包含了成员变量,不同的对象占用不同的内存,这使得不同对象的成员变量相互独立,它们的值不受其他对象的影响。例如有两个相同类型的对象 a、b,它们都有一个成员变量 m_name,那么修改 a.m_name 的值不会影响 b.m_name 的值。
2.可是有时候我们希望在多个对象之间共享数据,对象 a 改变了某份数据后对象 b 可以检测到。共享数据的典型使用场景是计数,如果我们想知道班级中共有多少名学生,就可以设置一份共享的变量,每次创建对象时让该变量加 1。
在C++中,我们可以使用静态成员变量来实现多个对象共享数据的目标。静态成员变量是一种特殊的成员变量,它被关键字static修饰。
static 成员变量属于类,不属于某个具体的对象,即使创建多个对象,也只为 m_total 分配一份内存,所有对象使用的都是这份内存中的数据。当某个对象修改了 m_total,也会影响到其他对象。
3.static 成员变量必须在类声明的外部初始化,具体形式为:
1 | type class::name = value; |
4.静态成员变量在初始化时不能再加 static,但必须要有数据类型。
被 private、protected、public 修饰的静态成员变量都可以用这种方式初始化。
注意:static 成员变量的内存既不是在声明类时分配,也不是在创建对象时分配,而是在(类外)初始化时分配。反过来说,没有在类外初始化的 static 成员变量不能使用。
5.static 成员变量既可以通过对象来访问,也可以通过类来访问。
代码示例如下:
1 | //通过类类访问 static 成员变量 |
注意:static 成员变量不占用对象的内存,而是在所有对象之外开辟内存,即使不创建对象也可以访问。具体来说,static 成员变量和普通的 static 变量类似,都在内存分区中的全局数据区分配内存
总结:
(1) 一个类中可以有一个或多个静态成员变量,所有的对象都共享这些静态成员变量,都可以引用它。
(2) static 成员变量和普通 static 变量一样,都在内存分区中的全局数据区分配内存,到程序结束时才释放。这就意味着,static 成员变量不随对象的创建而分配内存,也不随对象的销毁而释放内存。而普通成员变量在对象创建时分配内存,在对象销毁时释放内存。
(3) 静态成员变量必须初始化,而且只能在类体外进行。
初始化时可以赋初值,也可以不赋值。如果不赋值,那么会被默认初始化为 0。全局数据区的变量都有默认的初始值 0,而动态数据区(堆区、栈区)变量的默认值是不确定的,一般认为是垃圾值。
(4) 静态成员变量既可以通过对象名访问,也可以通过类名访问,但要遵循 private、protected 和 public 关键字的访问权限限制。当通过对象名访问时,对于不同的对象,访问的是同一份内存。
1.在类中,static 除了可以声明静态成员变量,还可以声明静态成员函数。
普通成员函数可以访问所有成员(包括成员变量和成员函数),静态成员函数只能访问静态成员。
2.编译器在编译一个普通成员函数时,会隐式地增加一个形参 this,并把当前对象的地址赋值给 this,所以普通成员函数只能在创建对象后通过对象来调用,因为它需要当前对象的地址。而静态成员函数可以通过类来直接调用,编译器不会为它增加形参 this,它不需要当前对象的地址,所以不管有没有创建对象,都可以调用静态成员函数。
3.普通成员变量占用对象的内存,静态成员函数没有 this 指针,不知道指向哪个对象,无法访问对象的成员变量,也就是说静态成员函数不能访问普通成员变量,只能访问静态成员变量。
4.普通成员函数必须通过对象才能调用,而静态成员函数没有 this 指针,无法在函数体内部访问某个对象,所以不能调用普通成员函数,只能调用静态成员函数。
5.静态成员函数与普通成员函数的根本区别在于:普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
6.在C++中,静态成员函数的主要目的是访问静态成员。
和静态成员变量类似,静态成员函数在声明时要加 static,在定义时不能加 static。静态成员函数可以通过类来调用(一般都是这样做),也可以通过对象来调用。
注意:
静态成员函数可以访问其他类的各种成员函数(包括静态/非静态的成员变量和成员函数)。只在静态成员函数所在的那个类受到各种限制。在这个函数里,调用其他的类的接口都是不受限制的。
对static静态元素做一个系统的学习记录
在c/c++种,static都有两种基本含义,并且这两种基本含义经常是互相冲突的。
(1)在固定的地址上进行存储分配,也就是说对象在一个特殊的静态数据区上创建的,而不是每次函数调用时在堆栈上产生的。这也是静态存储的概念。
(2)对一个特定编译单位来说是局部的。因此,static控制名字的可见性,所以这个名字在这个单元或者类之外是不可见的。这也描述了连接的概念,它决定连接器将看到哪些名字。
通常,在函数体内部定义一个局部变量时,编译器在每次函数调用时使堆栈的指针下移一个适当的位置,为这些局部变量分配内容。如果这个变量有一个初始化表达式,那么每当程序运行到此处,初始化就被执行。
然而,有时想在两次函数调用之间保留一个变量的值,可以通过定义一个全局变量来实现,但这样一来,这个变量就不仅仅只是受到这个函数的控制。
c/c++中都准许在函数体内部定义一个static对象,这个对象存储在程序的静态数据区内,而不是在堆栈中。这个对象只在函数第一次调用时初始化一次,以后它将在两次函数调用之间保持它的值。
如果没有为一个内建类型的静态变量提供一个初始值的话,编译器也会确保在程序开始时它被初始化为0(转化为适当的类型)。
其实对静态对象初始化(与其他对象的初始化一样)可以是任意的常量表达式,常量表达式可以出现常量以及在此之前声明过的变量和函数。
注意:无论什么时候设计一个包含静态变量的函数时,都应记住多线程问题。
(1)静态对象的构造函数
对一般静态变量的规则同样适用于用户自定义的静态对象,而且它同样也必须有初始化操作。但是,零赋值只对内建类型有效,用户自定义类型必须用构造函数来初始化。因此,如果在定义一个静态对象时没有指定构造函数参数,这个类就必须要有默认构造函数。
示例代码如下:
运行结果如下:
(2)静态对象的析构函数
静态对象的析构函数(包括静态存储的所有对象,不仅仅是上例的局部静态对象)在程序从main()中退出时,或者标准的c库函数exit()被调用时才被调用。
多数情况下,main()函数的结尾也是调用了exit()来结束程序的。这意味着在析构函数内部使用exit()是非常危险的,因为这样导致了无穷的递归调用。
同普通对象的销毁一样,静态对象的销毁顺序也是按与初始化相反的顺序进行的。当然只有那些创建了的对象才会被销毁。开发工具会记录对象初始化的顺序和那些已被创建的对象。
全局对象总是在mian()执行之前被创建,在退出main()时被销毁。
在C++中,全局静态对象的构造函数是在main()之前调用的,所以现在有了一个在进入main()函数之前执行一段代码的简单的、可移植的方法,并且可以在退出main()之后用析构函数执行代码。在C中,如果想要做到这一点,就显得非常繁琐,我们将不得不熟悉编译器开发商的汇编语言的开始代码。
如果一个包含局部静态对象的函数从未被调用过,那么这个对象的构造函数也就不会执行,这样也就不会执行析构函数。
示例代码如下:
运行结果如下:
《Thing in C++》
树莓派本身安装了64位系统的情况下,需要配置32位程序的运行环境,首先安装依赖库,操作步骤如下,以下操作都要在root用户下进行
git clone git://github.com/raspberrypi/tools.git
将arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/路径下的lib库都拷贝到/usr/local/lib32路径下,这个路径可以自己创建,自定义路径名,专门用来存放底层32位依赖库(如libstdc++.so.6)
其实最重要的,就是从官方提供的交叉编译工具链,把32位库给获取到,然后放到树莓派的自定义路径下,之后自己编译的32位执行程序/库,都要指定链接这个路径的基础库,否则会报错(找不到依赖库)
本身64位的树莓派系统是不带32位基础库的,所以必须从官方的交叉编译工具链里获取(目前,无法通过apt-get直接获取到所有的32位依赖库)
在终端进行如下操作即可:
sudo passwd root
然后根据提示输入root用户的密码
再重复输入一次刚刚设置的密码
切换root用户操作如下即可:
su -
输入设置的root用户的密码
操作如下即可:
sudo vim /etc/ssh/sshd-config
修改 PermitRootLogin yes
然后保存修改
然后执行sudo systemctl restart ssh
再通过ss -tnl查看是否开启成功即可
在需要无线网络连接的情况下,配置eth0的静态ip如下:
sudo vim /etc/network/interfaces
然后再文件里增加以下内容
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 192.168.0.1 //IP地址
netmask 255.255.255.0 //掩码
然后保存
但是此时,如果不设置一下wlan0,那么会发现虽然静态ip设置成功了,但是树莓派却无法联网了
所以还要在interfaces文件里追加以下内容
auto wlan0
iface wlan0 inet dhcp
wpa_conf /etc/wpa_supplicant/wpa_supplicant.conf
然后保存
退出文件
重启树莓派即可
https://www.raspberrypi.org/software/raspberry-pi-desktop/
通过官网,下载raspberry镜像64位系统iso文件之后,可以安装在虚拟机vbox/vmware里
通过github下载最新的官方树莓派交叉编译工具链
git clone git://github.com/raspberrypi/tools.git
将arm-bcm2708文件夹拷贝到/opt/arm-bcm2708下(自定义路径即可)
将上面到交叉编译工具链的路径配置到~/.bashrc文件
1 | sudo vim ~/.bashrc |
输入以下指令,如果有打印一些版本信息,那么说明交叉编译环境配置正确
arm-linux-gnueabihf-gcc -v
如图:
如果报错提醒如下:
可以按照以下的解决方案尝试一下:
然后重新输入arm-linux-gnueabihf-gcc -v即可发现打印版本信息
demo略,编译的指令如下:
需要注意的是,该可运行文件不能在PC机上运行,只能在树莓派arm板子上运行
到此为止,虚拟机上的树莓派arm的交叉编译工具链搭建完成,这个官方的交叉编译工具链还是很靠谱的
下载镜像参考以下网址
https://www.jianshu.com/p/1a65cb0b8f58
下载安装交叉编译链参考以下网址
https://www.cnblogs.com/zfyouxi/p/3831769.html
tag:
缺失模块。
1、请确保node版本大于6.2
2、在博客根目录(注意不是yilia根目录)执行以下命令:
npm i hexo-generator-json-content --save
3、在根目录_config.yml里添加配置:
jsonContent: meta: false pages: false posts: title: true date: true path: true text: false raw: false content: false slug: false updated: false comments: false link: false permalink: false excerpt: false categories: false tags: true
2023-08-22
#基础技能C++
2023-08-22
#基础技能C++
2023-08-22
#基础技能C++
2023-08-22
#基础技能C++
2023-06-10
#经典案例分析
2023-06-10
#经典案例分析
2023-06-10
2023-06-10
#基础技能C++
2023-05-27
#拾光
2023-05-27
#基础技能C++
2023-05-27
#基础技能C++
2023-03-09
#CNC#多线程
2023-03-09
#基础技能C++#CPU#CNC#多线程
2023-03-09
#基础技能C++#CPU#CNC#多线程
2023-03-09
#基础技能C++#CPU#CNC
2023-03-09
#CNC
2023-03-02
#生产力工具
2023-03-01
#生产力工具
2023-03-01
#基础技能C++
2023-02-28
#基础技能#生产力工具
2023-02-27
#生产力工具
2023-02-27
#生产力工具
2023-02-23
#ps教程
2023-02-04
#opencv#边缘检测
2023-02-04
#opencv#边缘检测
2023-02-04
#opencv#边缘检测
2023-01-27
#opencv#边缘检测
2023-01-27
#opencv#边缘检测
2023-01-27
#opencv#边缘检测
2023-01-27
#opencv#边缘检测
2023-01-26
#opencv
2023-01-12
#opencv
2023-01-05
#生产力工具
2023-01-05
#生产力工具
2023-01-04
#生产力工具
2022-12-11
#生产力工具
2022-12-11
#opencv
2022-12-11
#opencv#边缘检测
2022-12-04
#opencv
2022-12-04
#生产力工具
2022-12-04
#生产力工具
2022-11-29
#阅读
2022-11-29
#opencv
2022-11-16
#生产力工具
2022-11-15
#常见报错问题
2022-11-15
#常见报错问题
2022-11-13
#生产力工具#kali
2022-11-13
#生产力工具#kali
2022-11-12
#生产力工具#kali
2022-11-12
#生产力工具#kali
2022-11-12
#个人小工具
2022-11-12
#常见报错问题#第三方库
2022-11-10
2022-11-10
2022-11-10
#第三方库#加密
2022-11-10
#生产力工具
2022-11-10
#基础技能C++#框架学习
2022-11-07
#生产力工具
2022-11-07
#生产力工具
2022-11-06
#生产力工具#常用命令windows
2022-11-06
#第三方库#交叉编译
2022-11-06
#第三方库
2022-11-05
#个人小工具
2022-11-04
#常见报错问题
2022-11-03
#常见报错问题
2022-11-03
#生产力工具
2022-11-03
#生产力工具#常用命令windows
2022-11-02
#第三方库#交叉编译
2022-11-02
#opencv#第三方库
2021-12-12
#hexo博客搭建
2021-05-15
#个人小工具
2021-03-18
#基础技能C++
2021-03-08
#生产力工具
2020-11-24
#基础技能C++
2020-11-22
#基础技能C++
2020-11-15
#交叉编译
2020-11-15
#交叉编译
2020-11-15
#交叉编译
2020-11-15
#常用基础配置
2020-11-15
#常用基础配置#交叉编译
2020-10-29
#基础技能
2020-10-29
#基础技能C++
2020-06-29
#常用基础配置
2020-05-23
#设计模式
2020-05-16
#生产力工具
2020-04-25
#第三方库#交叉编译
2020-04-25
#常用基础配置
2020-04-11
#第三方库#交叉编译
2020-03-29
#常用基础配置
2020-03-29
#交叉编译
2020-03-01
#基础技能
2020-01-12
#设计模式
2019-12-26
#基础技能
2019-12-15
#常用命令linux
2019-12-15
#常见报错问题
2019-11-07
#生产力工具
2019-11-07
#生产力工具
2019-09-22
#基础技能C++
2019-09-09
#基础技能C++#第三方库
2019-09-05
#常见报错问题#通信协议
2019-09-05
#常用命令windows
2019-09-01
#基础技能C++#第三方库
2019-08-31
#常用命令windows
2019-08-31
#常见报错问题
2019-08-30
#常用命令linux
2019-08-29
#第三方库
2019-08-18
#基础技能
2019-08-13
#hexo博客搭建
2019-08-13
#常用基础配置
2019-08-13
#第三方库
2019-08-10
#常见报错问题
2019-08-08
#hexo博客搭建
2019-05-15
#通信协议
2019-02-27
#生产力工具
2018-03-06
#基础技能C++
2018-03-05
#基础技能C++
2018-03-05
#基础技能C++
2018-02-25
#基础技能C++
2018-02-22
#基础技能C++
2017-03-07
#基础技能C++
2017-03-07
#基础技能C++
2017-03-02
#星座学
2017-02-12
#基础技能C++
2013-03-06
#基础技能C++
2013-03-06
#基础技能C++
2013-03-05
#基础技能C++
2013-03-05
#基础技能C++