进程相关内容
一、父子进程共享内容
相同处
全局变量,.data, .text、堆栈,环境变量,用户id工作目录。
重点:文件描述符,mmap建立的映射区。
不同处
进程ID、fork返回值、各自父进程、进程运行时间、定时器,未决信号集。
子进程复制了父进程的用户空间,遵循读时共享,写时复制原则。
相同处
全局变量,.data, .text、堆栈,环境变量,用户id工作目录。
重点:文件描述符,mmap建立的映射区。
不同处
进程ID、fork返回值、各自父进程、进程运行时间、定时器,未决信号集。
子进程复制了父进程的用户空间,遵循读时共享,写时复制原则。
reserve方法用来给vector预留空间,预留的空间只会改变capacity的大小,不会改变size大小。resize方法表示重新调整数组大小,capacity和size都会改变。
使用reserve后,不能直接使用下标来增加元素,虽然内存是已经分配了直接使用不会报错,但是直接通过下标来复制会导致其他参数得不到更新(如size),会导致意想不到的错误。如以下代码:
int i;
vector<int> v;
v.reserve(10);
cout << "cap: " << v.capacity() << ", size: " << v.size() << endl;
for (i = 0; i < 10; i++) {
v[i] = i;
}
cout << "cap: " << v.capacity() << ", size: " << v.size() << endl;
输出:
cap: 10, size: 0
cap: 10, size: 0
通过下标给数组中的每个元素复制,实际上本身数组的长度并没有得到增长,一旦再执行push_back就会导致前面的数据被覆盖。正确的方式是使用push_back或者insert方法插入元素。
github pages是github提供的静态文件托管服务,支持部署仓库内的静态页面文件,特别适合hexo
、jekyll
以及gitbook
等应用部署网站使用。Github Pages有以下几个优点:
其实国内像gitee.com
和coding.net
也都是提供Pages服务的,但是经过几年的使用感受来说,这两家相对于github还略有不足。主要体现在:
用户名.gitee.com/仓库名/
来访问,HTTPS功能更是没有(因为太挫了所以gitee基本不用)。现在是支持了自定义域名,但是要开通会员才能自定义域名,每年99(直接放弃)。对比来看,github还算比较良心的,这也是为什么会选择github来托管的原因。但是github pages也有功能是不足的地方:
pages的设置在setting
页面中:
点击后,一直往下拖就可以看到pages设置:
pages目前只支持对master分支或者master的docs目录添加静态页面托管,要想托管页面,必须把文件放到master主目录或者docs目录下。而对于大多数应用(如gitbook)来说,主目录一般都是源码文件,因此建议把生成的静态文件放到docs目录下。当代码库中存在docs
目录时,第二个选项会自动亮起。
点击后就可以使用username.gitee.io/projectname
来访问静态页面了。
默认情况下,部署完成后,访问的URL为username.gitee.io/projectname
,github支持自定义域名来重定向这个页面。
配置方法:在Custom domain
栏目中输入自定义的域名,点击Save
即可。
我这里绑定的域名是html.book.maqian.io
:
配置好后,页面会提示:
Your site is ready to be published at http://html.book.maqian.io/
现在要做的就是把域名添加指向到github pages的服务地址,有两种方法:
username.github.io
。可以参考Quick start: Setting up a custom domain和Custom domain redirects for GitHub Pages sites。正常情况下建议使用CNAME解析来指向github,因为域名都有套CDN,访问速度优于指向IP地址。
更多相关信息可参考Using a custom domain with GitHub Pages。
添加CNAME解析
CNAME解析在DNS服务商处添加,因为打算把*.book.maqian.io
都作为自定义域名,因此添加了以下CNAME解析:
加好后,确认本机可以解析:
C:\Users\maqian>nslookup html.book.maqian.io
服务器: public1.alidns.com
Address: 223.5.5.5
非权威应答:
名称: maqianplus.github.io
Addresses: 185.199.111.153
185.199.109.153
185.199.108.153
185.199.110.153
Aliases: html.book.maqian.io
然后访问网站,部署的页面就出来了:
github pages支持添加https,会自动申请Let's encrypt
的证书,勾选上Enforce HTTPS
选项就可以了:
选框无法被勾选的时候,先清空Custom domain里面的内容,点击Save,然后再勾选上Enforce HTTPS,填入自定义域名。
看到以下信息的时候,说明https证书已经在申请过程中了,耐心等待一段时间:
申请完成之后,勾选上选框:
再次访问就是https的了:
package main
import (
"fmt"
)
func main() {
defer_call()
}
func defer_call() {
defer func() { fmt.Println("打印前") }()
defer func() { fmt.Println("打印中") }()
defer func() { fmt.Println("打印后") }()
panic("触发异常")
}
TCP与UDP基本区别:
为什么UDP有时比TCP更有优势:
UDP以其简单、传输快的优势,在越来越多场景下取代了TCP,如实时游戏。
netstat是控制台命令,是一个监控TCP/IP网络的非常有用的工具,它可以显示路由表、实际的网络连接以及每一个网络接口设备的状态信息。Netstat用于显示与IP、TCP、UDP和ICMP协议相关的统计数据,一般用于检验本机各端口的网络连接情况。
进程是表示资源分配的基本单位,又是调度运行的基本单位。例如,用户运行自己的程序,系统就创建一个进程,并为它分配资源,包括各种表格、内存空间、磁盘空间、I/O设备等。然后,把该进程放人进程的就绪队列。进程调度程序选中它,为它分配CPU以及其它有关资源,该进程才真正运行。所以,进程是系统中的并发执行的单位。
引入线程的目的是简化线程间的通信,以小的开销来提高进程的并发程度。有时称轻量级进程。 进程中的一个运行实体,是一个CPU调度单位, 资源的拥有者还是进程或称任务。事实上,引入线程主要是为了提高系统的执行效率,减少处理机的空转时间和调度切换(保护现场信息)的时间,以及便于系统管理。
多态是C++中的重要内容,多态的意思是可以使用父类指针指向子类,并通过这个指针调用子类的函数。
多态可以分为两种形式:
一般我们讨论的都是运行时多态,运行时多态的几个基本条件为:
多态形成的原理就是vptr
指针和vtable
虚函数表,当类中有虚函数时,编译器会自动生成虚函数表vtable,这个表属于这个类,所有类实例共享。同时还成一个vptr
指针指向这个虚函数表,执行调用的时候,先通过vptr指针找到自己的虚函数表,然后执行表中的函数。
多态形成的必要条件是虚函数和继承,当两者其中之一都不存在的时候,编译器不会形成多态,因此也不会生成vptr
指针。只有两者同时存在的时候,编译器才会生成vptr
指针。
以下代码通过类的大小来证明了vptr
指针的存在:
#include <iostream>
using namespace std;
class A {
public:
A() {};
~A() {};
void print() { cout << "AAAAAAAAAA" << endl; };
};
class B : public A {
public:
B() {};
~B() {};
void print() { cout << "BBBBBBBBBB" << endl; };
};
int main() {
cout << "sizeof(A): " << sizeof(A) << endl;
cout << "sizeof(B): " << sizeof(B) << endl;
return 0;
}
C++对空类会分配一个字节的内存,此时的输出A和B的大小都是1:
sizeof(A): 1
sizeof(B): 1
将A中的print()
函数设置为虚函数后:
virtual void print() { cout << "AAAAAAAAAA" << endl; };
两个结构体的大小会变成8,因为添加虚函数后,类中会生成vptr
指针,64位系统指针是四个字节,所以A和B的大小就变成了8。
在创建一个含有虚函数的类时,系统会为每个类生成虚函数表,存放了当前对象所有的函数列表,而vptr
指针就指向这个表的地址。子类继承父类后,也会生成一个属于自己的虚函数表和vptr
指针,如果子类有重写父类的虚函数,虚函数表中的函数地址就是自己类中的成员函数。执行子类调用时,先通过vptr指针定位到对应的虚函数表,然后执行对应的函数。
以上是一个图形示例,类B
继承了类A
,类B重写了f1
和f2
函数,它的函数表中f1
和f2
都指向自己的成员函数。而f3
并未继承,所以指向父类。因此执行子类函数时,f1
和f2
都是用的自己的成员函数。
关于静态联编和动态联编
说到多态肯定免不了要提到动态联编,动态联编就是指程序在运行时才能决定的运行片段,例如if
和switch
代码段,它们只有在运行时才能知道下一步是什么,走哪个代码代码段。多态也是如此,运行到了虚函数的时候才能决定执行哪个函数。而静态联编就是指程序在编译阶段就能决定的事情,就像main
函数,编译后就固定从它开始执行。
子类中vptr指针的值的绑定顺序:
通过以下代码和GDB调试器可以验证这个观点:
#include<iostream>
using namespace std;
class A {
public:
A() {
};
~A() {};
virtual void print() { cout << "AAAAAAAAAA" << endl; };
};
class B : public A {
public:
B() {
};
~B() {};
void print() { cout << "BBBBBBBBBB" << endl; };
};
int main() {
A a;
B b;
return 0;
}
带调试模式编译:
g++ main.cpp -g -o app -std=c++11
使用GDB调试执行,先在父类构造函数中添加断点:
子类构造函数中也添加断点:
输入run
执行,执行到第一个断点,是正在构造a对象的时候,此时打印出A的vptr指针地址:
继续往下执行,下一个断点依旧是在类A的构造函数处,此时正在构造对象b,因为B继承了A,所以先执行A的构造函数。此时打印出b的vptr指针地址,可以看到b对象的vptr指针和a对象中的vptr指针地址一样:
再继续执行,走到类B的构造函数,此时对象b的vptr指针就被修改成了类B自己的虚函数表地址了:
过程图示:
第一步,执行父类构造函数时,指向父类的虚函数表。
第二步,执行子类自己的构造函数时,再指向子类的虚函数表。
安装linux双系统后,只要进入linux系统,windows的系统时间就会错误。
解决方案:
//更新时间
sudo apt-get install ntpdate
sudo ntpdate ntp.sjtu.edu.cn
//将时间更新到硬件上面
sudo hwclock --localtime --systohc
大四上学期的时候因为毕业设计接触到了IC卡破解,当时是花了一个星期的时间破解了学校了水卡、洗衣卡等,毕业后没有再玩过了。
前几天搬家没有门禁卡,每次都是在楼下苦等没有办法。不得已之下又又拿出工具玩了玩。这里记录下流程,以备后用!