【嵌入式八股24】linux软件开发知识点
写一半我真后悔了,这篇太长了……
一、进程间通讯方式
在 Linux 系统中,进程间通讯(IPC)是实现不同进程之间数据交互和协同工作的重要机制,常见的通讯方式有以下几种:
- 管道(Pipe)及有名管道(named pipe):
- 管道:是一种半双工的通信方式,数据只能单向流动,且只能在具有亲缘关系(如父子进程)的进程间使用。它在创建时会在内核中开辟一块缓冲区,用于数据的传输。
- 有名管道:克服了管道只能在亲缘关系进程间通信的限制,它以文件的形式存在于文件系统中,通过文件名来访问。不同进程只要拥有对该文件的访问权限,就可以进行通信。
- 信号(Signal):是一种异步通信机制,用于通知进程发生了某种事件。信号可以由系统内核、其他进程或用户手动发送,进程接收到信号后会根据预先设置的信号处理函数来做出相应的响应,如终止进程、暂停进程等。
- 报文(Message)队列(消息队列):消息队列是一个消息的链表,存放在内核中。进程可以向消息队列中发送消息,也可以从消息队列中读取消息。消息队列可以实现不同进程间的消息传递,并且可以根据消息的类型进行分类处理。
- 共享内存:是一种高效的进程间通信方式,它允许不同进程访问同一块内存区域。进程可以直接对共享内存进行读写操作,从而实现数据的共享。为了保证数据的一致性和正确性,使用共享内存时需要配合信号量等同步机制来避免数据冲突。
- 信号量(semaphore):本质上是一个计数器,用于控制对共享资源的访问。它可以实现进程间的同步和互斥,确保在同一时刻只有一个或多个进程可以访问共享资源,防止数据竞争和不一致的问题。
- 套接口(Socket):是一种通用的进程间通信机制,不仅可以用于同一台主机上的进程间通信,还可以用于不同主机之间的网络通信。Socket 支持多种通信协议,如 TCP、UDP 等,根据不同的需求可以选择合适的协议进行通信。
二、内存管理相关
(一)内存申请函数
在 C 语言中,常用的内存申请函数有以下几个,它们在动态内存分配中起着关键作用:
calloc
:函数会在内存中分配一块指定大小的内存空间,并将该空间的所有字节初始化为 0。它接受两个参数,分别是元素个数和每个元素的大小,返回指向分配内存的指针。例如,calloc(10, sizeof(int))
会分配 10 个int
类型大小的内存空间,并初始化为 0。malloc
:用于分配指定字节数的内存空间。它只有一个参数,即要分配的字节数,返回指向分配内存的指针。如果分配失败,返回NULL
。例如,int *ptr = malloc(10 * sizeof(int))
分配了 10 个int
类型大小的内存空间。realloc
:可以重新调整已分配内存的大小。它接受两个参数,第一个是指向已分配内存的指针,第二个是新的内存大小。如果重新分配成功,返回指向新内存的指针;如果失败,返回NULL
。例如,ptr = realloc(ptr, 20 * sizeof(int))
可以将原来分配的内存空间大小调整为 20 个int
类型大小。
(二)Linux 内存分配说明
在 Linux 系统中,内存被划分为多个区域,每个区域有其特定的用途和特点,存放不同类型的数据:
静态存储区 | 静态数据、全局数据、常量 | 在程序编译的时候就已经分配好,其生命周期与程序的运行周期相同,在程序结束时才会被释放。 |
栈区 | 局部变量、函数参数 | 栈内存分配运算内置于处理器的指令集中,因此效率很高,但栈的大小有限,分配的内存容量相对较小。当函数调用时,局部变量和函数参数会被压入栈中;函数返回时,这些数据会被弹出栈。 |
堆区 | malloc 申请的内存 |
用于动态内存分配,需要程序员手动使用 free 函数来释放内存,否则可能会导致内存泄漏。堆内存的分配比较灵活,可以根据程序的需求动态调整大小。 |
代码区 | 代码 | 存放函数体的二进制代码,是程序执行的指令集合。代码区是只读的,防止程序意外修改自身的指令。 |
文字常量区 | 常量字符串 | 程序结束后由系统释放,存放程序中定义的常量字符串,这些字符串在程序运行过程中不会被修改。 |
三、GCC 编译过程
GCC(GNU Compiler Collection)是 Linux 系统中常用的编译器,它将 C/C++ 等源程序编译成可执行文件需要经过多个步骤,每个步骤都有其特定的作用和生成的文件:
预编译 | *.i |
预处理器首先处理源文件中的预处理指令,如 #include (包含头文件)、#define (宏定义)等,将头文件的内容插入到源文件中,并展开宏定义。经过预编译后,生成一个 .i 文件,该文件包含了展开后的源代码。 |
编译 | *.s |
编译器将预编译后的 .i 文件进行语法分析、语义分析和优化等操作,生成汇编代码,保存在 .s 文件中。 |
汇编 | *.o |
汇编器将汇编代码转换为机器可执行的目标代码,生成 .o 文件。每个 .c 文件经过编译和汇编后都会生成一个对应的 .o 文件。 |
链接 | 可执行文件 | 链接器将多个 .o 文件以及所需的库文件进行链接,解析符号引用,将各个目标文件和库文件中的代码和数据组合成一个完整的可执行文件。 |
四、文件系统
文件系统是 Linux 系统中用于组织和存储文件的重要机制,常见的文件系统有以下几种,它们在不同的场景中有着各自的优势和应用:
fat |
早期广泛使用的文件系统,兼容性好,支持多种操作系统,但安全性和性能相对较低,不支持大文件和长文件名。 |
fat32 |
是 fat 文件系统的扩展,支持更大的分区和文件,提高了对大文件的支持能力,但仍然存在一些局限性,如单个文件大小不能超过 4GB。 |
ntfs |
是 Windows 系统常用的文件系统,具有较高的安全性和性能,支持文件压缩、加密、权限管理等功能,但在 Linux 系统中对 ntfs 的支持相对有限。 |
ext2 |
是 Linux 系统早期的文件系统,具有较好的性能和稳定性,但不支持日志功能,在系统崩溃时可能会导致数据丢失。 |
ext3 |
在 ext2 的基础上增加了日志功能,提高了文件系统的可靠性和数据安全性,当系统崩溃时可以通过日志恢复文件系统的一致性。 |
ext4 |
是 ext3 的升级版本,进一步提高了性能、支持更大的文件和分区,并且增加了一些新的特性,如延迟分配、多块分配等,是目前 Linux 系统中常用的文件系统之一。 |
nfs |
网络文件系统,允许不同的计算机通过网络共享文件和目录,使得用户可以像访问本地文件系统一样访问远程的文件资源,常用于分布式系统和网络存储环境中。 |
五、硬链接和软连接
在 Linux 系统中,链接是一种特殊的文件,用于指向其他文件或目录,分为硬链接和软链接(符号链接),它们在实现方式和特点上有所不同:
(一)硬链接
硬链接直接指向文件的 i 节点(索引节点),它和原文件具有相同的 i 节点,共享相同的数据和属性。因此,硬链接文件显示的大小和原文件是一样的,并且对原文件的修改也会反映在硬链接文件中。需要注意的是,硬链接不能链接目录文件,因为这可能会导致目录结构的混乱。例如:
ln file2 /home/xiaxiaowen/file2hard
上述命令创建了一个名为 file2hard
的硬链接,指向 file2
文件。
(二)软链接(符号链接)
软链接则是建立了一个新的文件,这个文件包含了指向目标文件的路径信息。软链接文件和原文件的 i 节点不一样,它类似于一个快捷方式。软链接可以链接目录,并且当原文件被删除或移动时,软链接会变成无效链接。例如:
ln -s file2 /home/xiaxiaowen/file2soft
上述命令创建了一个名为 file2soft
的软链接,指向 file2
文件。
六、Linux 内核子系统
Linux 内核是操作系统的核心部分,由多个子系统组成,这些子系统协同工作,提供了系统的基本功能和服务:
- 进程管理:负责进程的创建、调度、终止等操作,管理进程的生命周期。它包括进程的状态转换(如运行态、就绪态、阻塞态之间的转换)、进程间的通信和同步等功能,确保系统中多个进程能够高效、有序地运行。
- 内存管理:对系统的内存资源进行分配、回收和管理,包括物理内存和虚拟内存的管理。内存管理子系统负责将内存分配给进程使用,并通过虚拟内存技术使得进程能够使用比实际物理内存更大的地址空间,同时还需要处理内存的换入换出等操作,以提高内存的利用率。
- I/O 管理:管理系统的输入输出设备,包括磁盘、网卡、串口等设备。它负责设备的驱动程序管理、数据的读写操作以及设备的中断处理等,为用户和应用程序提供了统一的 I/O 接口,使得应用程序可以方便地访问各种设备。
- 文件系统管理:负责管理文件系统的创建、挂载、卸载以及文件的读写、目录的操作等。文件系统管理子系统提供了对文件和目录的组织和存储功能,使得用户可以方便地存储和访问数据。
七、进程状态
在 Linux 系统中,进程有多种状态,这些状态反映了进程在不同时刻的运行情况:
- 运行态:进程正在处理器上执行,占用 CPU 资源。在单处理器系统中,同一时刻只有一个进程处于运行态;在多处理器系统中,可能有多个进程同时处于运行态。
- 就绪态:进程已经准备好执行,但由于 CPU 资源有限,暂时没有被调度到处理器上运行。就绪态的进程在就绪队列中等待调度,一旦获得 CPU 资源,就可以立即进入运行态。
- 阻塞态:进程因为等待某个事件的发生(如 I/O 操作完成、等待信号等)而暂时无法继续执行,此时进程会进入阻塞态。在阻塞态下,进程不会占用 CPU 资源,直到所等待的事件发生,进程才会从阻塞态转换为就绪态。
八、文件系统组成
Linux 文件系统由多个部分组成,每个部分都有其特定的功能和作用,共同构成了文件系统的完整结构:
- 超级块:存放文件系统本身的信息,比如文件系统的类型、每个区域的大小、未被使用的磁盘块的信息等。不同版本的文件系统,超级块的具体内容可能会稍有差别。超级块是文件系统的关键部分,它描述了文件系统的整体布局和属性。
- i-节点表:每个文件都有其属性,如文件的大小、所有者、最近修改时间等,这些属性被存储在
ino_t
的结构体中,称为 i 节点。所有的 i 节点都有相同的大小,i 节点表就是这样一些节点的列表。表中的每个 i 节点都通过位置来标识,例如标识为 2 的 i 节点位于文件系统 i 节点表中的第 3 个位置(因为索引通常从 0 开始计数)。i 节点是文件系统中用于管理文件属性和数据存储位置的重要数据结构。 - 数据块:用于存放文件的内容。由于数据块的大小是固定的,所以有时一个文件会分布在多个数据块上,甚至可能分布在多个磁盘上。数据块是文件数据的实际存储单元,文件系统通过 i 节点来记录文件数据在数据块中的存储位置。
九、i 节点
i 节点是一个 64 字节长的表,表中包含了文件的相关信息,是文件系统中非常重要的数据结构。其中包含了文件的大小、文件所有者、文件的存取许可方式以及文件的类型等重要信息。通过 i 节点,文件系统可以快速地定位和管理文件的属性和数据存储位置。每个文件都有一个唯一的 i 节点,文件系统通过 i 节点来区分不同的文件。
十、Linux 文件类型
在 Linux 系统中,文件可以分为多种类型,不同类型的文件具有不同的特点和用途,通过文件的属性可以进行区分:
普通文件 | - |
包含数据、程序代码等内容的文件,如文本文件、可执行文件等。 |
目录 | d |
用于组织和管理文件和目录的特殊文件,类似于 Windows 系统中的文件夹。目录中包含了文件和子目录的信息。 |
字符设备文件 | c |
用于表示字符设备,如串口、终端等,这些设备以字符流的方式进行数据传输。 |
块设备文件 | b |
用于表示块设备,如硬盘、磁盘分区等,这些设备以块为单位进行数据存储和传输。 |
符号链接文件 | l |
也称为软链接,是一种特殊的文件,它指向另一个文件或目录,类似于快捷方式。 |
套接字文件 | s |
用于实现进程间的网络通信,是网络编程中常用的文件类型。 |
管道文件 | p |
用于实现进程间的通信,分为匿名管道和有名管道,是一种半双工的通信方式。 |
十一、Linux 常用系统调用函数
Linux 系统提供了丰富的系统调用函数,用于实现各种系统功能,这些函数是用户空间程序与内核进行交互的接口。根据功能的不同,可以将系统调用函数分为以下几类:
(一)进程控制函数
进程控制函数用于管理进程的生命周期,包括进程的创建、运行、终止等操作:
fork |
创建一个新进程,新进程是原进程的副本,父子进程共享部分资源,如代码段等。 |
clone |
按指定条件创建子进程,可以更灵活地控制子进程的资源共享和行为。 |
execve |
运行可执行文件,用新的程序替换当前进程的内存空间,实现程序的执行。 |
exit |
中止进程,在终止进程前会进行一些清理工作,如关闭文件描述符、释放资源等。 |
_exit |
立即中止当前进程,不会进行过多的清理工作,直接结束进程的运行。 |
getdtablesize |
获取进程所能打开的最大文件数,用于限制进程对文件的访问数量。 |
getpgid |
获取指定进程组标识号,用于管理进程组的相关操作。 |
setpgid |
设置指定进程组标志号,用于改变进程所属的进程组。 |
getpgrp |
获取当前进程组标识号,返回当前进程所属的进程组 ID。 |
setpgrp |
设置当前进程组标志号,将当前进程设置为新的进程组组长。 |
getpid |
获取进程标识号,返回当前进程的唯一 ID。 |
getppid |
获取父进程标识号,返回当前进程的父进程的 ID。 |
getpriority |
获取调度优先级,用于了解进程在系统中的调度优先级。 |
setpriority |
设置调度优先级,调整进程在系统中的调度优先级,影响进程获取 CPU 资源的机会。 |
modify_ldt |
读写进程的本地描述表,用于管理进程的内存访问权限等信息。 |
nanosleep |
使进程睡眠指定的时间,以纳秒为单位,用于实现精确的时间延迟。 |
nice |
改变分时进程的优先级,调整进程在分时系统中的调度优先级。 |
pause |
挂起进程,等待信号,直到进程接收到一个信号才会继续执行。 |
personality |
设置进程运行域,用于指定进程的运行环境和行为。 |
prctl |
对进程进行特定操作,如设置进程的属性、获取进程的状态等。 |
ptrace |
进程跟踪,用于调试和监控其他进程的运行情况。 |
sched_get_priority_max |
取得静态优先级的上限,了解系统中进程优先级的最大值。 |
sched_get_priority_min |
取得静态优先级的下限,了解系统中进程优先级的最小值。 |
sched_getparam |
取得进程的调度参数 |
sched_getscheduler |
取得指定进程的调度策略 |
sched_rr_get_interval |
取得按RR算法调度的实时进程的时间片长度 |
sched_setparam |
设置进程的调度参数 |
sched_setscheduler |
设置指定进程的调度策略和参数 |
sched_yield |
进程主动让出处理器,并将自己放入调度队列队尾 |
vfork |
创建一个子进程,以供执行新程序,常与 execve 等同时使用 |
wait |
等待子进程终止 |
wait3 |
参见 wait |
waitpid |
等待指定子进程终止 |
wait4 |
参见 waitpid |
capget |
获取进程权限 |
capset |
设置进程权限 |
getsid |
获取会话标识号 |
setsid |
设置会话标识号 |
以下是对上一部分内容的继续完善,涵盖了剩余的系统调用函数分类以及其他重要的 Linux 软件开发知识点:
(二)文件操作函数
文件操作函数用于对文件进行各种操作,包括打开、关闭、读写、定位等:
fcntl |
文件控制,可用于设置文件的各种属性,如文件描述符标志、文件状态标志等。 |
open |
打开文件,返回一个文件描述符,用于后续对文件的操作。可以指定打开文件的模式(如只读、读写等)和权限。 |
creat |
创建新文件,如果文件已存在则截断其内容。可以指定文件的权限。 |
close |
关闭文件描述字,释放与文件相关的资源,如文件句柄等。 |
read |
从文件中读取数据,将数据读取到指定的缓冲区中。返回实际读取的字节数。 |
write |
向文件中写入数据,将缓冲区中的数据写入到文件中。返回实际写入的字节数。 |
readv |
从文件读入数据到缓冲数组中,适用于从文件中读取多个不连续的数据块。 |
writev |
将缓冲数组里的数据写入文件,适用于向文件中写入多个不连续的数据块。 |
pread |
对文件进行随机读,在不改变文件当前偏移量的情况下,从指定位置读取数据。 |
pwrite |
对文件进行随机写,在不改变文件当前偏移量的情况下,向指定位置写入数据。 |
lseek |
移动文件指针,调整文件的当前偏移量,用于随机访问文件中的数据。 |
_llseek |
在 64 位地址空间里移动文件指针,用于处理大文件的偏移量操作。 |
dup |
复制已打开的文件描述字,返回一个新的文件描述符,指向同一个文件。 |
dup2 |
按指定条件复制文件描述字,可以将一个文件描述符复制到另一个指定的文件描述符上。 |
flock |
文件加/解锁,用于实现对文件的独占或共享访问控制,避免多个进程同时修改文件导致数据不一致。 |
poll |
I/O 多路转换,用于监视多个文件描述符的状态,当其中一个或多个文件描述符准备好进行 I/O 操作时,函数返回。 |
truncate |
截断文件,将文件的长度缩短到指定的长度。如果文件长度大于指定长度,则截断超出部分;如果文件长度小于指定长度,则文件大小不变。 |
ftruncate |
与 truncate 类似,只不过操作的是已打开的文件。 |
umask |
设置文件权限掩码,用于控制新创建文件的默认权限。 |
fsync |
把文件在内存中的部分写回磁盘,确保文件的数据已实际写入磁盘,而不是仅仅存在于内存缓冲区中。 |
(三)文件系统操作函数
文件系统操作函数用于对文件系统进行管理,包括目录操作、文件权限设置、文件系统信息获取等:
access |
确定文件的可存取性,检查当前进程是否具有对指定文件的读、写或执行权限。 |
chdir |
改变当前工作目录,将当前进程的工作目录切换到指定的目录。 |
fchdir |
与 chdir 类似,只不过操作的是通过文件描述符指定的目录。 |
chmod |
改变文件方式,设置文件的访问权限,如读、写、执行权限等。 |
fchmod |
与 chmod 类似,只不过操作的是已打开的文件。 |
chown |
改变文件的属主或用户组,将文件的所有权或所属用户组更改为指定的用户或用户组。 |
fchown |
与 chown 类似,只不过操作的是已打开的文件。 |
lchown |
与 chown 类似,但是在处理符号链接时,操作的是符号链接本身,而不是其指向的文件。 |
chroot |
改变根目录,将当前进程的根目录更改为指定的目录,常用于创建一个隔离的文件系统环境。 |
stat |
取文件状态信息,获取文件的详细属性,如文件大小、修改时间、权限等。 |
lstat |
与 stat 类似,但是在处理符号链接时,返回的是符号链接本身的属性,而不是其指向的文件的属性。 |
fstat |
与 stat 类似,只不过操作的是已打开的文件。 |
statfs |
取文件系统信息,获取文件系统的相关信息,如总容量、可用空间等。 |
fstatfs |
与 statfs 类似,只不过操作的是已打开的文件所在的文件系统。 |
readdir |
读取目录项,用于读取目录中的文件和子目录信息。 |
getdents |
也是用于读取目录项,与 readdir 功能类似。 |
mkdir |
创建目录,在文件系统中创建一个新的目录。 |
mknod |
创建索引节点,用于创建特殊文件,如设备文件等。 |
rmdir |
删除目录,删除一个空目录。如果目录不为空,则无法删除。 |
rename |
文件改名,将文件或目录重命名为指定的名称。 |
link |
创建链接,创建一个硬链接,指向指定的文件。 |
symlink |
创建符号链接,创建一个软链接,指向指定的文件或目录。 |
unlink |
删除链接,删除指定的硬链接或文件。如果是符号链接,则删除符号链接本身。 |
readlink |
读符号链接的值,获取符号链接指向的目标文件或目录的路径。 |
mount |
安装文件系统,将文件系统挂载到指定的挂载点,使得文件系统可以被访问。 |
umount |
卸下文件系统,卸载已挂载的文件系统,使其不再可访问。 |
ustat |
取文件系统信息,获取文件系统的状态信息,如磁盘使用情况等。 |
utime |
改变文件的访问修改时间,设置文件的最后访问时间和最后修改时间。 |
utimes |
与 utime 类似,用于设置文件的时间戳。 |
quotactl |
控制磁盘配额,用于管理用户或用户组在文件系统上的磁盘使用限制。 |
(四)系统控制函数
系统控制函数用于对系统进行各种控制操作,如设置系统参数、获取系统信息、控制设备等:
ioctl |
I/O 总控制函数,用于对设备进行各种控制操作,如设置设备参数、获取设备状态等。 |
_sysctl |
读/写系统参数,用于读取和修改系统的内核参数,如网络参数、内存参数等。 |
acct |
启用或禁止进程记账,用于记录进程的资源使用情况,如 CPU 时间、内存使用等。 |
getrlimit |
获取系统资源上限,获取进程对某种系统资源(如文件描述符数量、内存大小等)的限制。 |
setrlimit |
设置系统资源上限,设置进程对某种系统资源的限制。 |
getrusage |
获取系统资源使用情况,获取进程或系统的资源使用统计信息,如 CPU 时间、内存使用等。 |
uselib |
选择要使用的二进制函数库,用于指定进程使用特定的函数库。 |
ioperm |
设置端口 I/O 权限,设置进程对特定端口的 I/O 访问权限。 |
iopl |
改变进程 I/O 权限级别,提升或降低进程的 I/O 权限级别,使其能够访问特定的 I/O 端口。 |
outb |
低级端口操作,用于对端口进行低级的读写操作。 |
reboot |
重新启动,用于重新启动系统。 |
swapon |
打开交换文件和设备,启用交换空间,将内存中的数据交换到磁盘上,以释放内存空间。 |
swapoff |
关闭交换文件和设备,禁用交换空间,将磁盘上的交换数据换回内存。 |
bdflush |
控制 bdflush 守护进程,用于管理磁盘缓冲区的刷新操作,确保数据及时写入磁盘。 |
sysfs |
取核心支持的文件系统类型,获取内核支持的文件系统类型列表。 |
sysinfo |
取得系统信息,获取系统的基本信息,如内存大小、CPU 数量等。 |
adjtimex |
调整系统时钟,用于调整系统的时间和时间间隔。 |
alarm |
设置进程的闹钟,设置一个定时器,在指定的时间后向进程发送 SIGALRM 信号。 |
getitimer |
获取计时器值,获取进程中指定类型的计时器的值。 |
setitimer |
设置计时器值,设置进程中指定类型的计时器的值和选项。 |
gettimeofday |
取时间和时区,获取当前的时间和时区信息。 |
settimeofday |
设置时间和时区,设置系统的时间和时区。 |
stime |
设置系统日期和时间,设置系统的日期和时间。 |
time |
取得系统时间,返回当前的系统时间,以秒为单位。 |
times |
取进程运行时间,获取进程的用户时间、系统时间和总运行时间。 |
uname |
获取当前 UNIX 系统的名称、版本和主机等信息,返回系统的标识信息。 |
vhangup |
挂起当前终端,挂起当前的终端会话。 |
nfsservctl |
对 NFS 守护进程进行控制,用于管理 NFS 服务器的守护进程,如启动、停止、重新加载等操作。 |
vm86 |
进入模拟 8086 模式,使进程进入模拟 8086 处理器的模式,用于兼容一些旧的软件。 |
create_module |
创建可装载的模块项,用于创建一个可装载的内核模块。 |
delete_module |
删除 |
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
一些八股模拟拷打Point,万一有点用呢