您的位置:首页 > 其它

知识点整理

2018-08-05 14:56 337 查看

第一篇博客,没有主题,主要是平时学习过程中碰到的一些问题,做个记录,也培养自己写博客的习惯,好记性不如烂笔头。

1、微软在VS中的int型是分配4个字节,在64位系统上面的char*型是8个字节(到底是几字节不是由CPU决定的,而是由模式决定的,64位CPU上面的指针类型可能是4个字节,比如win7,而在win8或者win10上面,就是8字节),所以如果用int型存储指针,要注意是否会发生截断。

2、虚拟内存技术:虚拟内存是利用硬盘的部分空间当做RAM来用的技术。可以缓解内存不足的问题。一般来说,如果硬盘超过一个,那么就不要将虚拟内存放在C盘。系统盘不能太满的原因就是因为如果虚拟内存放在系统盘,那么一旦系统盘被占用太多,就会导致虚拟内存太小。

百度百科链接:

https://baike.baidu.com/item/%E8%99%9A%E6%8B%9F%E5%86%85%E5%AD%98/101812?fr=aladdin

3、逻辑地址:程序经过编译后,每个目标模块都是从0号单元开始编址,称为该目标模块的相对地址或者逻辑地址。不同进程可以有相同的逻辑地址,因为这些相同的逻辑地址可以映射到主存的不同位置。也就是虚拟地址,是为了解决内存可以被随意访问的问题,每个进程的逻辑地址是由系统进行映射到实际的物理地址,这样一个进程就没法访问其他进程的物理地址。

4、对于文件操作,通过内存映射文件技术的效率要比系统调用read高。因为read函数是先将文件拷贝到cache缓冲区,然后再拷贝到用户空间。而内存映射技术则是直接将硬盘上的文件映射到用户空间,少了一次拷贝过程。虚拟内存的应用场景是在大文件读取的时候用到。因为读的效率高,映射后,基于可以像操作内存一样同一个返回的指针操作这个文件。

5、linux下,删除文件夹:rm -rf dirname

r:标识详细递归删除,不管有多少级目录

f:表示没有任何提示

这个操作是不可逆的,切记

6、信号量(semaphore):是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。比如初始化一个信号量,信号量是一个非负整数。信号量每被用一次,值就会减一。当减为0时,线程就会进入阻塞状态,除非信号量又被加为整数。sem_post会使信号量加1,这是一个原子操作-------即同时对一个信号量做加“1”操作的两个线程是不会冲突的。而同时对同一个文件进行读写做操的两个程序就可能会引起冲突。

7、http是无状态协议,无状态的意思是说服务器不会保存你访问信息,你的后一次访问如果需要前一次做到承前启后。

于是客户端浏览器cookie和服务器端session就应运而生。对于有cookie功能的网站,当你首次访问时,会带上你的个人用户信息在http头,服务器响应后也会在http头带上这些个人信息。然后浏览器就会把这些信息存在cookie中,以后每次访问都会带上这些个人信息。同样,当你访问这个网站时,服务器会为你生成一个session并获得一个独一无二的session ID,这个session里面保存的就是你的访问信息。服务器在响应时会把session id 也带上,同样也保存在cookie 中。浏览器每次访问也会把这个session ID带上。就这样,通过session ID实现在客户端和服务器的状态保持。浏览器会出现你访问过的东西是因为浏览器里面有保存你的cookie 个人信息,而服务器的session保存你的浏览记录,每次你重新访问时,浏览器带过去的session ID会让服务器找到对应的session并将你的访问记录带回来形成精准广告投放。所以你如果把浏览器的cookie信息清除,那么你的http请求就不会有个人信息和session ID,那么服务器就没法将你和特定session相对应,从而那些推送就是随机的了。一般不同网站的cookie是不能互相访问的,但是现在有广告联盟,通过在代码里面嵌入可以访问其他网站cookie的代码,达到可以访问其他网站cookie的信息,这也就是为什么你在淘宝的访问记录会出现在天猫的网页上。

8、在64 位操作系统下,64位的dll是在system32文件夹里面,32位的dll是在sysWOW64文件夹里面。sysWoW64 (Windows-on-Windows 64-bit)是一个Windows操作系统的子系统, 能够运行32-bit 应用 windows操作系统程序, 并且在所有的64-bit 版本的windows上都存在,WoW64被设计用来处理许多在32-bit Windows 和64-bit Windows之间的不同, 尤其是在Windows自身的结构变化上的不同。出现0x0007b错误可能是dll文件64为和32位的弄错了

