学习系统编程No.16【进程间通信】

简介: 学习系统编程No.16【进程间通信】

引言:

北京时间:2023/4/9/20:44,昨天,也就是这个周末的星期六,就是传说中的蓝桥杯,哈哈哈!摆烂,做题方面真不怎么行,可惜,当初可能是年少轻狂或者说是没什么经验阅历,希望在有了这次的经历之后,明年的今天,能够更加从容吧!谁让我们平时不怎么做题呢?准确的来说是没什么做题的习惯,也可以说是没什么时间做题,虽然我从小就知道时间像海绵里的水,挤一挤总是有的,但一个懒字了的,例如,蓝桥杯回来,本可以直接开始写博客,但是直接摆烂到了现在,算了,主要是没什么太大的压力(这个可能就是当代大一学生的现状,没目标,没压力),这种情况下,一个人有100中说服自己的理由摆烂,我也不例外,哈哈哈!但,还是那句话,摆烂归摆烂,不放弃就行,有小目标就好,哈哈哈!当然,像我其实压力还是很大的,最直接的一点就是CSDN上各路大佬坚持更新,我们自然而然也不甘屈居人后啦!所以导致我还是比较容易上道!所以今天我们就来学习一下有关进程间通信的知识和上篇博客有关动静态库的知识吧!


回顾动静态库

上篇博客我们了解到了,动态库由于使用的时候,不是把库中的所有数据拷贝到目标程序中,而是通过头文件的形式被调用,所以只有编译器可以识别到动态库路径,而操作系统不能识别,这样就会导致动态库在使用时,也就是可执行程序要被执行时,操作系统出现链接错误,并且了解到,为了解决这个问题,有三种解决方式,如下:


1.将库路径导入到环境变量中(临时)

2.软连接(永久)

3.配置文件方案


并且上篇博客,我们已经了解了前面两种,导入环境变量和建立软链接,所以接下来就让我们学习一下第三种,配置文件方案的方式吧!并且复习一下前两种,导入环境变量和创建软链接:


导入环境变量

也就是将动态库所在的路径导入到操作系统可以默认识别到的环境变量之中,因为本质就是为了让动态库所处的路径可以被操作系统找到,此时这个环境变量为:&LD_LIBRARY_PATH,所以此时想要让操作系统找到动态库路径,只要把动态库路径导入到这个环境变量中,就行,指令:export LD_LIBRARY_PATH=&LD_LIBRARY_PATH:"动态库所在的路径",所以当我们把动态库所处的路径导入到了这个环境变量中之后,操作系统此时就可以找到对应的路径了,如下图所示:


image.png


建立软链接

还是明白,本质上就是为了让操作系统可以找到动态库所处路径,所以建立软链接的本质就是给动态库所处的路径建立一个在操作系统默认可以识别到的路径下的映射文件,指令:sudo ln -s /home/vimtest/其他人的动静态库/我的动态库/lib/libmymath.so /lib64/libmymath.so,如下图:


image.png

表示的就是,把动态库所处的路径在操作系统默认可以识别的lib64路径下建一个libmymath.so的软链接映射文件

如下图:此时操作系统默认就可以找到动态库所处的路径了

image.png


配置文件方案

首先还是明白,不管是那种方式,它的本质都是为了可以让操作系统找到动态库所在的文件路径,所以配置文件的方式,同理,如下图所示,并且注意:在Linux系统下,默认存在一个配置文件路径


image.png


并且明白,我们是可以在该配置文件路径下,创建自己的文件,指令:sudo touch /etc/ld.so.conf.d/bit_107.conf

image.png

如上图,我们就可以发现,在系统的配置文件路径下,此时就有了bit_107.conf这个文件,所以此时我们只需要把动态库所在的路径写入该文件中,如下图:

image.png

并且最后将这个配置文件的路径重新激活,指令:sudo ldconfig,此时就可以让动态库所在的路径被操作系统识别到了,如下图所示:

image.png

总:通过上述三种方式,此时就可以明白,操作系统在执行一个可执行程序的时候,有三种方式去寻找相应的动态库路径



深入动静态库内部

