动态库的加载
动态库是一种在运行时被加载到进程中的库。与静态库不同,静态库在编译时被链接到应用程序中,而动态库则在运行时被加载。这允许我们在不重新编译整个应用程序的情况下更新库。
在Linux中,动态库通常以.so
(共享对象)为扩展名。在Windows中,它们被称为DLL(动态链接库),并以.dll
为扩展名。
示例(Linux环境下):
假设我们有一个名为libexample.so
的动态库,其中包含一个函数int add(int a, int b);
。
首先,我们需要编写动态库的源代码。例如,example.c
:
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
然后,使用gcc
编译为动态库:
gcc -shared -o libexample.so example.c
使用动态库:
编译选项 l:链接动态库,只要库名即可(去掉lib以及版本号) L:链接库所在的路径. 示例:
gcc main.o -o main –L. -example
进程之间通信--管道
在操作系统中,进程间通信(IPC, Inter-Process Communication)是一种允许不同进程之间交换信息或数据的机制。管道(Pipe)是IPC机制中的一种,主要用于具有亲缘关系的进程之间(如父子进程)的通信。管道是一种半双工的通信方式,数据只能单向流动,并且只能在具有共同祖先的进程间使用。
管道的概念
管道是一种特殊的文件,它存在于内核中,用于实现进程间的通信。管道有两个端点,一个用于写入数据(写端),另一个用于读取数据(读端)。数据从写端流入,从读端流出。管道对于管道两端的进程而言,就像一个文件,但它不是普通的文件,而是只存在于内存中的临时文件。
管道有以下特点:
- 半双工通信:数据只能单向流动,即数据只能从写端流向读端。
- 有界性:管道的大小是有限的,当管道写满时,再写入数据会阻塞或失败。
- 生命周期:管道的生命周期随进程,当进程结束时,管道也被销毁。
- 匿名性:管道没有名字(也称为无名管道),因此它只能用于具有亲缘关系的进程间的通信。
管道的创建与使用
在Unix/Linux系统中,可以使用pipe
系统调用来创建管道。pipe
函数原型如下:
#include <unistd.h>
int pipe(int fd[2]);
其中fd
是一个包含两个文件描述符的数组,fd[0]
是读端,fd[1]
是写端。如果pipe
调用成功,则返回0;否则返回-1并设置errno
。
管道通信示例
下面是一个简单的示例,演示了如何使用管道在父子进程间进行通信:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main() {
int fd[2];
pid_t pid;
char buffer[128];
// 创建管道
if (pipe(fd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
// 创建子进程
pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) { // 子进程
// 关闭读端
close(fd[0]);
// 向写端写入数据
const char *message = "Hello from child!";
write(fd[1], message, strlen(message) + 1);
// 关闭写端
close(fd[1]);
_exit(EXIT_SUCCESS);
} else { // 父进程
// 关闭写端
close(fd[1]);
// 从读端读取数据
ssize_t bytesRead = read(fd[0], buffer, sizeof(buffer) - 1);
if (bytesRead > 0) {
buffer[bytesRead] = '\0'; // 确保字符串以null结尾
printf("Received from child: %s\n", buffer);
}
// 关闭读端
close(fd[0]);
wait(NULL); // 等待子进程结束
}
return 0;
}
在这个示例中,我们首先使用pipe
系统调用创建了一个管道。然后,我们创建了一个子进程。在子进程中,我们关闭了读端(因为我们不需要从管道中读取数据),然后向写端写入了一条消息。在父进程中,我们关闭了写端(因为我们不需要向管道中写入数据),然后从读端读取子进程发送的消息,并打印出来。
注意,在读取和写入完成后,我们都关闭了相应的文件描述符,以释放资源。同时,在父进程中,我们使用了wait
系统调用来等待子进程结束,以确保所有资源都被正确清理。