9、LoadLibrary显示加在dll失败的原因可能有:1、路径错误2、要加载的dll还由依赖其他的dll,如果缺少其中的一个就会导致loadlibrary失败。可以用dependency Walker进行查看依赖的dll 3、dll32位和64位错误,32位机上面需要32位的dll,64位机上面需要64位的dll

10、生成patch

git format-patch HEAD^ <==最近的1次commit的patch 

git format-patch HEAD^^ <==最近的2次commit的patch

git format-patch HEAD^^^ <==最近的3次commit的patch

git format-patch HEAD^^^^ <==最近的4次commit的patch

git format-patch HEAD^^^^^ <==不支持!!!!error!!!

应用patch

git apply xxx.patch

11、pdb用法:在软件崩溃的时候,点击调试,导入pdb文件,查看调用信息

每次提交代码,编译生成的pdb文件最好是要同步提交,方便到时出现问题了排查问题。

12、调用约定:

https://www.geek-share.com/detail/2683911157.html

调用约定是对一个函数调用的一个约束和规定,描述了函数参数是怎么传递和由谁清除堆栈额。他决定以下内容:1、函数参数的压栈顺序2、由调用者还是被调用者吧参数弹出栈3、产生函数修饰名的方法。其实函数的完整写法应该是:void __cdecl function(); C/C++默认的调用约定为__cdecl。所以我们用的时候可以不写。其他的调用约定有__cdecl 、__stadcall、fastcall.应用最广的是前面两个。

13、函数的调用过程:

1)栈是向下生长的,所谓的向下生长是指从内存的搞地址->向低地址延伸,所以有栈顶和栈底之分,且栈顶的地址比栈底的低。寄存器ebp,可称为帧指针或基址指针,ebp寄存器在为受到改变之前始终指向栈帧的开始,也就是栈底,起作用是在堆栈中寻址用的。

2)寄存器esp可称为栈指针。esp是会随着数据的入栈和出栈移动的,也就是说,esp始终指向栈顶

A为调用函数 B为被调用函数:

1)先将调用者A的ebp入栈,以保存之前的任务信息。

2)将调用者A的esp栈顶指针赋给ebp,作为新的基址。(这个基址是用来下一步开辟一块作为B的栈底。

3)然后再这个基址上开辟响应的空间用作被调用者的栈空间。

4)函数B返回后,从当前的栈帧的ebp恢复为调用者A 的栈顶,使栈顶恢复为函数B被调用之前的位置,然后调用者A再从恢复后的栈顶弹出之前的ebp值,赋给ebp。这样,ebp和esp都恢复了调用函数B之前的位置,也就是恢复函数B调用前的状态。

14、代码中的特殊注释技术:

TODO:+说明:

如果代码中有该标识,说明在标识处有功能代码待编写,待实现的功能在说明中会简略说明

FIXME:+说明:

如果代码中有该标识,说明标识处代码需要修改,甚至代码时错误的,不能工作,需要修复,如何修正会再说明中简略说明

XXX:+说明:

如果代码中有该标识,说明标识处虽然实现了功能,但是实现的方法还有待商榷,希望将来能改进,要改进的地方会在说明中简略说明

15、malloc和new的区别

1、malloc/free是标准库函数 new/delete是c++的运算符

2、对于非内部数据类型而言,光用malloc/free无法满足动态对象的要求。对象在创建的时候要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不再编译器的控制权限之内,不能够吧执行构造函数和析构函数的任务强加于malloc/free

3、new可以看做是malloc加构造函数的执行。new出来的数据是直接带类型信息的,而malloc返回的是void类型

4、new可以对对象进行初始化,而malloc不行

16、中断的执行过程:中断请求、中断判优、中断响应、中断处理、中断返回

17、pthread_setcancelstate, pthread_setcanceltype, pthread_testcancel-set cancelabilitystate.这三个函数用来设置线程是否可以被其他线程调用pthread_cancel函数取消/终止

18、内存池概念:应用程序可以通过系统的内存分配调用预先一次性申请适当大小的内存作为一个内存池,之后应用程序对内存的分配的释放则可以通过这个内存池来完成。只有当内存池大小需要动态扩展时,才需要再调用系统的内存分配函数,其他时间堆内存的一切操作都在应用程序的掌控之中