首先明白,上述的知识和上篇博客的知识,本质上只是动静态库在操作方面的知识,所以此时就让我们深入动静态库的内部,具体的看一看动静态库吧!(理论)


本质上还是动静态库在使用特征上的不同:使用静态库时,目标程序需要把整个静态库的实现给拷贝到程序中,而动态库则是通过头文件中函数接口的声明,间接使用动态库中函数接口的实现,并且动态库是可以通过条件判断来区分被使用了的函数接口和没被使用的函数接口,达到精准使用的能力,不需要盲目拷贝


当明白了上述知识,此时轻而易举就可以知道 ,当静态库加载到了程序之中,那么此时就会非常的占用资源,无论时空间资源,还是网络资源,而动态库在使用的时候,由于只是将头文件加载到了目标程序,而整个动态库的实现是在程序被执行的时候才加载到内存之中(因为本质上是二进制文件而已),所以此时动态库的使用将不会那么的占用资源,如下图所示:


静态库


image.png


动态库

动态库链接到目标程序,此时并没有拷贝动态库中的具体内容,而只是将动态库所对应的接口实现的地址通过头文件和条件判断替换到相应目标程序所对应的外部符号而已,具体过程如下图所示:


image.png


总的来说,就是在使用动态库的时候,不仅需要将目标程序加载到内存,还需要将动态库也加载到内存

6TFDD

并且还要 注意 ,根据上图,我们可以知道,物理内存中已经存放着相应的动态库了,如果此时别的程序,也就是别的进程,也同样需要用到该动态库,那么此时就当操作系统识别到内存中已经有相应的库,此时就不需要重新从磁盘中加载,而是直接将动态库通过页表映射到自己地址空间的共享区中就行,达到一库多用的作用,这样就可以大大的节省资源,无论是操作系统效率方面还是时空方面


