*****************day06进程间的通信方式**************
【1】共享内存-----进程间最高效的通信方式
定义:
共享内存进程间通信机制主要用于实现进程间的大量数据的传输。共享内存是在内存中单独开辟的一段内存空间,这段内存空间有自己特有的数据结构,包括访问权限,大小和最近的访问的时间。
两个进程在使用此共享内存空间之前,需要在进程地址空间和共享内存空间之间建立联系,即将共享内存空间挂载到进程中。
(**)在使用共享内存进行数据存取时,有必要使用二元信号量来同步两个进程以实现对共享内存的写操作。
注意:
共享内存在父子进程间遵循以下的约定
1.使用fork函数创建一个子进程后,该进程继承父进程挂载的内存共享
2.如果调用exec执行一个新的程序,则所有挂载的共享内存将被自动卸载
3.如果在某个进程中调用了exit()函数,所有挂载的共享内存将与当前进程脱离关系。
步骤:
1.ftok---使2个不相关进程建立外部联系.
2.shmget---创建或者打开共享内存(share memory get)
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
函数的功能:创建或者打开一个共享内存
参数:key 外部键值
size 设置共享内存大小
shmflg 标志位 IPC_CRAT (flag)
IPC_EXCL
0664
返回值:成功返回共享内存的标识符 失败:-1
3.shmat---将共享内存映射到进程各自的地址空间中(连接共享内存)
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
函数的功能:将共享内存映射到各自进程空间
参数:shmid 进程标识符
shmaddr 映射后进程的地址
shmflg 0 可读可写
SHM_RDONLY 只读
返回值:成功返回映射后地址,失败NULL===(void *)-1
4.shmdt----解除映射
int shmdt(const void *shmaddr);
函数的功能:解除映射
参数:shmaddr 映射的地址
返回值:成功返回0,失败-1
4.shmctl----删除共享内存(share memory control)
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
函数的功能:删除共享内存
参数:shmid 共享内存的标识符
cmd IPC_STAT :得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中.
IPC_SET :改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构内.
IPC_RMID :删除这片共享内存。
buf 共享内存的属性信息 NULL
返回值:IPC_RMID 成功返回0,失败-1
例子:
要求:2个不相关的进程1_shmA.c;2_shmB.c
要求利用共享内存实现1向2发送信息,2接收消息
【2】信号灯集----同步互斥机制
定义:
信号灯集里面有多个信号量(灯),至少有一个信号量。一般与共享内存搭配使用。信号灯集又一个缺点就是创建和初始化不能原子操作。初始化和创建各自独立完成。
注意:信号灯集虽然作为ipc通信方式的一种,我们可以理解成一种同步机制,用于进程间通信。
步骤:
1.ftok
2.创建信号灯集
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);(semaphore get)
函数的功能:创建信号灯集
参数:key 键值
nsems 信号量的个数(几类资源)
semflg IPC_CREAT
IPC_EXCL
IPC_CRAT|IPC_EXCL|0664
返回值:成功返回信号灯集的标识符,失败-1
3.初始化信号灯里面的信号量
int semctl(int semid, int semnum, int cmd, ...); (semaphore control)
函数的功能:初始化信号量
参数:semid 信号灯集的标识符
semnum 要初始信号量的名称
cmd SETVAL,IPC_SAT,IPC_SET,GETALL, SETALL,IPC_INFO选择以下任一一种会有第四个参数,共用体
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
共用体:union +共用体名称
空间成员变量共享
空间的大小--最大成员变量所占的字节数决定
返回值:成功返回0,失败-1
4.操作信号灯(p,v操作)
int semop(int semid, struct sembuf *sops, unsigned nsops);
函数的功能:操作信号量
参数:semid 信号灯集的标识符
sops 指针 指向一个结构体
struct sembuf
{
unsigned short sem_num; /* semaphore number */ 操作的信号量的编号
short sem_op; /* semaphore operation */
-1 减一操作 申请资源 p
1 加一操作 释放资源 v
short sem_flg; /* operation flags */ 0 阻塞
IPC_NOWAIT 非阻塞
}
nsops 操作信号量的数量
返回值:成功返回0,失败-1
5.销毁信号集
int semctl(int semid, int semnum, int cmd, ...);
函数的功能:销毁信号量
参数:shmid 信号灯集标识
cmd 命令
IPC_SET
IPC_STAT
IPC_RMID
IPC_INFO
buf 指向一个结构体,用于保存信号灯属性信息 NULL 默认
返回值:成功返回0,失败-1
例子:当前进程中创建父子进程,父进程向共享内存中写,子进程从共享内部中读
信号灯集控制父子进程操作共享内存--保持同步
(3)静态库和动态库
库的定义:本质上说库是一种可执行的二进制的形式,可以被操作系统载入内存执行,
由于windos和linux的本质的不同,因此二者的库是不兼容的
linu下有2种库,静态库和动态库(共享库),二者的不同在于代码被载入的时刻的不同。
静态库:在程序编译时被链接到目标代码,程序运行时不再需要改静态库,因此体积较大
动态库:程序在编译的时候不会被连接到目标代码,而是在程序执行的时候才被载入,
因此程序运行的时候还需要动态库的存在,因此代码的体积比较小。
掌握如何制作静态库,和如何使用它
掌握如何制作动态库,和如何使用它
基本知识:
<1>.动态库和静态库的区别:
(1)静态库文件以.a为后缀,动态库文件以.so为后缀。
(2)静态库全部编译进可执行文件中;而动态库只编译进库的一些索引声明信息,在链接时才会临时加载库。故静态库编译生成的可执行文件体积较大。
(3)由于(2)的特点,再编译完后若将库删除,则动态库编译生成的可执行文件执行时会提示找不到库文件,而静态库编译生成的可执行文件依然可以执行。
(4)静态库移植性优于动态库。
<2>.静态库演示:删除库仍然可以运行
<3>.静态库的制作方法:
(1)gcc -c add.c //生成目标文件add.o
(2)ar crs -o libadd.a add.o //生成静态库文件 libadd.a
(3)gcc main.c -L. -ladd //因为gcc默认只会去寻找默认目录下的标准glibc库,所以我们需要指定链接第三方库
<4>.动态库演示:删除库运行失败;可以通过修改库而不重新编译程序来做到改变进程实现效果。
<5>.动态库制作方法:
(1)gcc -c add.c
(2)gcc -fPIC -shared -o libadd.so add.o //生成共享库文件 libadd.so
(3)gcc main.c -L. -ladd
注意:易出现问题及应对方法
问题:编译时找不到动态库文件libadd.so ?
解决方法:
(1) 把库拷贝到/usr/lib或/lib目录下。 (常用)
(2) 在LD_LIBRARY_PATH环境变量中加上库所在路径。
例如动态库libhello.so在/home/ting/lib目录下,以bash为例,使用命令:
$export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ting/lib
(3) 修改/etc/ld.so.conf.d/目录下的.conf文件,把库所在的路径加到文件末尾,并执行ldconfig刷新
注意:ldd命令可以查看可执行程序依赖的共享库
<6>gcc的一些参数
-shared:指定生成动态链接库。
-static:指定生成静态链接库。
-fPIC:表示编译为位置独立的代码,用于编译共享库。目标文件需要创建成位置无关码,概念上就是在可执行程序装载它们的时候,它们可以放在可执行程序的内存里的任何地方。
-L.:表示要连接的库在当前目录中。
-l:指定链接时需要的动态库。编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称。
-Wall:生成所有警告信息。
-ggdb:此选项将尽可能的生成gdb的可以使用的调试信息。
-g:编译器在编译的时候产生调试信息。
-c:只激活预处理、编译和汇编,也就是把程序做成目标文件(.o文件)。
-Wl,options:把参数(options)传递给链接器ld。如果options中间有逗号,就将options分成多个选项,然后传递给链接程序。