从线程安全的角度来说,内存池可以分为单线程和多线程内存池。单线程内存池整个生命周期只被一个线程使用,因而不需要考虑互斥访问的问题;多线程内存池有可能被多个线程共享,因此则需要再每次分配和释放内存时加锁。相对而言,单线程内存池性能更高,而多线程内存池适用范围更广。

 

内存池的使用是要解决默认的内存管理函数的一些不足。默认的内存管理函数在堆上分配和释放内存会有一些额外的开销。系统在接手到分配一定大小内存的请求时,首先查找内部维护的内存空闲块表,并且需要根据一定算法,完成内存分配和释放。默认的内存管理函数还要考虑多线程的应用,需在每次分配和释放内存时加锁,同样增加了开销。

可见,如果应用程序频繁地在堆上分配和释放内存,则会导致性能的损失。并且会使系统中出现大量的内存碎片,降低内存的利用率。

优点如下:

1、针对特殊情况,例如需要频繁分配释放固定大小的内存对象时,不需要复杂的分配算法和多线程保护。也不需要维护内存空闲表的额外开销,从而获得较高的性能

2、由于开辟一定数量的连续内存控件作为内存池块,因而一定程度上提高了程序的局部性,提升了程序性能

3、比较容易控制页边界对齐和内存字节对齐,没有内存碎片的问题。

19、拷贝构造函数:普通类型的对象之间的互相复制是很简单的事情,但是当涉及到类对象的时候,由于类对象内部一般结构比较复杂,存在各种变量。拷贝构造函数的存在就是为了解决这种问题。

拷贝构造函数如果类中没有实现,编译器将会自动生成一个默认的拷贝构造函数,该默认构造函数完成对象之间的位拷贝,又称为浅拷贝。

在以下情况都会调用拷贝构造函数:

1、一个对象以值的方式传入函数体

2、一个对象以值传递的方式从函数返回

3、一个对象需要通过另一个对象进行初始化

浅拷贝的弊端:

在某些情况下,类成员变量需要动态开辟内存,如果实行为拷贝,也就是把对象的值完全复制给另一个对象,如A=B。这是如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。那A中的那个成员变量也指向同一块内存。这就出现一个问题,如果B把内存释放了,这时A的指针就是野指针了。出现运行错误。所以如果类中有涉及堆内存分配的变量,那么,就需要自己实现拷贝构造函数

20、虚函数

在某基类中声明为virtual并在一个或者多个派生类中被重新定义的成员函数。用法格式为:virtual 函数返回类型 函数名 (参数表);实现多态性,通过指向派生类的基类指针或者引用来访问派生类中同名覆盖成员函数。

假设类A 类B继承类A,A中有fun函数。B中也有fun函数。

A a;B b; A *p1=&a; B *p2 =&b;p1->fun();p2->fun();如果A中的fun没有定义为虚函数,那么两个调用调到的都是A类中的fun。如果A 中定义了fun为虚函数,那么就会调用A和B中的fun.

注意:父类中有虚函数,那么子类中的和父类虚函数同名的函数也是虚函数

虚函数的实现原理:https://baike.baidu.com/item/%E8%99%9A%E5%87%BD%E6%95%B0/2912832?fr=aladdin

问题:虚函数是如何做到因为对象的不同而调用其相应的函数呢?

如果一个类中有虚函数的存在,编译器就会在这个类中插入一段数据,并未这个类创建了一个表,这个表用来保存虚函数的地址。插入的这段数据就是个指针,这个指针指向了这个虚函数表。这个指针实际上是保存在类的对象里面。一下有一段简单的代码可以模拟虚函数:

#include<iostream>

using namespace std;

//将上面“虚函数示例代码”添加在这里

int main()

{

void(*fun)(A*);

A *p=new B;

long lVptrAddr;

memcpy(&lVptrAddr,p,4);

memcpy(&fun,reinterpret_cast<long*>(lVptrAddr),4);

fun(p);

delete p;

system("pause");

return 0;

}

21、ssl https://www.2cto.com/article/201203/121534.html

1)安全概念:保密性(只有你和你允许的人可以看到相关信息)。完整性(你的信息没有被破坏过或者篡改过)。可获得性(你需要的时候能够给访问到的信息或者保证对方能够收到你的信息)