如何理解动态库地址(fPIC

搞定了上述有关,动静态库在内存中的是如何调用的之后,此时我们就再深入了解一下动静态库具体的寻址方式,目的就是为了理解fPIC(与位置无关码)的相关知识,如下述所说:


首先第一点要明白,一个程序是允许同时存在多个地址的,就类比为,一个人是允许有多个名字或者多个编号,学号、宿舍号、身份证号,明白了这点之后,显然就可以知道,一个程序肯定是存在不同的地址的,例,磁盘中的逻辑地址或者物理内存中的物理地址或者是虚拟地址空间中的虚拟地址,也就是表明,一个客观存在的物体或者人,在不同的场景之下,就会存在不同的属性,这个属性可能是形态上的,也可能是逻辑上的,明白了这点,就让我们继续深入地址吧!


明白了上述的知识,知道一个程序可以有不同的地址,逻辑地址,物理地址,虚拟地址,并且明白,这些不同的地址之所有有不同的名字,只是因为程序所处的场景不同而已,所以总的来说程序有很多地址,并且这写地址会因为场景不同而不同,但是总的来说,地址本质上只有两类,一类是绝对地址(静态库),一类是相对地址(动态库),并且明白虚拟地址空间中的编址属于绝对地址


理解绝对地址和相对地址:

相信我们以前都了解过什么是绝对路径,什么是相对路径,同理,在编址的时候,地址也是一样的,绝对地址就是像物理内存中的物理地址一样,内存中的每一个字节都有属于自己的编号(地址),这个就叫绝对地址,而相对地址,就像是磁盘中的逻辑地址一样,以一个固定的点或者值为参照,然后通过偏移量的形式来进行编址,这个就是相对地址


为什么动态库使用相对编址:

无论是从上篇博客知识,还是上述知识,此时我们都明白,一个目标程序在链接静态库的时候,是直接将静态库中所有的接口实现给拷贝到目标程序中,所以此时进程虚拟地址空间通过页表映射物理内存的时候,虚拟地址空间上存放的就是通过页表映射的物理内存上目标程序完整代码,使用绝对地址存放毫无关系,只要操作系统能找到就行;而动态库由于不同的进程,运行程度不同或者是运行的程序不同,需要使用的动态库接口数量上或者接口实现上不同,或者是需要使用的第三方库不同,所以注定每一个目标程序的虚拟地址空间上存储的动态库不同,每一个进程的地址空间不同,每一个进程的共享区间上存储的接口实现代码不同,所以动态库不允许使用绝对地址,而要使用相对地址,具体理由如下:


动态库不能使用绝对编址是因为动态库在被加载时,会被映射到进程的地址空间中,并且动态库是被多个程序共享的,而每个程序的内存地址空间都是不同的,所以动态库不能使用绝对编址是因为动态库是在被加载到进程的内存空间中时确定其最终的内存位置的,而不同进程的内存地址空间不同(可执行程序不同),因此,如果动态库使用绝对地址进行编址,那么在加载该库的不同进程中,其代码和数据可能会被映射到不同的内存地址上。这会导致代码无法在多个进程之间共享,因为其中一个进程中的地址无法被其他进程正确解释。因此,动态库必须采用相对地址,以确保其代码和数据可以在所有进程中使用相同的偏移量进行访问


如下图所示:


image.png



如上图,就是为什么要使用相对路径,而不是绝对路径,并且注意:动态库中的所有函数接口实现的地址都是偏移量,并且默认从0开始并

究极理解:

所以本质上动态库不允许使用绝对地址,就是因为地址空间和内存地址的映射关系是对应的,而地址空间上的动态库由于程序代码的不同,导致动态库不同,但又因为进程之间是共用一份动态库,所以导致地址空间上动态库的接口也必须相同,否则就不能通过对应的映射关系找到内存上的那份唯一的动态库数据,所以此时地址空间上就不能使用绝对地址,从而使用相对地址,因为使用相对地址,此时就可以通过动态库的起始位置加偏移量(只要各个进程使用的接口实现是相同的,偏移量就相同),所以调用时,调用的动态库的接口就相同,最后再根据映射关系,访问到的内存上的动态库的接口实现就相同


所以得出结论:


总:这就是为什么要使用fPIC的原因


反正终极目的就是理解动态库如何实现局部加载,相对地址的概念


测试动静态库的具体使用场景

明白了上述的知识,此时动静态库有关操作和理论方面的知识我们就搞定了,此时我们就再深入几个场景看看,动静态库具体还有什么特点,首先要明白,一个庞大的程序,是拥有非常多的代码和数据的,此时就不仅需要有动态库的实现,此时还要有静态库的实现,所以大部分的程序都是静态库和动态库同时使用来完成的,但是一般一些小的程序 ,仅仅使用动态库或者静态库就能完成,所以假如此时有一个动态库和一个静态库,并且这两个库中的函数接口实现都是目标程序需要的,那么此时系统是链接静态库还是链接动态库呢?如下图所示:


image.png


所以此时我们知道,当一个库中同时有静态库和动态库时,此时程序优先调用的是动态库,那么我相信,大家此时肯定有一个疑问,那就是那么如何优先调用静态库呢?如下图所示:

image.png


所以我们就知道了,当一个库中同时拥有动态库和静态库时,虽然默认的是动态库,但是我们依然可以优先调用静态库,如下图通过查看文件的属性,此时我们也可以看出程序链接的是动态库,还是静态库,具体指令:

gcc -o mytest-d main.c -I include -L lib -l mymath

gcc -o mytest-s main.c -I include -L lib -l mymath -static


image.png


总的来说就是有什么用什么,但是默认动态库优先,没有动态库才链接静态库

进程间通信


image.png

进程间通信发展

1.管道 2.System V进程间通信 3.POSIX进程间通信

进程间通信分类

1.管道 2.匿名管道pipe 3.命名管道


详细知识下篇博客见



总结:动静态库的知识真的非常多,终于全部搞定了,难搞呀!接下来,就让我们再谈进程吧!进程间通行详解,下篇博客见啦!

相关文章
|
24天前
麒麟系统mate-indicators进程占用内存过高问题解决
【10月更文挑战第7天】麒麟系统mate-indicators进程占用内存过高问题解决
110 2
|
2月前
|
监控
MASM32写的免费软件“ProcView/系统进程监控” V1.4.4003 说明和下载
MASM32写的免费软件“ProcView/系统进程监控” V1.4.4003 说明和下载
|
26天前
麒麟系统mate-indicators进程占用内存过高问题解决
【10月更文挑战第5天】麒麟系统mate-indicators进程占用内存过高问题解决
99 0
|
2月前
|
监控 Ubuntu API
Python脚本监控Ubuntu系统进程内存的实现方式
通过这种方法,我们可以很容易地监控Ubuntu系统中进程的内存使用情况,对于性能分析和资源管理具有很大的帮助。这只是 `psutil`库功能的冰山一角,`psutil`还能够提供更多关于系统和进程的详细信息,强烈推荐进一步探索这个强大的库。
40 1
|
2月前
|
安全 开发者 Python
揭秘Python IPC:进程间的秘密对话,让你的系统编程更上一层楼
【9月更文挑战第8天】在系统编程中,进程间通信(IPC)是实现多进程协作的关键技术。IPC机制如管道、队列、共享内存和套接字,使进程能在独立内存空间中共享信息,提升系统并发性和灵活性。Python提供了丰富的IPC工具,如`multiprocessing.Pipe()`和`multiprocessing.Queue()`,简化了进程间通信的实现。本文将从理论到实践,详细介绍各种IPC机制的特点和应用场景,帮助开发者构建高效、可靠的多进程应用。掌握Python IPC,让系统编程更加得心应手。
31 4
|
2月前
|
Linux C语言
C语言 多进程编程(四)定时器信号和子进程退出信号
本文详细介绍了Linux系统中的定时器信号及其相关函数。首先,文章解释了`SIGALRM`信号的作用及应用场景,包括计时器、超时重试和定时任务等。接着介绍了`alarm()`函数,展示了如何设置定时器以及其局限性。随后探讨了`setitimer()`函数,比较了它与`alarm()`的不同之处,包括定时器类型、精度和支持的定时器数量等方面。最后,文章讲解了子进程退出时如何利用`SIGCHLD`信号,提供了示例代码展示如何处理子进程退出信号,避免僵尸进程问题。
|
2月前
|
消息中间件 Unix Linux
C语言 多进程编程(五)消息队列
本文介绍了Linux系统中多进程通信之消息队列的使用方法。首先通过`ftok()`函数生成消息队列的唯一ID,然后使用`msgget()`创建消息队列,并通过`msgctl()`进行操作,如删除队列。接着,通过`msgsnd()`函数发送消息到消息队列,使用`msgrcv()`函数从队列中接收消息。文章提供了详细的函数原型、参数说明及示例代码,帮助读者理解和应用消息队列进行进程间通信。
|
2月前
|
缓存 Linux C语言
C语言 多进程编程(六)共享内存
本文介绍了Linux系统下的多进程通信机制——共享内存的使用方法。首先详细讲解了如何通过`shmget()`函数创建共享内存,并提供了示例代码。接着介绍了如何利用`shmctl()`函数删除共享内存。随后,文章解释了共享内存映射的概念及其实现方法,包括使用`shmat()`函数进行映射以及使用`shmdt()`函数解除映射,并给出了相应的示例代码。最后,展示了如何在共享内存中读写数据的具体操作流程。
|
2月前
|
监控 API
【原创】用Delphi编写系统进程监控程序
【原创】用Delphi编写系统进程监控程序
|
2月前
|
Linux C语言
C语言 多进程编程(七)信号量
本文档详细介绍了进程间通信中的信号量机制。首先解释了资源竞争、临界资源和临界区的概念,并重点阐述了信号量如何解决这些问题。信号量作为一种协调共享资源访问的机制,包括互斥和同步两方面。文档还详细描述了无名信号量的初始化、等待、释放及销毁等操作,并提供了相应的 C 语言示例代码。此外,还介绍了如何创建信号量集合、初始化信号量以及信号量的操作方法。最后,通过实际示例展示了信号量在进程互斥和同步中的应用,包括如何使用信号量避免资源竞争,并实现了父子进程间的同步输出。附带的 `sem.h` 和 `sem.c` 文件提供了信号量操作的具体实现。