进程间通信的七种方式(二)

*****************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结构中的uidgidmode复制到共享内存的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分成多个选项,然后传递给链接程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值