共享进程就是多个进程都可以使用的,但是为了不影响进程的连续性,我们的系统会把这个共享的内存映射到每一个独立的进程空间里面去;
按照下面的这个图进行理解,我们的这个共享内存会映射到这个AB里面各一份,AB是两个各自独立的进程,这个时候,AB对于映射到自己进程的这个小的空间进行操作就是相当于对于这个大的共享内存进行操作;
其实整个列成只需要4个步骤:
但是第四个需要根据实际情况决定是否要删除,如果多个进程都要使用,删除这个共享内存之后,可能某些进程就会受到影响;
key(键值)指的就是共享内存的一个编号,是在内核层面进行标识的;
但是在应用层上面,我们使用的是标识符(IPC_CREATE)进行标记的;
size就是这个共享内存的大小;
其中这个函数参数的第三个IPC_CREAT就是指的当我们创建共享内存单元的时候,如果这个共享内存单元不存在,我们就创建,已经存在的话,我们就尝试去访问打开这个共享内存;
返回的就是应用层的共享内存的描述符,就是上面说到的这个identifier;
shmat把共享内存的标识符映射到调用的进程的地址空间里面,实际上就是一个映射的过程;
第一个参数就是标识符,我们把这个共享内存映射到进程的哪一个地方,我们可以指定,需要给出来具体地址,但是也可以使用null让这个操作系统给我们分配位置;
第三个参数,就是这个shmflg,如果我们写成0,就是进程对于这个映射过来的共享内存具有读写权限,当然,我们可以设置为只读或者是只写的权限,但是我们写成0就可以了;
返回值,表示的映射的地址空间的首地址,就是这个进程里面映射区域的首地址;
shmat函数就是用来进行这个解除共享内存在这个进程里面的映射,从而让这个共享内存在这个映射区域里面不会发挥作用;
上面的这个是父子进程,两个是有亲缘关系的,但是这个时候我们的两个进程是完全独立运行的,两个之间没有任何的关系;
就是我们上面的这个进行共享内存创建的时候,使用的是这个shmget函数的IPC_PRIVATE参数,就这个参数而言,父进程和子进程是相同的,因为这个子进程完全拷贝我们的父进程的代码和数据
但是如果是两个不相关的进程,我们肯定是一个进程进行read操作,一个进程进行write操作,这个read进程把这个写的内容进行读取,这个时候我们的两个进程里面肯定是要各自去创建共享内存的,而且这个shmget函数的第一个参数需要是一样的,否则我们的一个进程写进去之后,另外一个进程根本无法进行读取;
上面的是因为父子进程的这个第一个参数是一样的,所以这个可以使用默认值,他们也可以找到写入的数据,但是这个没有亲缘关系的两个进程,如果不进行这个参数的指定,读的进程是根本找不到这个内从写到哪里呢,就也不知道去哪里读,这个就是和上面的父子进程最大的区别;
负责写内容的进程,可以看到这个里面是设置了一个宏常量进行这个参数的制定的,而且两个进程的这个参数值是一样的,这样这个数据才可以被正常的读取;
负责读取内容的进程:
描述符标识符就是这个函数的返回值,返回值和我们的这个key相互关联,这个key是在我们的内核层,就是键值,应用层使用的就是identifier进行区分;
下面的这个msgget函数就是创建一个消息队列,我们的父进程使用一个函数megsnd发送消息的内容到这个队列里面去,我们的子进程通过一个函数msgrcv读取这个父进程放到这个消息队列里面的内容;
非亲缘关系的进程之间的这个消息的传输也是可以进行的,只要我们的这个接收端前往和发送端的消息号相同的消息队列里面去读取内容就是可以成功的;
也就是这个msgrcv函数里面的倒数第二个参数需要我们的发送端的这个消息数据的类型号是一样的,这样才可以保证消息的准确传输;
发送端的进程:
接收端的进程:
管道,共享内存和消息队列都是进行这个数据的传输的,一个资源想要被多个进程访问,就是进行同步,信号量就是进行任务之间的同步;
信号量就是一个整数值,A访问资源的时候,检查信号量是不是大于0,B进程也想要访问这个内存,B进程也是需要检查这个进程的信号量是不是大于0的,这个时候进程B处于阻塞状态,直到这个信号量大于0,才会继续运行;
信号量进行工作的时候,如果被设置为1,一个进程访问之后,就会被消耗为0,这个时候其他的进程想要访问这个内存的时候,其他的想要访问的进程就会处于阻塞的状态;
我们的这个信号量也是划分为无名的信号量和有名的信号量,这个和我们当时学习的管道是一样的逻辑,因为我们的管道也是划分为这个有名管道和无名管道的;
对于这个进程的控制,因为这个信号量是可以进行消耗和发布的,例如这个信号量只是1,我们的父进程使用这个资源,消耗掉了这个信号量,这个时候的父进程就会处于阻塞的状态,因为只有这个信号量大于0 的时候这个才是可以正常运行的,这个时候,我们的子进程负责发布信号量,这个时候我们的子进程什么时候发布这个信号量,我们的父进程就会从这个阻塞状态变为正常的运行状态;
因此这个子进程发布信号量的时刻决定了这个父进程从阻塞状态转换为正常运行状态的时刻,因此这个就间接的实现了我们的子进程对于父进程的控制;
sem_init函数的用法就是创建信号量,用来被其他的进程使用;
sem_wait函数的用法就是检查这个信号量是不是大于0,如果大于0,我们的进程就可以使用,如果不是我们就需要被处于阻塞状态;
sem_post函数作用就是进程发布信号量,让处于阻塞的进程正常运行;
mmap函数就是产生虚拟的地址空间,让这个父进程和子进程共享这个生成的地址空间,从而让这个子进程增加的信号量可以被子进程看到,否则我们的子进程发布的信号量无法被父进程看到;
这个代码就是综合上面的函数以及这个父子进程的这个行为,实现的子进程增加信号量,当我们的父进程因为消耗掉信号量处于阻塞状态的时候,能够看到这个子进程的发布而让这个阻塞的进程运行起来;
创建一个父进程,消耗信号量,创建一个子进程,发布信号量控制第一个进程的状态:
这个主要介绍一下区别,就是我们的进程之间是相互独立的,因此我们需要使用这个mmap函数创建一个寻你的空间让两个进程之间的信号量可以相互看到,但是对于进程而言,多个线程公用一个进程的资源,因此这个是可以直接进行这个信号量的传递的,不需要使用这个mmap函数进行信号量的虚拟地址的开辟,两个线程使用的这个信号量本来就是属于相同的进程的,因此他们是可以相互看到的,不需要使用这个mmap函数;
下面的这个代码就是调用这个pthread函数进行现成的创建,使用这个sem_wait函数消耗信号量,使用sem_post去发布信号量;