2)那么我们如何保证上面的几个安全问题呢?通常有以下两个方法:认证:认证就是证明你就是那个你所声称的人。授权:授权是指一个系统里面有很多用户,有些用户能做某些事,有些用户不能做某些事。SSL和数字证书更多关注第一点

3)bt用的是MD5(散列)。MD5是hash中的一种,但不是hash的全部,hash还有sha1.哈希就是能够把任意大小的文档变成一个固定大小的字符串,并且这个过程是不可逆的。所以你要发送文件时,只要把对应的hash也发过去。接收方收到以后自己生成哈希值和你发的哈希值进行比较就能知道有没有被修改

对称加密:

对称加密的算法是公开的,但是密钥是保密的。对称加密是指加密和解密的密钥是同一个。对称加密的算法的优点是速度快,缺点是密钥管理不方便,要求共享密钥

非对称加密:

加密和解密不是用的同一个密钥,不能从公约推导私钥,或者说很困难。非对称加密的优点是密钥管理很方便,缺点是速度慢

数字签名:

在现实世界中,我们会通过签名来证明你确认了一件事。别人可以对比你的签名来判断是否你是冒充的。而在虚拟世界里,我们有数字签名来证明某个文档是你创建的,或者是你认可的。数字签名所用的技术是散列和非对称加密。数字签名的假设是:只有你自己知道你的私钥。根据前面散列的介绍,我们先为你要签名的信息生成一个hash子串,hash1,然后用你的私钥加密得到加密的hash1。这就是你对这个文档的数字签名。当别人需要验证某个文档是否是你签名的时候,只需要用你的公约解密你的签名得到hash1,并和该文档计算出来的hash进行比较,查看是否一致。由于只有你有私钥,所以任何其他人都无法产生用你的私钥加密过的hash1,所以是不可伪造。由于你对文档A的签名不可能对文档B有效,因为你对文档B的签名必然和对A的签名不一样,这是hash的唯一性保证的。拿你对A的签名去校验B是不可能通过的,所以是不可移植和复制。因为不可伪造不可移植不可复制,所以不可否认。

疑问:为什么对hash值进行加密,不直接对文档进行加密。这是由于加密文档的时间一般比加密hash来的久。

 

在现实生活中,各种加密手段往往是配合使用以达到最好的效果和效率。比如SSL和数字证书,就是混合了各种的加密手段

重点来了

数字证书:现实世界中,我们可以用身份证和护照来证明身份,那么在虚拟世界里,数字证书就是身份证。在虚拟世界里,并不是每一个上网的用户都有数字证书,只有当一个人需要证明自己的身份的时候才需要用到数字证书。像我们访问浏览器,我们并不用证明我们自己的身份,相反的,网站需要证明自己身份。比如你想提交信用卡信息,如何确定你正在访问的网站就是你所想要访问的那个呢?所以在提交自己的信息之前一定要先验证一下网站的身份,要求网站出示数字证书。一般正常的网站都会主动出示自己的数字证书。

数字证书的构成:

我们的身份证是由公安机关办法的,并加有很多防伪技术,不能伪造或者说很难。同样的,数字证书也有专门的发证机关(Certificate Authority,简称CA,其实就是一些商业公司)。数字证书的发证机关会对自己发放的证书加上自己的数字签名,用于保证证书不能被伪造。数字证书包含:持有者姓名、发证机关、有效日期、证书持有人的公钥、扩展信息、由发证机关对该证书的数字签名。

可以这么理解:数字证书包含证书的主体和数字签名。证书中的签名是对证书主体的签名

 

如何验证数字证书:

首先我们是默认拥有发证机关的公钥的。如果是浏览器的话,常见的发证机关的公钥是内置的。当浏览器拿到一个数字证书,先看发证机关,然后从内置的证书中找到发证机关的相关证书,获得相应的发证机关的公钥。用此公钥解密MD5,再用MD5重新计算一遍MD5 ,对比一下即可。

