指针函数和函数指针的区别
指针函数是指带指针的函数,即本质是一个函数,函数返回类型是某一类型的指针。
首先它是一个函数,只不过这个函数的返回值是一个地址值。函数返回值必须用同类型的指针变量来接受,也就是说,指针函数一定有函数返回值,而且,在主调函数中,函数返回值必须赋给同类型的指针变量。
函数指针是指向函数的指针变量,即本质是一个指针变量。
内存分布
进程运行时的典型内存布局
内存越界、堆区、栈区的概念
- 栈 - 由编译器自动分配释放
- 堆 - 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收
- 全局区(静态区),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。- 程序结束释放
- 另外还有一个专门放常量的地方。- 程序结束释放
在函数体中定义的变量通常是在栈上,用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。
用户态、内核态的概念
用户态
(User Mode)和内核态
(Kernel Mode)是计算机系统中的两种不同的执行模式,用于区分运行在不同特权级别下的程序或操作系统的代码。
用户态(User Mode):
用户态是指程序在较低特权级别下执行的状态。在用户态下执行的程序受到限制,不能直接访问和操作系统的底层资源和硬件设备。
在用户态下运行的应用程序只能访问自己的内存空间和被授权的资源,不能直接进行系统级的操作,如访问硬件设备、修改内核数据结构等。
用户态下的程序通常是由用户编写的应用程序,如文本编辑器、浏览器等。它们依赖于操作系统提供的系统调用接口来访问内核功能和资源。
内核态(Kernel Mode):
内核态是操作系统内核执行的特权级别,具有最高的特权级别。在内核态下执行的代码可以直接访问和操作系统的底层资源和硬件设备。
内核态下的代码可以执行敏感的操作,如修改内核数据结构、管理系统资源、处理中断等。它们能够执行特权指令和访问受保护的内存区域。
内核态下的代码通常是操作系统的内核代码,负责管理和控制计算机系统的各个方面,如进程调度、内存管理、设备驱动程序等。
在操作系统中,用户态和内核态之间的切换是通过特权级别的转换来实现的。当应用程序需要访问操作系统的功能或资源时,它会发起系统调用,将控制权转移到内核态,让内核执行相应的操作。完成操作后,内核将结果返回给用户态,并将控制权重新交还给用户态程序。
用户态和内核态的划分有助于保护操作系统的稳定性和安全性。通过限制用户态程序的访问权限,可以防止恶意程序对系统的滥用和破坏。同时,内核态的特权级别可以确保操作系统能够有效地管理和控制系统资源,提供稳定和安全的计算环境。
以下是一个简单的示例代码,演示了用户态程序通过系统调用接口向操作系统请求服务的过程:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
int fd;
char buffer[100];
// 打开文件
fd = open("example.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1) {
perror("open");
return 1;
}
// 写入数据
write(fd, "Hello, World!", 13);
// 关闭文件
close(fd);
printf("File saved successfully.\n");
return 0;
}
野指针
野指针:访问一个已销毁或者访问受限的内存区域的指针,野指针不能判断是否为NULL来避免
野指针产生的原因:
- 指针定义时未被初始化:指针在被定义的时候,如果程序不对其进行初始化的话,它会随机指向一个区域,因为任意指针变量(除了static修饰的指针)它的默认值都是随机的
- 指针被释放时没有置空:我们在用malloc()开辟空间的时候,要检查返回值是否为空,如果为空,则开辟失败;如果不为空,则指针指向的是开辟的内存空间的首地址。指针指向的内存空间在用free()和delete释放后,如果程序员没有对其进行置空或者其他赋值操作的话,就会成为一个野指针
- 指针操作超越变量作用域:不要返回指向栈内存的指针或者引用,因为栈内存在函数结束的时候会被释放。
大小端
大端模式:是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中。
小端模式:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。
下面以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,我们可以用unsigned char buf[4]来表示value
进程间通信的理解
常见的通信方式
管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
命名管道FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
排查问题的思路
memcpy,malloc
void *memcpy(void *str1, const void *str2, size_t n)
从存储区 str2 复制 n 个字节到存储区 str1。
void *malloc(size_t size)
分配所需的内存空间,并返回一个指向它的指针。
该函数返回一个指针 ,指向已分配大小的内存。如果请求失败,则返回 NULL。
标准的linux行为
-
文件系统层次结构:Linux遵循文件系统层次结构标准(Filesystem Hierarchy Standard,FHS),定义了文件和目录在系统中的组织结构。例如,系统文件通常位于/bin、/sbin、/etc等目录下,用户的主目录位于/home目录下。
-
Shell和命令行界面:Linux使用Bash(Bourne Again Shell)作为默认的命令行解释器,并提供了一套标准的命令行工具和选项。这些工具和选项在不同的Linux发行版之间具有一致的行为和语法。
-
系统调用接口:Linux提供了一组标准的系统调用接口,允许用户程序与操作系统进行交互。这些系统调用包括文件操作、进程管理、网络通信等功能,通过它们可以实现各种应用程序。
-
POSIX兼容性:Linux遵循POSIX(Portable Operating System Interface for Unix)标准,这是一套定义了Unix-like操作系统接口的规范。POSIX兼容性确保了在符合标准的系统上,应用程序可以以一致的方式运行。
-
网络协议支持:Linux支持各种网络协议,如TCP/IP、UDP、IPv4、IPv6等。它提供了一套标准的网络编程接口,使开发人员能够编写网络应用程序。
-
用户和权限管理:Linux使用用户和组的概念进行权限管理。每个用户都有唯一的用户ID(UID)和所属组ID(GID)。文件和目录具有所有者、所属组和其他用户的权限控制。
-
进程管理:Linux使用进程作为执行程序的基本单位。它提供了进程创建、终止、调度和通信的机制,允许多个进程并发执行。
-
设备驱动:Linux使用统一的设备驱动模型,通过设备文件(如/dev目录下的文件)来访问硬件设备。设备驱动程序提供了一套标准的接口,允许应用程序通过文件操作来访问设备。
常用的perf、strace、valgrind等查问题工具
perf top分析出来CPU时间主要花费在哪里
strace
有两种运行模式。
一种是通过它启动要跟踪的进程。用法很简单,在原本的命令前加上strace即可。比如我们要跟踪 “ls -lh /var/log/messages
” 这个
命令的执行,可以这样:
strace ls -lh /var/log/messages
另外一种运行模式,是跟踪已经在运行的进程,在不中断进程执行的情况下,理解它在干嘛。 这种情况,给strace传递个-p pid 选项即可。
比如,有个在运行的some_server服务,第一步,查看pid:
pidof some_server
17553
得到其pid 17553然后就可以用strace跟踪其执行:
strace -p 17553
完成跟踪时,按ctrl + C 结束strace即可。
交换机方向:
MAC地址
MAC 地址(Media Access Control address)是一个用于识别网络设备的唯一标识符。它是由网络适配器(如网卡)或其他网络接口设备硬件上的固定地址。
MAC 地址通常是一个由六组十六进制数字(0-9,A-F)组成的字符串,每组表示两个数字。例如,一个典型的 MAC 地址可能是类似于 “00:1A:2B:3C:4D:5E” 的形式。
MAC 地址的长度是固定的,通常是 48 位或 64 位,其中一部分用于标识设备的制造商(OUI,Organizationally Unique Identifier),另一部分用于设备的唯一编号。
MAC 地址在局域网(LAN)中起着重要的作用。它用于识别和定位网络中的设备,以便在数据通信过程中正确地发送和接收数据包。例如,当您从计算机发送数据包到网络上的另一个设备时,数据包会包含目标设备的 MAC 地址,以确保数据包被正确地传递到目标设备。
每个网络设备都应该具有唯一的 MAC 地址,这样可以确保网络中的设备能够正确地进行通信。MAC 地址一般由设备制造商在生产过程中分配,并且很少会发生冲突。
需要注意的是,MAC 地址是一个物理地址,与设备的 IP 地址(逻辑地址)是不同的概念。MAC 地址工作在数据链路层,而 IP 地址工作在网络层。
mtu的概念
MTU(Maximum Transmission Unit)是指在网络通信中,数据链路层协议(如以太网)一次能够传输的最大数据包的大小。它表示在不分片的情况下,网络中可以传输的最大数据量。
MTU 的大小通常以字节为单位,常见的 MTU 值为 1500 字节。这是以太网中最常见的 MTU 大小,但在某些网络环境中,也可能使用不同的 MTU 值。
MTU 大小对网络性能和通信效率具有重要影响。较大的 MTU 值可以减少数据包的数量,从而减少了网络开销和传输延迟。然而,较大的 MTU 值也会增加数据包的传输时间,因为较大的数据包需要更长的时间来传输。此外,如果网络中的某个设备或链路的 MTU 大小小于发送端的 MTU 大小,数据包可能会被分片,导致额外的开销和性能下降。
在实际网络配置中,可以根据网络环境和需求来调整 MTU 大小。例如,在某些广域网(WAN)连接中,由于链路质量或网络设备的限制,可能需要降低 MTU 大小以避免分片和传输问题。另外,一些特殊应用或网络协议可能需要使用较大的 MTU 大小来提高性能。
要查看和配置系统中的 MTU 大小,可以使用操作系统提供的网络配置工具或命令。例如,在 Linux 中,可以使用 ifconfig 或 ip 命令来查看和修改网络接口的 MTU 大小。在 Windows 中,可以使用 netsh 命令或网络适配器设置界面来配置 MTU 大小。
tcp在哪层
TCP 在传输层(运输层);
IP 在网络层(互联网层)。