证书就是身份证,但是身份证可能被别人捡到,盗用,数字证书也有同样的问题,像任何人都可以拥有你的证书。。在验证完身份证的真假之后,还要验证持有人。这就要依赖证书里面包含的公钥了,此公钥是这张证书所有者的公钥。注意是所有者不是持有者。我们用此公钥加密一段信息发送给证书的持有者,如果持有者能发送回(可以是被私钥加密也可以是明文,没关系)被加密的这段信息的话,就证明该持有者拥有该证书对应的私钥,也就是说,该持有者就是该证书的所有者。最后一步核对证书上面的common name。如果三步都核对过了,就没问题了。

 

SSL的基本原理

由于Internet架构问题,信息在网络上很容易被别人发现。前面讨论那么多的保证信息安全的技术,ssl就是建立在这些技术的基础之上的一套协议。secure socket layer,是一种间于传输层(tcp/ip)和应用层(http)的协议。

最简单的方法来保证通信安全是用非对称加密。如果双方都认证了对方的数字证书,那么每次传输信息的时候都用对方的公钥加密,这样就只有对方能解密,从而保证了信息的安全。但是有两个问题:非对称加密速度缓慢,消耗资源。不可能要求每个用户去申请数字证书。

 

上图分别是SSL握手单向认证和双向认证。Please send back Keys[s_pub]("1234")就是用对方的公钥加密后发送给对方用来确认证书持有者就是证书所有者。最终协商出一个对称密钥,然后就进入了传输协议的部分。但是并不是简单地用这个对称密钥加密数据。而是每次同时为这个消息生成一个时间戳,用词密钥为消息和相应的时间戳生成消息认证码(mac),也就是说每次发送的内容包括

Encrypt(message) + MAC(message + timestamp)这样做有几个好处:

防止消息被篡改:所谓的篡改就是第三方篡改往来的消息,由于消息是加密的,第三者不能获得消息的内容,但是可以闭着眼睛瞎改。如果没有mac的话,接受者就无法判断此消息是否被篡改过。

防止消息被重放:防止第三者截获消息,并重新发送给客户端或者服务端。

SSL的基本思想是用非对称加密来建立连接,也就是握手阶段,用对称加密来传输数据,也就是传输阶段。这样既保证了密钥分发的安全,也保证了通信的效率

SSL并不依赖与TCP,他可以建立在任何可靠的传输层协议(比如TCP),不能建立在UDP上面。很显然,如果传输都不可靠,偶尔丢几个包或者顺序换一换,那还怎么保证安全?

22、signal(SIGPIPE, SIG_IGN);

TCP是全双工的信道, 可以看作两条单工信道, TCP连接两端的两个端点各负责一条. 当对端调用close时, 虽然本意是关闭整个两条信道, 

但本端只是收到FIN包. 按照TCP协议的语义, 表示对端只是关闭了其所负责的那一条单工信道, 仍然可以继续接收数据. 也就是说, 因为TCP协议的限制, 

一个端点无法获知对端的socket是调用了close还是shutdown.

对一个已经收到FIN包的socket调用read方法, 

如果接收缓冲已空, 则返回0, 这就是常说的表示连接关闭. 但第一次对其调用write方法时, 如果发送缓冲没问题, 会返回正确写入(发送). 

但发送的报文会导致对端发送RST报文, 因为对端的socket已经调用了close, 完全关闭, 既不发送, 也不接收数据. 所以, 

第二次调用write方法(假设在收到RST之后), 会生成SIGPIPE信号, 导致进程退出.

为了避免进程退出, 可以捕获SIGPIPE信号, 或者忽略它, 给它设置SIG_IGN信号处理函数:

signal(SIGPIPE, SIG_IGN);

这样, 第二次调用write方法时, 会返回-1, 同时errno置为SIGPIPE. 程序便能知道对端已经关闭

23、用按位运算代替四则运算可以提高程序执行效率,具体可以查看相同功能的语句的反汇编结果,按位运算消耗的cpu周期会少很多。

a % b <==> a&(b-1) 条件:当b 是2的n次幂的时候,n为正数

24、编译命令 gcc -o main -c main.c

加了-c只编译不链接,如果你的main文件里面包含其他库的东西,会导致执行失败,解决办法是不加-c

http://lib.csdn.net/article/linux/32802

25、要用gdb 调试程序,编译的时候要加上-g 如:gcc test.c -o test -g

26、

int main(void){

struct in_addr2\ addr1, addr2;

char * str1, * str2;

 

addr1.s_addr = htonl(0x1020304);

addr2.s_addr = htonl(0xc0a80101);

 

str1 = inet_ntoa(addr1);

str2 = inet_ntoa(addr2);

 

printf("%#lx -> %s \n", (long)addr1.s_addr, str1);

printf("%#lx -> %s \n", (long)addr2.s_addr, str2);

 

return 0;

}

这种情况下,str2的结果可能会覆盖strr1的结果,因为inet_ntoa函数会在内部申请内存保存结果并返回,如果两次申请的内存一样,就会导致结果被覆盖。

String类型的c_str也会有同样的问题。所以解决方法是str1返回后,立马用另一块内存来保存结果。

24、new和malloc的区别

1)、new/delete是C++关键字,需要编译器支持,malloc和free是库函数,需要头文件支持

2)、new申请时无需指定大小,,而malloc需要显示指出尺寸

3)、new成功后,返回的是特定对象,是符合类型安全的操作符,而malloc分配成功则是返回void*

4)、new分配失败时,会跑出异常,malloc失败返回NULL

5)、自定义类型,new会先调用operate new函数,调用构造函数和析构函数等,而malloc不支持对象的构造和析构

6)、new允许重载,malloc不允许重载

7)、存储地址不一样,new在自由存储区,malloc在堆

25、引用和指针的区别

  (1). 指针是一个实体,而引用仅是个别名;

   (2). 引用使用时无需解引用(*),指针需要解引用;

   (3). 引用只能在定义时被初始化一次,之后不可变;指针可变;

   (4). 引用没有 const,指针有 const,const 的指针不可变;

   (5). 引用不能为空,指针可以为空;

   (6). “sizeof 引用”得到的是所指向的变量(对象)的大小,

    而“sizeof指针”得到的是指针本身(所指向的变量或对象的地址)的大小;

   (7). 指针和引用的自增(++)运算意义不一样;

26、死锁的四个条件及处理方法。

  (1)互斥条件:一个资源每次只能被一个进程使用。    

       (2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 

       (3)不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。   

       (4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。  

      解决死锁的方法分为死锁的预防,避免,检测与恢复三种 

27、内存对齐问题的原因?

 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据;

 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐,因为为了访问未对齐的内存,处理器需要做两次内存访问,而对齐的内存访问仅需要一次。

28、比较一下进程和线程的区别?

 (1)、调度:线程是CPU调度和分派的基本单位

 (2)、拥有资源:

  *  进程是系统中程序执行和资源分配的基本单位

  *  线程自己一般不拥有资源(除了必不可少的程序计数器,一组寄存器和栈),但他可以去访问其所属进程的资源,

     如进程代码,数据段以及系统资源(已打开的文件,I/O设备等)。

(3)系统开销:

  *  同一进程中的多个线程可以共享同一地址空间,因此它们之间的同步和通信的实现也比较简单

  *  在进程切换的时候,涉及到整个当前进程CPU环境的保存以及新被调度运行的进程的CPU环境的设置;

     而线程切换只需要保存和设置少量寄存器的内容,并不涉及存储器管理方面的操作,从而能更有效地使用系统资源和

     提高系统吞吐量。

29、进程间通信方式:管道、命名管道、消息队列、共享内存、信号、信号量、套接字。

 (1)、 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。

       进程的亲缘关系通常是指父子进程关系。

 (2)、有名管道 (named pipe) :有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

 (3)、信号量( semophore ) :信号量是一个计数器,可以用来控制多个进程对共享资源的访问。

      它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。

      因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

 (4)、消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。

      消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

 (5)、信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

 (6)、共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,

      这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,

      如信号两,配合使用,来实现进程间的同步和通信。

 (7)、套接字( socket ) : 套接字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。 

30、宏和函数的优缺点?

 (1)、函数调用时,先求出实参表达式的值,然后带入形参。而使用带参数的宏只是进行简单的字符替换。

 (2)、函数调用是在程序运行时处理的,分配临时的内存单元;而宏展开则是在编译时进行的,在展开时并不分配内存单元,

   不进行值的传递处理,也没有“返回值”的概念。

 (3)、对函数中的实参和形参都要定义类型,二者的类型要求一致,应进行类型转换;而宏不存在类型问题,宏名无类型,

   它的参数也是无类型,只是一个符号代表,展开时带入指定的字符即可。宏定义时,字符串可以是任何类型的数据。

 (4)、调用函数只可得到一个返回值,而宏定义可以设法得到几个结果。

 (5)、使用宏次数多时,宏展开后源程序长,因为每次展开一次都使程序增长,而函数调用不使源程序变长。

 (6)、宏替换不占运行时间,只占编译时间;而函数调用则占运行时间(分配单元、保留现场、值传递、返回)。

31、C和c++的不同

 c和c++的一些不同点(从语言本身的角度):

 1)c++源于c,c++最重要的特性就是引入了面向对象机制,class关键字。

 2)c++中,变量可以再任何地方声明;c中,局部变量只能在函数开头声明。

 3)c++中,const型常量是编译时常量;c中,const常量只是只读的变量。

 4)c++有&引用;c没有

 5)c++的struct声明自动将结构类型名typedef;c中struct的名字只在结构标签名字空间中,不是作为一种类型出现

 6)c语言的main函数可以递归调用;c++中则不可以

 7)c中,void *可以隐式转换成其他指针类型;c++中要求现实转换,否则编译通不过

32、大小端问题

大小端格式问题。

 方法一:

 void checkCpuMode(void)

 {

  int i = 0x12345678;

  char *cp = (char *)&i;

  if(*cp == 0x78)

   printf("little endian");

  else

   printf("big endian\n");

 }

 方法二:

 void checkCpuMode(void)

 {

  int a = 0x12345678;

  if((char)a == 0x12)

   printf("big endian\n");

  else 

   printf("little endian\n");

 }

 方法三:

 void checkCpuMode(void)

    {

  union 

  {

   short s;

   char c[sizeof(short)];

  }un;

  un.s=0x0102;

  if(un.[0]==1&&un.c[1]==2)

   printf("big endian\n");

  else

   printf("little endian\n");

 }

34、由C/C++编译的程序占用的内存分为以下几个部分 

 1、栈区(stack): 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 

 2、堆区(heap): 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。

                 注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。 

 3、全局区(static): 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,

                    未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后有系统释放 。 

 4、文字常量区: 常量字符串就是放在这里的, 程序结束后由系统释放。 

 5、程序代码区: 存放函数体的二进制代码。

35、编写内核程序中申请内存和编写应用程序时申请内存有什么区别

     应用程序使用C函数库中的内存分配函数malloc()申请内存内核会为进程使用的代码和数据空间维护一个当前位置的值brk,

 这个值保存在每个进程的数据结构中。它指出了进程代码和数据(包括动态分配的数据空间)在进程地址空间中的末端位置。

 当malloc()函数为程序分配内存时,它会通过系统调用brk()把程序要求新增的空间长度通知内核,

 内核代码从而可以根据malloc()所提供的信息来更新brk的值,但此时并不为新申请的空间映射物理内存页面。

 只有当程序寻址到某个不存在对应物理页面的地址时,内核才会进行相关物理内存页面的映射操作。

     当用户使用内存释放函数free()动态释放已申请的内存块时,c库中的内存管理函数就会把所释放的内存块标记为空闲,

 以备程序再次申请内存时使用。在这个过程中内核为该进程所分配的这个物理页面并不会被释放掉。

 只有当进程最终结束时内核才会全面收回已分配和映射到该进程地址空间范围内的所有物理内存页面。

36、CPU密集型(CPU-bound)和I/O密集型(I/O bound)

CPU密集型是指CPU的性能低于硬盘和内训的性能。或者由于多线程的处理,导致大部分时间下都是CPU占用率极高

IO密集型是指内存和硬盘性能比CPU差,大部分时间下,都是CPU在等待I/O的的读写操作或者网络的读写操作。

对于IO密集型的任务,用运行速度较快的C语言替换Python这种运行速度极低的脚本语言是无法提升效率的。对于IO密集型任务,最合适的语言就是开发效率极高的脚本语言。

总之,CPU密集型适合C语言多线程,IO密集型适合脚本语言开发的多线程。因为CPU密集型要求处理速度快。而IO密集型就算CPU再快也是没有用的,如果读写本身就慢,CPU再快也是没用的,这种情况下,开发速度快才是王道。

斜程:协程适合处理的是I/O密集型任务,处理CPU密集型任务并不是它的长处,如果要提升CPU的利用率可以考虑“多进程+协程”的模式。协程类似于线程,只是协程是在同一个线程里面执行的,跟线程相比,不用锁,切换开销小于线程切换。

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: