花钱买不到系列-linux进程信号

        下面正式进入今天的话题。下一个啊,我们叫做进程间通信,还有一种通信方式叫做消息队列,那么说一下啊。system V版本的进程通信,那么这种通信方式呢?本来不想说的了,但是后来还是继续分享出来,主要是讲这种通信方式呢,尤其是上面共享内存,那么归根结底其实不是为了讲它,而是让大家知道有这些通信的这个方式。因为它说实话,其实并不是特别重要,在大部分情况下呢,我们在进行网络编程,后面的系统编程的时候呢,它很少出场。        

        主要是因为它呢,这种通信方式确实是操作系统单独给我们设计出来的一套通信方案。但是呢,它的那个共享内存标识符以及我们一会儿可能还会谈的消息队列标识符或者信号量标识符,它和我曾经分享过的的文件兼容性并不是特别好,我们一直都在谈那么linux下一切皆文件。那么我们后面,在学习网络的时候,它其实也是文件,所以网络,和我们以前讲的本地文件兼容性特别好。所以一般,进行网络编程。网络通信时,文件操作是一个非常基本的操作,也就是基本操作也是和我们对应的本地文件和我们网络。它们两个两份代码是很容易就能结合在一起的,但是进入间通信呢,它不一样,它的这个那么共享内存对应的标识符呢,包括我们一会说的其他的标识符呢。它也属于文件描述符的一呃,也属于数组下边的一种,但是,以前学的和以后学的。兼容性特别不好。

所以一般呢,人们在选择通信方式的时候呢,往往不会优先考虑这些啊,包括我们在那么系统当中呢,那么我们在写某些代码时。那么,如果它的兼容度不是特别好,那么一般就是那些通信的代码,我们一般用的很少啊,但是我们要知道,要不然我们对于这个进程通信这个概念理解不好的话?反向,对进程本身的理解呢?也不会不会特别好啊,所以呢,这块,要注意一下,再下一个,就是那消息队列呢。那就是相当于冷门儿中的冷门儿了啊,基本上我们。就不怎么用它了啊,

就不怎么用它,然后呢,因为现在企业呢,它有非常成熟的那么啊,通信的方式,还有消息队列的方式。那么,这种系统级的通信方式呢?其实而且还是本地的,所以基本上已经属于一个那么被划分到特别不常用的范畴了。但是呢,我们把它还是得了解一下啊,下面呢,我花点时间来说一说这个消息队列。

那么消息队列呢,它是一个什么呢?它是一个操作系统,给我们提供的内核级队列,那么内核级队列呢?那么有什么样的特点呢?那么是一个什么东西呢?是一个我们内核所提供的一种队列。

通过画图理解,下面就是消息队列在内核的一个大概样子。

那么,我们上层的用户,操作系统它给我们提供了一些接口,可以让我们在系系统当中维护一个一个一个的消息队列,刚开始队列只有一个头?大家也学过数据结构队列?它就是一个我们对应的包含头结点的,或者说是包含以数据块为单位来构建的。大家可以理解成是一种啊,先进先出的一种结构。

那么这刚开始的时候,这个队列是空的,然后当你有新的数据发过来的时候,它把这个数据呢,就直接以队列的方式呢,就给你维护好了,所以未来呢,有一个人进行我们的读,有一个人呢,来进行写啊,或者呢,双方可以互为读写

我们比如说我这儿呢,想进行读取,那么它呢,想优先进行写入它,的数据可以把自己呢,那么以数据节点的方式呢,那么放到我们对应消息列当中,对方呢,就可以从消息队列当中去进行,拿我们的数据了。

因为是互为读写的,也就是我能拿你,你能拿我数据,它是用一个队列完成的,所以这个队列。它往往是需要我们有一个叫做类型的,那么这个队列当中的节点呢?实际上它要投递出来的数据,用户层面上?我们其实只需要关心两个字段就可以了。int type, char buff。

第一int type 这个类型呢?表示数据块的类型,char buff,表示要装的数据。这个类型呢?一般是自定义的,比如说呢?用"0"呢?表示这个数据,是我发的,所以呢,凡是我发出去的数据呢,我把类型设为零,凡是你发的呢,我把它都都置为一。

所那么以呢,当我们在进行通信读取的时候呢,它不会读取“1”,因为一是它写的,所以他只会读类型为“0”的数据,这就是我们内核给我们提供的一个消息地点啊。那么这个队列呢?那么我们应该怎么去进行我们对应的查看呢?那么因为它也属于system v的进程通信。所以怎么查呢?

消息队列,我们称之为msg,然后呢?那么获取消息队列,我们叫做msgget 

能够头文件包含,它和共享内存一样,来看看,标志1的位置的key这个字段见过没?这个字段是不是见过啦?叫做key也要在操作系统层面上创建一个消息队列,然后呢,msflag呢,常见设置选项就是这两个。IPC_CREAT和IPC_EXCL

跟之前写的共享内存一模一样,然后呢返回值呢?就是我们对应创建好的我,我们可以称之为叫消息队列的我们的identify表示标识符,

所以呢,你就可以使用这样的接口啊,当然呢key值怎么来,就不说了还是之前说的的ftok。就形成一个消息队列,

那么如果我们想去掉消息队列呢?那么叫做msgctl,看我之前的共享内存shmctl啊?现在呢?它叫做msgctl。是不是和共享内存一样,一样的选项。就可以删除消息队列了

消息队列呢,你也可以直接,那么获取消息队列的属性。这个地方呢,下面就是消息地列在内核当中的样子。

对应的数据结构第一个字段也叫做ipc_perm,那么其中呢,第一个字段也叫做ipc_perm,那么然后呢,有了这个结构之后呢,1指向的位置,又有了一个我们叫做大家可以理解成叫做ipc_erm,然后呢,它对应的结构体里面第一个字段也是king。所以我们创建的key就是被设置这里。

那么当然呢,消息队列和共享内存呢,属性肯定其他属性不一样,但是它的第一个字段呢,是个结构体,大家都是一样的啊。

然后呢,那么我们消息队列呢,就是要俩接口,一个叫做msgsnd。那么通过我们的应用层向我们的消息队列放出去。放数据呢,那么它一定是一个数据块的样子。

所以第一个参数呢,就是你要放的数据,第二个就是该数据的大小。第三个选项,默认设为零就可以了啊。那么消息队列呢?它设置的时候一定因为它是队列啊,

你可以理解成它是一个链式结构的,我们对应的队列结构。所以呢,它这里上面的一个一个一个的节点。

那么对应的一定是一个具有类型的数据块。所以它对应发送的数据往往的类型呢,就是这种样子的啊。叫做msbuff。

1:可以自定义这个我们消息体的大小,2:自定义对应消息类型,那么直接发到队列里就可以了。

第二个:我们还可以msgrcv,那么读取我们对应的消息队列。从该对应的那么结构体,该我们的message ID,当中读取我们指定的消息,队列数据块,然后呢?那么读取的时候呢?这个参数就是个输出型参数,你也要定义这样的结构体把数据块。根据它的大小,根据它的类型啊,你在设的时候不是把类型带进去了吗?那么根据它的类型,再把你要的数据读上来就可以了。那么很简单啊。

所以原理它就是这个原理啊。那么对我们来讲,是消息队列呢,在操作系统层面上,为了进行我们所对应的进程间通信呢,大家可以再想一想,它为了进行进程间通信呢?那么,是不是一定一定也要能够保证那么不同的进程都有可能用消息队列?所以呢,那么如果我们系统当中有多个进程都要消息队列来通信,那么势必呢,带来的一个结果呢,就是我们所对应的消息队列呢,那么也要被操作系统管理起来。

所以消息队列呢,它呢,不仅仅在内核当中出现存在这样的结构。它还要有对应的内核,数据结构用来描述这个消息队列本身,所以我们就有了,也是先描述再组织,所以才有了下面的这个结构。

这个结构里面呢,包含了消息队列,它对应的权限信息里面重要的就是这个key,还有呢,就是消息队列呢,发送最后一次发送数据的时间,最后一次接收数据的时间,然后。被改变的时间,已经累计收到了多少字节的数据,有多少个我们对应的数据块,然后呢,那么等等等啊。其中呢,那么这呢,也就是我们内核当中的相关数据结构了。

因为它不是特别重要了,所以呢,我就把它简化了说一下,然后呢,我们就不谈了啊。下面呢,我们来进入下一个话题。

信号量

叫做我们的信号量啊,好那么信号量呢,今天呢,我们也是不再讲它的代码了。

信号量的它的相关的一个原理呢,要给大家再来谈一谈啊,如果我今天呢,想根据你刚刚给我讲的,如果我现在呢,想查看一下销售队列,那么我们用的选项命令呢ips -q。

想查看我们对应的,那么接下来要讲的信号量的话呢?也是可以查的,ipcs -s。那么,这就叫做我们信号量数组啊!

当然呢,进入间通信当中呢!在system v这里,我们要进行使用信号量,信号量的话,其实成本挺高的它。它远比我们的共享内存和消息列加起来还要复杂啊,不过我正式谈之前呢,还是一样第一个首要解决的问题,应该叫做信号量是什么啊?因为那个共享内存,那就内存块儿,它没有理解成本。第二个呢?你说这个消息队列啊,那它队列结构数据结构我们都学过啊,它无非就是操作系统给我们维护的队列。

所以呢来讲,我觉得它的理解成本并不高。啊,但是呢,信号量这东西呢,它可是一个全新的概念,所以呢,那么我们首先要来理解一下信号量是什么?那么接下来我要聊的是我们关于后续的聊多线程,之前的一些概念的铺垫,所以关于信号量这儿呢,把一些概念给大家先铺设一下。

信号量正式开始。

第一个呢,信号量是什么呢,直接告诉大家结论,本质是一个计数器?它是一个计数器,那么这个计数器呢,用来表示。我们对应的公共资源中啊,那么资源数目,是用来进行我们衡量。那么它一共有多少资源?还剩多少资源?哪些可用的问题啊?为什么要有就是这个信号量,那么它是个计数器,那么我们自己定一个int count数据行不行呢?那么,在我们目前来看的话,如果我现在呢?那么想有一把公共的计数器啊,那么我们可不可以在我们多进程环境当中定义一个全局变量?

然后呢?那么用全局变量整数的方式。来让我们不同的进程,可以根据这个计数器来统计,我们某种资源呢?那么答案是不能啊,因为呢,我们的全局数据呢,那么虽然你定义出来了。但是这个全局数据呢,因为我们父子进程之间,那么它通常去需要进行写实拷贝,所以你定义全局变量,你也看不到,这是其一。

其二,如果我是两个毫不相干的进程,你想让我们俩进行,看到同一份数据根本就不可能啊,在默认情况下,你必须得引入一些通信策略。才能让我看到,那么在我们继续往下谈之前呢,还是得再铺垫几个概念,那么,第一个概念?那么今天呢?我们已经能够在通信场景当中能感受到一个概念,叫做公共资源。

那么公共资源呢?就是被进程一进程二,父进程子进程那么同时看到的这种资源呢?我们一般称之为公共资源啊。被多个进程同时可以访问的资源,我们就称之为公共资源。诸如我们之前讲的管道匿名还是命名,再比如上一篇说的共享内存,以及刚刚提过的消息队列,这些呢,都属于我们可以被多个进程同时看到的。那么,这就叫做公共资源啊,而访问公共资源或者叫做没有保护的公共资源。如果我们呢?

多个进程呢?在访问没有保护的公共资源的时候。比如说共享内存,我想向共享内存里写,那么abcd1234这个字符串,那么我在写的时候呢?假如说你在读取。那么当你在读取的时候呢?有可能我刚写了abcd那么1234还没写,此时呢,你就过来读了,因为我没有办法阻拦你读。所以呢,那么在我正在写的时候,你就来读了,可是呢,假设abcd1234那么必须被整体写入,或者整体被你读取才有意义。那么此时呢?你在我写一半,你就来读了,那么这种情况呢?就导致我们叫做数据不一致问题啊。数据不一致问题好,这是第二点。也就是说,

因为有了公共资源。然后呢,我们就可能被多个进程同时访问,因为被同时访问,所以可能会带来一个新的问题,就叫做数据不一致问题。那么,我们暂停一下啊,那么下面呢,就要再来把这个那么思路呢,再提升一个层次。那么,我们要回答或者是理解一个东西,这叫做那么我们目前为什么啊?要让不同的进程啊,

不同的进程看到同一。一份资源呢,就是我现在呢,闲了没事干,我为什么让两个地球看到同时资源呢,当然是因为我想通信。,那你为什么要通信呢?因为我可能想让一个进程把数据交给另一个进程。因为我可能想通过一个进程,想向另一个进程发送某种事件,因为我想呢,用一个进程,想通知另一个进程的哪些事件已经就绪了。那么你可以做什么样的动作了,总之呢,我们最根本的为什么要通信呢?因为我们想让进程间实现协同好,那么我们可以简单这么理解啊。所为了通信啊,但是呢,因为进程具有独立性,所以呢,我们没有办法直接让两个进程之间通信,

所以为了解决进程,因为独立性。而无法通信的问题,所以呢,我们引入了通信机制,那么为了引入通信机制呢?那么它的本质就是我们需要让他看到同一份资源啊,那么让进程看到同一份资源好,我们为什么要进行看到一份资源呢?它是一个对应的一个结论呢?是为了解决前面我们所面临的问题的,也就是说呢,我们想解决一个老问题,所以我们提出来了一个方法。但是同时我们在提出我们对应的方法的时候呢,同时也引入了新的问题。

所以这点能理解吗?这个跟我们现实生活中其实也是一样的。因为我们这个世界上呢,本来它就是那么没有100%的是把这个问题解决掉的,只不过呢,两害相权取其轻,对吧?

那么相当于呢?我们是为了解决进程间通信,那么为了让进程间协同,所以但是进程有独立性,所以呢,我们那么要让它通信,就得让它看你的通信资源。要提出要看到,同一份资源,你就得提出方法,看到同一份资源,因为我们提出来这个方法呢?最后引入了新的问题。而引入了新的问题呢?这个问题就是我们接下来要谈的,就是上面的数据不一致问题,那么必定会面临的两个进程呢?它最终呢?可能访问同一个资源时。因为彼此如果没有加保护的时候呢,那么就势必会带来,我在写的时候呢,你可能来读,反之你在读,我也可能来写,对吧?

那么,这样就造成了数据不一致问题,所以呢,我们未来怎么解决它呢?所以要想办法通过一些保护策略呢去解决它,我们后面再说。几乎可以预料到的就是,我们再引入一些保护策略的时候呢?那么我们一定可能会面临另外的新的问题,那有人当然就会说。那你这样不断解决问题,引用新问题不断解决问题,

又引用新问题,那难道这样就无休无止了吗?不是的啊。而是说呢,那么当我们在提出问题,那么解决这个问题,再引入新问题再解决。直到我们把这个问题解决的,它在我们的那么叫做可接受的范围之内,所以往往某些技术,因为它要解决的问题呢,是不断在产生的,所以慢慢的也就诞生了该技术所对应的特性。所以就一定注定了任何一种技术,

它就注定了有一定的应用场景好,所以这个问题呢,我们要注意啊,当我们有了这点认识之后呢,然后再回过头那么。我们未来啊,我们叫做将我们对应的被保护起来的公共资源呢,我们一般呢。称之为叫做临界啊临。虽然现在被保护的这个方法,我们还没学,但我们未来呢,知道被保护起来的资源呢,我们称为临界资源。那么被保护的资源一定是公共资源。系统我们自己写的代码当中一定是有大部分的资源。是独立的,而在你的进程当中呢,你自己写的那么申请的堆空间占空间,这大部分都是属于你这个进程自己的。而我们因为进入一些通信,所以想办法在进程和进程之间开了个小窗口,让它俩能通信了,那么只有这部分看到的这部分公共资源呢?是被我们所共享的。那么它有有并发访问的一个问题啊,但是我们并不能因为我们在谈这个公共资源而忽略掉,那么人家大部分资源是进行独立的。所以呢,我们不要一并认为所有的这些那么都是公共的,而是说我们进程如果在通信情况下呢?大部分资源它都是那么属于进程,

自己的少量的资源呢?是属于多进程共享的共享这个资源呢?那么它一般叫共享资源,如果我们后续呢再加保护。那么,有一个说法就叫做临界资源好,这是我们对应的引出来的,第二个我们重要的概念啊,所谓的资源呢?

大家可以把它理解成什么呢?比如说诸如内存,诸如我们对应的叫做文件等?还有什么网络等啊?其中呢?也要记住资源这个东西它。在进程内部存在的意义是要被使用的,大家觉得是不是比如说我申请了对空间,我就是为了用它。

所以资源是要被使用的,那么那么如何被进程使用呢?那么一定是该进程有对应的。我们的代码来访问这部分资源好同学们,那么这个呢,应该很好理解,举个例子啊。在我们自己写的代码当中啊,比如说你申请一个堆空间,你想想你为什么申请呢?你又不是闲了没事干,你说申请一个玩儿玩儿。而是说你申请它未来是要用的,那你要用的时候呢?那么其实怎么用呢?

是不是你要通过编写代码那么比如说遍历式的访问?像这个我们对应的那么比如说我们申请一块空间,我们便利式的把这个我们对应的空间做初始化,或者对空间的内容呢做设置啊,做我们的赋值等等。所以一定是要有对应的代码来访问这部分资源的对不对,而我们呢,一般把访问这部分被保护起来的临界资源,那么我们称之为这部分代码叫做临界区,我们一定要记住,有临界区这个概念。

虽然我们在讲它啊,但是它一定是我们在未来那么情况当中,它是少数情况,因为多个进程看到同分资源本身就是一个少量情况,概率非常低的,那么大部分情况下的进程呢?它访问自己的,申请自己的资源,它用自私有的代码呢?去访问自己的资源啊。所以呢,与我们的临界资源呢相对应的,有大部分资源是独立的,与临界区相对的,我们一定会存在大量的不访问这部分公共资源的这部分代码,我们称之为。非临界区,所以呢,不能给大家只讲黑不讲白,我们两方面都要讲,所以我们在境界通信当中呢,那么我们有大量的代码是属于临界区的。那么,还有一部分是属于非临界区的,举一个最简单的例子,把我们对应的代码,比如说呢,我们上次说的,我们叫做共享内存,我把这个server打开在。我们的server当中呢?

这部分代码,那么还有包括我们的打印,包括我们的其他的这部分,这部分代码,那么包括我们后续的一大堆的代码。那么,它其实和我们对应的那么像return 0啦sleep啦这些东西跟临界区一点关系都没有,然后呢,你真正访问临界区的时候呢,是你进行对它进行我们对应的,叫做我们对应的在箭头指着哪里。我们对这个临界资源呢进行我们对应读取时,这部分代码就这一部分代码,它才能称之为叫做临界区的代码,剩下都是非临界区的好,这是第二个概念啊。应该严格立场,这是第一个数据不一致问题,第三组大概念开始,那么下面的问题就是,我们就可以用更专业的话去表述它了,就叫做,多进程在通信时,它本质是要看到一份公共的资源。这部分公共资源在未来如果被保护起来呢?那么这份公共资源呢?我们就称其为叫做临界资源,访问那部分资源的那部分代码,我们称为临界区不访问我们这部分代码呢?我们称为非临界区。

概念确实比较多,但是呢,我们必须得有这个过程,因为这个过这个概念,如果没有这样的经历的话呢,后面我再讲信号量。在讲那个多线程的时候呢,会非常恶心啊,所以呢,我们把这个概念提前要说一下,要渗透一下啊。大家需要有一个接纳的过程。那么当我们把这个谈完,然后呢?那听你的意思呢?就是我们无非就是来了一堆名词,那我们再套一套以前我们的知识啊。就比如说呢,我们以前学管道,那么管道本身是要被两个进程同时访问的,那么管道我们就称之为公共资源。那么管道呢?因为它自己自带了保护策略,所以管道呢?我们就称之为叫做,临界资源,而我们未来呢?还会访问管道,比如说我们调用read或调用write来向管道当中读写。那么,对应的访问管道的这部分代码呢?我们就称之为临界区,是这个意思吧?

在讲共享内存的时候呢,我们有两个进程,创建共享内存,然后看到一份公共资源,那么这一部分共享内存呢,就称之为公共资源,现在对公共资源没有加保护,所以它只。能被称为公共资源啊,后面呢,如果我们能够对公共资源做保护了,那么共享内存做保护之后呢,那么它呢,也称为临界资源,然后呢?未来我们访问共享内存的那部分代码呢?我们叫做临界区,这是概念啊,那么最后一个概念呢,就是如何保护的问题啊。如何保护这部分公共资源呢?那么一般常见的策略呢?我们有两种,一种叫做互斥,一种同步好。那么其中呢?

因为今天我们呢是属于概念铺垫,所以同步这个概念呢?今天我暂时不说,要不然嗯,这个概念也不好讲啊,我们等到我们到多线程部分的时候,我们到时候再来谈。但是呢,那么有一个概念,其实蛮好理解的,叫做互斥,那么互斥呢,就是我们即便是望文生义,我们也能理解它意思就是说呢,

当有两个进程,他们两个想访问一个,我们可以称之为叫做。那么,公共资源的时候,那么我们只能让一个人去进行一次完整的访问,我在访问时你不能打扰我,你我在正在访问时你来了,对不起,你得等。等我访问完了,你才能来,那么这种策略呢?我们就称之为互斥。

未来呢,我们在讲的时候呢,还会基于我们的互斥呢,还有同步的概念,再结合代码去给大家讲,今天呢,我们就只是知道它就可以了。好,那么在我们的现实生活之中呢?那么我们呢?是其实是随时随地会存在互斥这样的概念的。

我们在生活当中呢,比如打印机,一个人在用了,因为一个人就必须等着,不可以打一半就给你了,这就是互斥而互斥呢。一般我们在进行我们对应的后续编写时一般是要通过锁来完成的啊,好后面呢,我们关于什么锁和怎么利用锁呢?我们后面都统一再说啊,这就是我们的第四组概念。

做互斥的概念,还有一组概念呢,那么这组概念呢,我个人认为理解起来呢,有点成本啊,那么不过呢,为了能够更好的让大家理解呢,我还是想把这个概念再谈一谈啊。

今天你,想往一个缓冲区里写一段儿数据。那么我现在要求,写个字符串abcd1234,我要求必须我是把abcd1234写完。你才能来读,如果我没写完,你就你就读不了啊,那么对于我来讲呢,我在写的时候呢,那么我要么就不写。要写就必须得写完才对你有意义,那么其中这种要么不做啊,要么就给你做完了这种状态呢,我们称之为叫做原子型

原子性,要么不做啊,要做就做完,那么只有两态,我们就称之为一般,称之为原子性啊。那么,下面给大家举两个例子来帮助大家去理解一下,但是我认为呢,它理解并就是并不容易啊,因为在生活当中呢,其实这个原子性这种状态呢。啊,那么其实并不多啊,虽然我们也能举出来一些我们现实生活中的一些例子,但是往往就不是特别有说服力啊。因为在现实生活中呢,我们要想有这个原子性呢,那么一般很难有这样的具体场景啊,但是呢,有一些场景可以给大家试着说一说啊。就比如说呢。你以前呢,在上学的时候呢,你们老师告诉你这个作业啊,你要么就别做啊,要做你就做到最好。这是第一,所以他只能在老师的心目当中呢,那么他认为你的你的努力过程不重要,我只看结果。你要么不做,你要就做就做做最好,这就是一种两态,再比如说呢,你在公司里面啊,碰到那种看起来很强势的老板,那么他经常给他的员工说呢啊,这个事情。你要么就不做,那么要做了,你就把结果给我啊,不要给我再说中间这些状态,我不关心,我只关心结果。那么,这叫什么?这叫做站在老板视角呢?他认为,这个员工做事情,要么你就别做,要做就做完,我只拿结果,这也是一种两态的。好,所以在生活当中呢,我们确确实实会存在很多的这种类似于原子性这种场景啊。但是呢,这个东西并不具备很强的说服力啊,那么下面呢,给大家稍微再举一点点,那么稍微那个严格点的例子啊。

就比如说呢,那么我今天呢,想给你转账,那么我卡上呢,比如说有1000块钱,你卡上呢,比如说有1000块钱,我想给你转200。转200的时候呢,那么在银行的系统里面呢,它要做两件不两件事情,那么第一件事情呢啊,它要让在我的账户的1000块钱里减去200。

然后第二件事情呢,那么他要在你的那1000块钱上呢,给你加上200,所以他要做两件事情,所以呢,就是大家可以理解成叫做招商银行,那么你可能是农业银行,那么其中呢,在我们转账的时候。那么,如果他把你比如说把我这200块钱扣了,然后但是呢,想把想在你那里加上200块钱的时候啊。啊,那么出现了网络问题而导致转账不成功,那么银行呢?它不是这样就完了啊,它必须得想办法。把曾经给我减的200块钱重新给我加回来,那么这个在大家那里呢?我们就叫做操作失败了。大家能理解吧,所以很显然这两步操作呢,那么我们转账这个逻辑,它必须要保证,要么就不转,要转就必须得成功。那么它就是一种叫做原子性的保障,那么如果你呢?当前转账失败,你也必须得想办法,那么让曾经被转账的用户的账户信息不受影响。那么方便,他发起再次转账,举个最简单的例子,我要扣200,那行,我变成800了,结果你那钱没到账,无缘无故200块钱没了。

那你觉得那么我作为一个转账方,我能跟你银行,把这个事情能轻轻松松了结吗?答案是肯定不能对吧?所以呢,他的这个转账的逻辑呢,站在客户的角度,就站在你们的角度,你们的转账时其实就只有两态,第一个呢,就是他要么就不转,要转就转弯。有人说,那我也关关注它的中间过程呀啊,但是最终的结果呢?

它一定是那么要么转,要么就不转,不会存在说啊,转账中。然后你等了两天,它还转这种啊,这就不合理,同学们,所以我们把这种只有两态的,要么不做,要么做完的这种状态呢,我们称之为原则性。好,一般呢,

这个原则性这个东西呢,一般和我们对应的叫做互斥和同步,以及对临界区进行保护,临界资源进行保护,那么是强相关的一个概念啊,那么如上。我们给大家核心点铺设了五个概念,还有一个概念呢,到多线程再谈,叫做同步问题,我们到时候再说,刚刚我讲的这五个概念,大家听懂了没?

那么我的下一个问题就来了,你上面说的这一大坨,那么请问又和信号量有什么关系呢?啊,那么信号量是做什么的?信号量呢?它未来呢?就是用来实现。让多进程之间进行协同的,那么或者我们未来的多线程,用来进行原子式的互斥,或者同步的。就叫做我们信号量是其一种解决方案啊好。那么下面呢?我们再给大家继续往后再讲,之前呢?我们再来重新再谈一谈信号量是什么?那么,如果说第一次我在说信号量是什么呢?那么就是告诉大家,它是个计数器啊。那么现在大家再想一想,如果信号量是一个计数器,它用来统计公共资源当中资源数目的多少呢?

那么我这里就会有俩问题,第一个问题啊,那么啊,全局的整数那么作为一个计数器,为什么不行呢啊?答案是呢,用全局的整数呢?多个进程。啊,在父子关系上你都看不到,因为父子关系当中两个进程同一个全局变量,它要发生写示拷贝。不同的进程,你更看不到,所以呢,在进程间,如果你想让两个进程看到同一个计数器。也前提条件是得先让这两个进程看到公共的一个计数器资源,所以它就也被划分到了我们叫做进程间通信的范畴。那么信号量这东西呢,它就个计数器,那么它也不是说一个进程向另一个进程发数据,另一个进程那么从我们公共资源读数据,它不是这样子的。而是通过这个计数器来能够衡量我们当前的公共资源的资源数目的多少的好,那么这是第一个。

第二个那么衍生出来的问题呢?就叫做我为什么要这把计数器啊?为什么要信号量好,那么上面呢?我们只能够一个那么概念铺垫的方式来讲,信号量是什么?我也只能给大家这么说啊,那么下面呢?我们来谈一谈。为什么要有信号量好那么并且呢?还有你说的这个信号量呢?通常用来表示公共资源当中资源数量有多少?

那是什么意思呢?我该如何理解那么下面呢?

我给大家再找一个生活当中的例子作为切入点。帮助大家去理解,为什么要有信号量啊?那么,以及我们什么是信号量这样的概念啊?下面呢?那么我来问一下大家啊。在座的各位大概率是都看过电影的。对不对?

如果你今天想看一场电影,你是不是在某一个放映厅里面?你是不是必须得有对应的座位?对不对?这是放映厅。放映厅里面呢,它有很多很多的座位啊,那么我呢,想问大家一个问题。

你说当我想看电影的时候,一定是我得有座位吧,我总不能站着看,我想站着人家不让我站呀。啊,我那么我现在呢,想看这场电影的时候呢,我是不是?只要坐在啊,这个对应的座位座位上。上啊,我真正的坐到这个座位上啊,这个座位才是我的。好同学们来认为是的,

我现在在,比如说我现在在看电影。然后呢?我是不是屁股真的坐在这个座位上,这个座位才是我的?啊,那么是不是呢?那么答案是并不是啊。

那什么情况下这个座位就已经是我的了呢?答案是那我看电影之前我得先买票好,那么我们的买票呢,大家可以理解成呢?票上面呢,有两个重要的信息,第一个信息呢代表的是座位号。,第二个呢?我们可以称之为票号,

也就是我们票呢,那么你是第一张票,第二张,第三张票,对不对?所以,只要我们买了票啊,哪怕我现在电影开场了,我都没去。这个座位在一般情况下,那么它也不会有人去占用这个座位,就给我留着呢,所以我们看电影买票。的本质叫什么呢啊?

看电影买票的本质,我们就叫做。我们对应的对放映厅中的座位。就相当于对我们放映厅当中的座位呢,那么进行的一种预定机制。那么,如果我今天呢?反过来说。

只要我买到票,一定有我的座位,再反过来呢?只要我坐在了我的座位,或者我有座位,就一定是曾经我买过票。那么当我们理解到这一点的时候呢,我们再把这种生活的例子,再把它呢,再进行我们所对应的升华一下。

当我们想要某种资源的时候呢?我们我们可以进行一定的,或者可以进行预定啊,那么所谓的预定呢,就是我现在还不用,但是你给我留着。当我想用的时候,你再给我,这就叫做预定啊。

那么假设呢,我们现在还是电影院,那么电影院当中,在进行日常看电影的时候,如果这个放映厅里面它只有100个座位。如果这个放映厅里面只有100个座位。请问我会不会卖出去101,102,103......?会不会啊?那么答案是不会这个放映厅里面,它如果只有一百个座位,那你卖票你最多卖100张,你不能卖的太多了。对不对?你绝对不能,要不然你卖了,电影院里面只有100个座位,你最后卖了101张票,那么你还有站票吗?还是有卧票啊?那肯定不行。

你说电影院呢,它应该怎么样保证自己的票呢?不会多卖。那么相当于呢,电影院它其实呢,在系统当中呢,那么全部都售禁。但是呢,他还要考虑,就是因为一些系统的问题,而导致自己把票多卖出去了啊,怎么解决呢?

很简单,所以电影院呢,

它就有了一个叫做int count好,那么count呢?默认呢?我们叫做100好,那么当有一个人付了钱啊,当前新出的这个票票号呢,直接先等于1,然后我们count再做减减,

同学们能理解吧?就是我们对应的票号呢,就编一下码啊,比如说一开始。然后呢?我们让count再减减,只要你买成功了,那么票号就是一下次呢,就看到减减再下来,如果另一个人再买成功了,

那么票号。好,那么比如说等于二,然后呢?它的count呢?再继续减减,当然呢?如果当前呢?我们最后的票数呢?最后已经等于零了,没有票了。那我们就不能不能再买了啊,那么或者说卖了啊。为了防止我把票数卖卖的太多了啊,我一定至少要保证一个座位和一个人是对应的。所以呢,那么我呢,就定义了一个计数器啊,那么来一个人呢,那么你买了票对不对?好,那我就把计数器减减。然后你再来一个对不对?然后我再讲解最后呢?那么每一次操作的时候呢?我还要做判断,如果票数为零,

我就不卖了,同学们。那么其中呢?我就可以通过这样的计数器方案呢,来保证我的票不会多卖。在这个卖票的过程之中呢,我们其实是有两个逻辑的,

第一个逻辑呢,就是每卖一张票,我们就要让我们的票价总数进行减减。所以这个count数据,它表示的就叫做当前我们的这个放映厅里面剩余座位的数量。对不对?好那么这是第一层,第二层呢?那么只要你买了票,哪怕你未来还不来,但是我一定保证你有对应的座位来给你。就是你哪怕不来,但是只要你拿到了票,那么我未来呢?就让你进行我们资源的访问,那么你想来,那么就可以来,你不来,资源给你留着,你来了就赶紧给你用。只要你票拿到手了,就未来一定有你的资源,这就是一种预定机制,对不对?同样的同学们,那么如果呢?我们把我们刚刚举的电影院的例子,放映厅当成我们对应的内,或者叫做我们的共享空间好,那么这里呢?我要暂停一下了啊。在未来呢我们在使用的时候,关于共享啊资源啊,或者共享空间了。比如说我现在申请了一个共享内存,那么或者是我们嗯,其他的被多个执行流同时看到了这部分公共资源。这部分公共资源呢?那么将来被使用的方式呢?

有两种,第一种呢叫做作为啊作为。作为一个整体使用啊,第二个呢,叫做我们可以将部分公共资源呢,那么划分?成为一个一个的我们的子资源,我们叫做片段啊,好那么或者资源的子部分啊,在生活当中呢,有些东西是整体使用的,比如说我买辆车。我买辆车,那么它一定是作为整体使用的啊,那那总不能开个方向盘出去,对不对啊?有些东西呢,它是你拆开去用的。啊,或者比如说那么一台电脑,它一定是整体使用的显示器CPU内存全部配合起来,它会整体使用,还有一些资源呢,那么还有现实生活中的有很多东西,它是要你拆开去用的。比如说呢,

以前,我们的那个新冠疫情不是比较严重嘛?大家要做核酸检测啊,你们要有什么测测那个测试你们是否得了新冠的那些试纸?那一盒子那么多,你总不能把一盒子全部用起来,而是一个个用好,所以呢,生活当中呢,只要涉及到一些资源呢,那么有些是整体使用,有些是那么分开使用的。那么,我们未来,可能在大部分情况下碰到的都是一个公共资源被整体使用,最典型的就是我们之前讲的管道。管道呢,它就是整体使用的,那么我在写,你就不能来读啊,你要读对不起,你就那么我写满的时候呢,你来读了,你就读到了,但是我不写,你再读没数据,你就不能读,整体使用。当然呢,有时候呢,我们可能一个公共资源呢,我们把这公共资源拆了,好多子资源,我们想让不同的执行流啊,

比如说我们想让进程一访问它进程二访问它进程三访问它。想让这几个不同的进程呢,访问一份公共资源的不同区域,这样的话呢,就可以实现一定程度的并发啊,

如果我们。整体作为使用的话呢,那么只能是我访问时你就不能访问,那那最终呢,就会导致我访问完你再访问,那这就相当于互斥导致的一个串行的工作啊,效率就低了。所以呢,有时候我们对一份公共资源把它拆成一个个子资源,你访问它,它访问它,它访问它。我们彼此呢,只要访问,虽然是公共资源,但是对这个公共资源访问,我们访问的是公共资源的不同区域。这样的话,我们就可以同时去访问了,那么那么所以呢,要谈论的其实是属于第二种情况啊,好同学们,所以我们假设呢,我们现在有一份公共资源了,然后呢,那么在多个进程当中呢,我们可能想让不同进程。访问的是,这个公共资源的不同的区域,那么如果我们把这个大部分的我们整个放映厅呢,就看作成我们对应的叫做共享资源。

然后呢?那么其中这一小块儿一小块儿的呢?我们就可以称之为共享资源的一部分好一部分啊。

那么接下来呢?把我们对应的,比如说我们的座位,我们就看成我们对应的内存当中的一个小块儿啊,虽然整个资源是被所有进程共享的,比如共享内存。共享内存呢,我有4096字节,然后我呢想把它看作啊四个资源部分,比如一个每一个资源都是1024字节。我可以,我就可以那么划分上让那么进程一和进程二进程三进程四,他们访问共享内存的不同区域,这样可以吗?可以啊,对不对?

好。所以对我们来讲呢,那么我们可以把这个资源呢划分成一部分,然后呢,那么座位呢,相当于资源的一部分,然后我们人就相当于进程啊,那么人看电影呢,人相当于进程。而我们人呢,在想那么访问资源,比如说你这个进程想直接访问公共想资源的一部分,你不能直接去访问,

就好比你去看电影,你不能一屁股坐在座上。人家不让你进啊,那么你每一个人呢?在看电影之前都得先进行买票好,那么这个票呢,我们就称之为叫做信号量。好同学们,那么这个票呢?我们就称之为信号量,我可以给你保证,只要你呢?今天这个人你买到了票。那么你一定能够看这场电影,座位一定给给你留着呢,

如果你买不到票。那么其中对不起,你看不了这场电影了,那么我们就可以通过那么每一个那么进程,想访问某些公共资源时,先申请信号量。那么,申请信号量成功的时候呢?就相当于预定了共享资源当中的某一部分小资源,然后呢,可以允许你进到这个小资源里进行访问。如果你申请信号量不成功,那么此时我就不允许你这个进程进入我们对应的共享资源,进而呢,

达到保护共享资源以及其他进程的目的。所以这种我们通过我们的那么计数器的方式来对我们的临界资源进行保护的这种计数器,我们称之为叫做。信号量。

好,那么所以呢,对我们大家来讲呢,未来如果我在多进程环境当中,我想访问一部分公共资源呢,我首先要进行的第一个。信号量,那比如说我定义一个信号量,sem,定义一个信号量,假设呢,我们资源是20个啊,然后呢,我们要访问某种资源时,任何一个进程,它必须得先进行信号量减减。好,那么然后呢?再访问。访问我们对应的公共资源好,那么然后访问完之后呢?sem加加,

那么我们对信号量做减减呢?我们称之为叫做预定资源。把资源预定完毕之后呢?那么你最终不用了,那么最后呢?这个地方SM加加我们称之为叫做释放资源。就好比呢,当我们在看电影的时候,那么我看电影之前,我得先买票,买到了票之后呢,那么系统里的票就减了一个,

所以它叫做sem减减。然后呢?那么此时我们接下来呢?那么再访问公共资源,然后呢?当我把电影看完了,我最后退出去的时候呢?那么这个资源是不是自动就被释放出来了?所以我们要对资源进行加加。就是那如果我们两个两个人预定了这个资源,进去之后我们发现我们访问的是同一个座位或者同一个资源,那怎么办呢?不用担心啊,一会儿呢,我给大家说来,只要我们先进行预定,然后呢,再访问,然后再释放,我们可以称之为叫做公共资源,对吧?好,这是其一,那么下面呢?那么我们把概念再继续抛出来啊。同学们,那么这儿呢,就可能有一点点难了,然后不过呢,我想把结论先告诉大家啊嗯,如果这一个整数。进行加加假设这里的信号量,按照你刚刚的说法啊。所有的,比如说所有的进程呢?它要进行访问,

我们共享内存的时候,它都得先预定,那么我们对应的共享内存的一个部分。那么,预定之后呢?然后给预定成功时你才能访问对吧?所以所有的进程在访问公共资源时都得先。访问我们对应的叫做信号量啊,按照你的逻辑呢,就是所有的进程啊。进程。在访问公共资源的时候之前。都必须先访问我们对应的先申请计数器啊,叫做sem信号量。对不对?所有的进程在访问公共资源前,你都得先申请信号量,申请成功了你才能进去,不成功对不起你得等,对吧?那么那么这个地方呢?就有个问题了啊。那么,所有的进程呢?它都必须先访问信号量,那么而必须先访问信号量的前提是所有进程。 必须先得看到同一个信号量,是不是这个道理?你们每一个进程想预定资源行啊,那你先申请我信号量吧,那么我要申请公共资源,那我就得先申请信号量。那么所有的信息,就必须得先得看到同一颗信号量。

对不对?根据我们刚刚的概念,凡是被多进程同时访问的资源呢,那么它就叫做公共资源,所以我们对应的信号量本身。想一想啊。首先啊,所有的进程呢,在访问公共资源前,它都必须得先申请信号量。那么,先认定信号量呢?那么,你是不是所有的进程都得先看到同一个信号量?

你连看都看不到,你凭什么那么叫做访问同一个信号量呢?就好比同你把信号量在你的脑海里,现在不要想其他的,你把这个信号量就想象成一个整数,现在呢,我们所有进程在访问之前都得对一个全局的整数进行减减。啊,那么我们多个进程呢?就是要必须得先访问我们的信号量计数器。那那我们就得先看到这个信号量吧啊,那么因为我们多进程环境下看不到啊,我们即便是全局的,那也看不到。所以呢啊,信号量本身也是公共资源,那么所以呢,那么信号量是不是也要?保证自己的安全呢,是不是这个道理啊?

你们所有人想访问公共资源,你得给我打个招呼。你们得先来访问我,那么所以呢,那进程说行,然后呢,那么所以每个进程都来了。来了的时候呢,那么他必须得全体保证他每一个进程都得先看到同一个信号量,所以换句话说,所有进程在访问公共资源前都必须先得访问信号量,那信号量本身是不是就必须得是一个公共资源?那它是个公共资源,那它是保护别人的,

那怎么保证自己的安全呢?如果一个信号量它连自己那么对应的数据,访问的安全性都无法保证。啊,计数器加加减减那么乱七八糟,最后加错了,减错了,那么引起的公共资源的问题,那你连自己都保护不好,你还保护别人呢,对不对?,这就好比呢,在生活当中呢,你有一个特别要好的朋友,他很勇敢。对吧,但是他很弱小,然后呢,那么出去玩的时候,那么你跟一些社会人呢,打架啊,那打架的时候呢,你这个舍友呢,他非常勇敢他,他已经被揍的都爬不起来了,那么他还是亮亮呛呛站在你面前,然后呢?那么用他渺小的身体挡住你的大腿,然后呢?就对这些坏人说啊,你们不能欺负他,你们得先。看来你要欺负他,你得先过我这一关,所以这叫什么?这叫做不自量力,对吧?这叫做不自量力,那么为什么呢?因为你要保护别人,前提是不是得保证你自己是安全的,

对不对?好,所以呢,信号量它要保证自己安全,怎么办呢?因为信号量主要是涉及到叫做计数器,说白了你就可以理解成是一个整数的加加和减减。所以信号量对应的减减和加加操作,在未来呢,那么信号量啊,那么必须保证自身。操作的自身操作的安全性,那么它是如何保证的呢?那么信号量内部呢?

它帮你保证,那么它对应的减减或者加加操作。原子的,概念终于出来了啊,也就是说呢,信号量呢,那么它呢,既然本身也是一个临界资源,被所有进行访问。所以呢,它必须保证它自身的减减或加加代表的本质,就叫申请资源和释放资源,这个操作在信号量这里,它也要保证。

减减或加加操作是也是原则的啊,所以呢,那么信号量怎么做到原子性这些呢?我们后面再说,所以我们今天呢,就可以简单的理解成信号量预定资源进行信号量减减。这个我们叫做计数器减减代表申请资源,它在信号量的语义当中呢,一般被称之为p操作啊。好,那么当我们在释放信号量的时候,我们称之为微操作,这就是鼎鼎大名的信号量,要对临界资源进行访问。它所对应的pv操作。

那么所以后续呢信号量呢,它一定要想办法来保证自身的安全,那么这就叫做pv操作啊。所以对于信号量的理解呢,我们一定要有非常完整的理解链,那么虽然现在还很难建立出完整的理解链。但是呢,那么概念的铺垫是必要的,好关于信号量的理论呢,

我就先说这么多,不敢再说了啊,这部分那么我们就作为一个当前对于后续知识埋下的一个那么伏笔,然后等我们到后面正式讲信号量,在多线程场景当中去谈它的时候好谈多了,到时候我们再来写代码,帮助大家慢慢理解它。最后呢,关于这部分呢,就产出一个结论,那么信号量就是一把计数器,system V版本,在多进程环境当中。那么,这把计数器,可以被多个进程同时看到,然后它必须给我们匹配上两种操作,一种叫做p操作,一种叫做v操作,然后呢?认信号量对我们的临界资源进行特定的访问。

那么如上呢,就是我们对应的这里的一个问题,最后留一个小问题啊。如果一个信号量,那么它的一个初始值就是一,代表什么意思呢?大家思考一下,如果一个信号量呢,它的初始值是一,

像我们现在呢,这个共享资源里面有这么多。放映厅里这么多座位,如果我们对应的信号量计数器值为一代表什么意思呢啊?那么代表一的话,是不是就相当于未来我们访问这个公共资源呢?是不是就作为整体来使用了?那么,作为整体来使用的时候呢,当我申请成功的时候,你还能申请到吗?我能申请到的时候,你能申请吗?我申请到了,你还能申请吗?

对不起,你不能申请了。这叫什么?是不是就叫做互斥啊?对不对?所以我们呢?一般我们把这种提供互斥功能的信号量,那么只有两种状态的信号量,我们称之为二元信号量。它呢?主要提供互斥功能啊,

那么所以这个呢就叫做信号量啊,那么就这么说,在我们的并发编程里面呢?那么,我们对应的这个大家可以理解成信号量这个概念是最难理解的,那么我就这么说啊,那么在我们信号量这里呢?如果你想充分的去理解它,是其实很困难啊。但是呢,只要大家能够把我刚刚给大家推导这些结论,尤其是几个重要的结论,第一个就是我们的信号量的本质,它是对资源的一种预定。

只要你申请信号量计数器成功了,即便你现在还没来得及访问这个资源,那你已经有这个资源了啊,这是其一,你如果忘了,你就想一想,你看电影买票。你只要把票买到了,哪怕你现在还没去看电影,然后未来你知道在特定时间点这个资源就是你的,这是第一个。第二个呢,信号量对应的pv操作啊好,这是最重要的,那么最后一个。

行,我知道大家会有一个困惑,那我现在申请个信号量,那么你也申请个信号量。那我们两个申请的信号量,那么最终我就能保证我自己能够预定某种资源了,比如说我申请的这个资源,你能保证有这个资源,但你并没有说你是哪一个,所以有没有可能一个进程申请信号,另一个竞争也申请信号了,两个都申请成功。但是两进来访问同一个资源呢?有没有呢?答案是有的。

在我们谈论这个话题之前呢?我们这个呢就有点超纲,但是我们现在先给大家。把这个概念再说一说啊啊,首先当你不要觉得你把信号量申请成功了。那么,接下来就是你要进行我们对应资源访问啊,它其实还没完,

那么当你把信号量申请成功了。只能证明这个资源里一定可以给你留一个资源,但具体访问哪一个资源呢?那么是需要我们在多执行流当中。我们是需要通过某种方式去区分的,也就是说呢,我们就好比呢,我们刚刚故意呢在写这个,买票的时候多加了一个叫做座位号。啊,那么我们申请信号量的时候呢?你买到了票,那票就有票号啊,那么但是呢,一旦你买到。

票之后呢?那么每一张票,它还带了座位号。

所以你在电影院里就不会存在这样的问题,同样的,那么当我们申请信号量之后,访问公共资源时,我们还要再想办法。去保证我访问的这个资源呢,不能再让其他进程也来访问了,所以呢,这部分工作一般由谁来完成呢?一般是由。程序员写代码来确认不同的进程呢,要访问哪一个资源。

因为我们没有今天没有并发场景,所以呢,我们没办法写,后面我们再讲多线程时,我们会专门来说它啊。所以不用担心多个线程会访问同一个公共资源的问题,首先这个问题存在,但并不用担心啊,我们内部呢,未来可以通过编码来解决。让不同的执行流访问不同的资源,那么如上就是信号量的储备概念,我们呢,

重点讲了如下的名词叫做临界资源,临界区统,同步。那么,还有我们对应的那么互斥啊,这样的概念那么再比如说呢,还有原子性这样的概念再下来呢,还有我们信号量是什么以及pv操作?那么,还有我们深刻理解信号量,设法计数器,它本质上是一种资源的预定机制。好这些概念我说完了啊,那么下面呢?

我们再把这一块呢?我们再来一下。那么说了半天,然后呢??在多进程环境当中,如果我想使用信号量,我该怎么做呢?那么还是一样,你是不是得先申请一个可以被大家看到的公共资源呀?所以semget。看着啊头文件和semh只有这一个头文件不一样,sem就是信号量的意思啊。

那么第一个参数来。认识吗?这不就是我们之前在写那个共享内存,以及刚刚看消息队列时候,那么申请的ipc资源都通过这种key来标识。那么第二个呢?这个地方nsems这个代表的是你想申请几个信号量那么system V版本的信号量呢?它在底层呢是?是可以一次让你申请上很多信号量注意。很多信号量和这个信号量是很大的值,这两种概念,比如说我想申请十个信号量。不是代表的,是这个信号量是十,不是一个信号量,是十个信号量啊,所以这儿呢,代表你想申请几个信号量啊,比如我想申请一个,那么这就是一。

那么,再下一个SN flank参数ipc_creat和ipc_excl跟之前的含义一模一样返回值呢?如果我们当前申请信号量成功了。它给我们返回的是一个信号量级的标识符,因为我们只申请一个的话,那么它这个nsems相当于集合,里就只有一个信号量。如果我未来不想用这个信号量了,就semctrl

我们就可以直接根据信号量对应的ID,然后呢,那么设置我们这个参数呢?可能有很多信号量。那么这个呢,1的位置就相当于信号量的下标啊,比如说我申请了一个信号量,那么这一个信号量的下标就是零数组啊,然后呢,如果我申请了十个信号量,我想对第做九号下标的信号量做操作,这样填九就可以啊,然后再下来呢?

如果你不想填填默认“0”,然后再下来com呢,那么这个毫无疑问就是你要删除,我们也可以通过semctrl通过com去获取指定信号量相关的一些信息。

它也是给我们提供了信号量相关的内核数据结构来,那么structsemid_ds这是不是信号量的数据结构,然后呢?第一个字段依旧是ipc_perm,第一个参数依旧是key里面的属性完全一样啊,

我们以前的消息队列共享内存都有权限。那么,对于信号量来讲呢?那么,大家呢?可以看到,信号量所对应的最啊,最近一次被那么操作的时间,最后一次被更更改的时间,还有一个信号量的一个数啊,这就是信号量的属性。

那么我们对于信号量来讲,我呢?能获取它啊,只要有get函数。那么,十个八个进程,都可以通过get来获得同一个信号量,那么可以semctl,那么我们就可以最终删掉这个信号量。可是呢,我们对于信号量,你说了它是个计数器,那么最终我们是不是还要对信号量做你刚刚所说的p操作和v操作对不对?

我们的semop操作呢,

那么它呢就可以被封装成我们对指定的信号量设定,我们对应的操作,这就是我们这个参数呢,就代表的是。好给大家说一下sembuff,那么我们呢sembuff里面呢?sembuff里面呢?这个结构体它上面没写啊?但它的结构体里面呢,

就三个参数,第一个呢叫做num那么代表的就是你想对你申请的多个信号量当中的哪一个进行操作。假设我只有一个信号量,那么下标就是零,所以编号设为“0”,代表的是想对零号,那么信号量做操作。第二个就是这里的op啊,它是short类型,一般只有两种,你可以设两种值,一种是1,一种是-1。如果你设的是负一,就相当于对信号量做减减,对应的就是p操作,如果你想设为1,那对应的就是对信号量加加,那么对应的就是v操作。第三个,选项我们设为“0”就可以了啊

通过向信号量集合当中设置一个我们应的semop。然后它的这个nsops参数呢?代表sops这个结构体有多少个,这个参数设为1,那么就代表我们要对一个信号来做操作。如果我曾经一次性申请了十个信号量,那么我想同时对十个信号量同时做某种我们的设置,那么其中你就可以直接定义一个十个元素的数组放到sops。nsops这个参数就是十,然后依次把每一个我们结构体当中的pv操作的那么选项一设置我们。那么呃,system V版本的间接通信当中呢?关于信号量部分,它是允许你一次可以同时去申请多个信号量所表示的资源。这就好比呢,我看电影啊,那么我可以同时买上十张票,那么同时买十张票呢,可能买的一会买万达的,就是今天的,明天的,后天的。或者买万达的电影票,或者买其他的电影票,反正我同时可以进行多种操作啊,它就其实是一个数组。你可以允许你同时对多个信号量同时进行pv操作啊,

就这一个函数,semop,它就充当了我们对应的p和v操作啊。

那么如上就是信号量相关的一些操作接口。啊,那么嗯,信号量操作接口呢?说白了,其实呢,它用起来呢会比较难一些啊,那么如上就是我们关于信号量部分的内容。那么还是我说的更多内容呢?

后面我们再讲那个。多线程时,我们会重新回过头再来谈谈啊,那么今天呢,以及后面呢,因为我们有大量的内容都要学,所以这部分内容我们就不再花时间说了。,那么如上就是我们对应的今天的第一个大话题,叫做我们的消息队列和信号量的概念部分。

来给大家再来复盘一下,我们讲的这些通信啊。

那么这些通信呢,那么就是管道其实很好理解啊。没有什么理解难度,因为之前讲文件系统花了很长时间,就是讲道理啊,凭良心说。共享内存消息队列和信号量?那么其实这种ipc资源呢?看不到又摸不到,虽然我们有一件利器,就是理解它的时候呢,有那六个字先描述再组织。

我们心里很清楚,操作系统为了维护这种IP的资源,它必须得花足够多的,那么数据结构来进行描述这种资源。所以任何一种ipc资源,那么我们说是资源,那么如果通俗点儿呢?它里面的资源呢?核心就两类。第一类呢,就是你所申请的资源本身,比如说共享内存消息,队列和信号量。那么,第二类呢?

有一个隐性的成本就是操作系统。为了维护这一堆的,我们对信号量消息的管理内存。啊,必须得创建对应的数据结构来进行组织,这部分资源啊,那么实际上呢,当我们的系统,在进行组织这个ipc资源的时候,明显感受到一个非常重要的东西,那么这个东西呢?我们就叫做menshm,我们可以看一下啊ctl。其中呢,我们发现呢,就是我画的这个和手册上的这个资源呢?它其实不准确啊,它是经过我们操作系统挑选的属性,不是内核里的100%属性啊,但是它很能说明问题。这个资源呢,是我们在讲共享内存时,它匹配的它的属性,那么对应的集合。

我们还有我们刚刚所讲的msgctl啊,我们叫做消息队列。那么我发现呢,之后呢,它也有对应的,我们这一堆的结构好,然后把它呢也拿过来啊,这是我们的第二个,我们称之为叫做。啊,我们对应的啊,就是啊,我们的消息息列再下来呢,我们再来一个semctrl呢。我们再看它也有对应的内核数据结构啊,那么我们把它也截图过来,

那么按照大家的认识呢,那么很很明显的第一个现象呢,就叫做它们的接口相似度非常高啊,好非常高,尤其是获取。与我们的删除好,那么其中呢,他们的获取都叫做某某某get。那么,删除或者是控制它的属性呢?我们就叫做某某某ctrl,虽然操作上有差别啊。啊,共享内存,它就是挂接和去关联,那么消失这点呢?它就是send和rece,然后我们对应的信号量呢?它就是一个OP。啊,操作上有有差别,但是它们的接口非常相似,

这是第一个我们看到的一个共性啊。那么第二个共性呢,我们发现呢,它们的属性字段当中呢,都有对应的某某某id_ds这样的结构啊,这是第二个共性,第三个共性呢,它对应的这种那么描述我们IP资源的这种属性的第一个字段,它全部都叫做ipc_perm。那么这三种共性呢?实际上呢,

就能够看出来一个细节,这三种它都叫做。system v标准的啊,我们叫做标准的进程间通信,所以什么叫做我们对应的标准呢?所谓的标准就是大家用的方式,用的那么包括接口的设计,数据结构的设置都必须得遵守某种标准。从这就可以看出来,这三个货呢,其实在某种意义上来讲呢,它属于同一个范畴下的东西好,那么下面呢,我们要谈的呢,

当然就不再谈它的接口使用了,这些我们再来进一步。那么,操作系统内部,它是如何去管理?我们对应的,你看一会我申请个消息队列,一会你又申请个信号量。一会儿又申请一个共享内存好,那操作系统究竟对这部分资源组如何管理呢啊?谈到管理,那么就如同进程管理一般。那么,管理的并不是共享内存本身消息,队列本身或信号量本身不是管理的这个。而是要先描述再组织对匹配的相关资源的内核数据结构做管理。所以呢,因为当前信号量,共享内存,消息队列,这三个资源呢,它本身的第一个字段,我们可以称之为第一个成员呢。全部都是这种结构啊

我们发现呢,不管是那么共享内存还是消息队列。还是信号量它的第一个资源都是它,所以呢,我们都可以使用同一个key呢来标识我们对应资源的唯一性啊,这是其一。其二呢,那么在内核当中该如何去组织它呢?那么很简单啊,

在内核当中呢?实际上呢操作系统,可以维护一个,我们就称之为叫做struct_perm对应的一个数组好,那么写清楚呢?这个数组呢?假设它叫做perms,这我随便写的,一会儿我们再看看具体啊。它这是一个什么东西。

大家应该都在,这种类型是什么了吧?那么它是不是就叫做指针数组?对不对?它是一个指针数组,那么然后?

如果有一个用户说,我今天就想创建一个共享内存。所以操作系统说行吧,那给你创建一个共享内存,那么给你申请空间,并且呢,把你对应的那么共享内存的相关数据结构给你创建出来。在我们内核里给你处理好啊,然后呢,那么内核的数据结构呢,把它呢,不是创建树结构,是创建对象啊。创建出来之后,然后呢,因为你这个共享内存的第一个字段呢,我们叫做ipc_perms shm_perm。那么所以呢,比如说定义了一个struct shmid_id myshm。未来,不是个数组吗?我直接可以直接,在perms[0],直接等于取地址myshm,shm_perm换句话说呢,那么我们呢?是不是就可以根据这个结构体所定义出来的对象?把这个对象的第一个成员,那么他的地址填到这里面,

然后呢?那么未来呢?有人说那我今天想创建一个我们对应的叫做消息队列,那行,那我就创建呗,,我们就把它对应的那么在内核当中呢,也有它对应的结构创建出来之后。然后呢?怎么办呢?然后我们其中呢?再继续用我们对应的struct msqid_ds。定一个mymsg,然后呢?在perms[1]的位置,再把我们对应的取地址mymsg.msg_per,我们第一个字段放到数组[1]位置里面。虽然呢,我们对应的这个就是属性的名字不一样啊,但是我们的类型都是ipc_perm这个类型。所以我把它的地址填到了我们对应的这个数组的第二个下标里,那么此时我们就把它搞定了。

如果再有共享内消息,队列信号量,我们照样继续填。因为C语言啊,那么结构体。结构体的第一个成员的地址在和我们对应的结构体对象本身的地址是相等的。这句话能理解的吗?

这个可是C语言啊。

那么其中呢,对我们来讲呢,那么我们就可以看到那么它俩的数字是一样的。所以,因为所有的ipc资源呢,它的第一个字段呢,全都是我们的ipc_pom的结构,所以我定一个统一的ipc_pom结构呢,定一个指针数组。那么指针数组呢,然后我们申请那么不同的对象呢,我们可以使用相同的数组呢来指向不同的对象。

然后呢,比如说我未来,想访问我们对应的某个资源的时候,那么其中呢,我就可以直接再做一件事情。比如说我现在,我想访问零下标?零下标是什么类型呢?它是一个叫做共享内存。我想提取共享内存属性,怎么提取呢?那么我们直接很简单,叫做perms[0]。那么是不是就相当于拿出来了,我们对应的数组0位置的内容。

然后把它呢直接强转成(struct_shmid_ds*),然后它是不是就变成了一个直接就变成了一个指向共享内存的指针,然后我不就可以直接访问它,剩下的其他的属性了吗?比如取key值。

如果你将来,你想访问它的那个,直接对它这种指针,再做强转不就直接可以访问后续内容了吗?所以共享内存呢?那么其中唉,叫做我们的ipc资源呢?就可以把所有不同的资源统一使用一个数组来进行保存。我们就称之为其他的属性,那么Linux内核,就可以根据它的类型来进行操作了。

有人会说呢,强转的时候那么我们,怎么知道它类型呢?注意我这里呢。我是这样写的啊,但如果让我去设计的话,我们其实可以这么设计啊,你不像你上面写的,比如说,那我怎么知道什么类型呢?很简单。我们定义一个struct myipc。

结构体里面包含俩字段俩字段第一个叫in type ,第二个是一个指针,struct ipc_perm*,所以呢,未来不要维护上面那种数组,而是直接维护这种数组,所以最后当你存的时候,把你类型往里面一填,最后我们把结构体的第一个字段地址放到这个数组里。未来你想那么是什么类型?我先根据它判断什么类型,然后再做强转,那么最后就拿到它对应的值了啊。

所以呢,大家你不用担心啊。只要这种技术是一个比较成熟的技术啊,而比较成熟的一种思路,所以呢,那么它一定有解决方案啊,我们重点其实倒不是说操作系统,它怎么做的,而是说呢,我们想通过这种理解呢,把我们之前的东西那么柔和在一起啊。

来那么另外一方说赋值了,不会出错吗?不会的啊,因为我们地址做强转是很正常的啊,不会出错来。

像给大家之前讲的文件系统了,讲的文件描述符了啊,包括后面讲的信号了,通信了,那么其实呢,都在内核当中,有具体的实现啊。好,那么当我们明白这一点之后呢?那么我们就知道了ipc资源究竟是如何组织的?我再多嘴问一句啊。聊这个的时候,你们不觉得熟悉吗?多态呀。

是不是像多态,不知道大家有没有学过c++或者java,什么叫做多肽呢?那么是不是我们父类的指针?如果我们用C++的实现方式,是不是这个我们的shm_prom它叫做基类,然后你下面的这三种资源呢?那么对应的。那么,对于这三种资源呢?是子类,这三个子类呢?分别继承自己类,然后呢?

我们往后呢?直接可以定义基类指针数组。然后我们的这个基类的指针指向哪个对象就调用哪个对象的方法或属性,对不对,所以这东西它就是多态啊。那有人会说咦,这操作系统太无耻了,它竟然照抄C++的多态,各位,谁是因谁是果啊,谁是先谁是后啊啊,同学们那么Linux内核是用C语言写的呀。

在有C语言的时候还没C++呢。所以你以为,你们现在学的那个面向对象思想是凭空来的吗?你要记住任何一种,我们现在已经被广泛使用的技术,它绝对不是凭空创造出来的。而是什么呢?而是人们经过了大量的工程实践发现,那么我们这样写是比较好的。所以那不是说Linux内核照抄了C加加,而是因为那些写操作系统源码的这些工程师。就有可能曾经呢,就参与过我们C++语言的设计啊,

那么谁是爹,谁是儿子知道吧,所以。这点要注意啊。它叫做多态,能理解吧,好这种呢,其实就相当于是使用我们那么C语言的方式来实现多态。那么所以呢啊,还记得我们以前给各位少年们叫做我们用c来实现做面向对象?当时说的,叫做linux下一切接文件。那么,些接文件,其中它内部,是不是就可能会包含各种函数指针?实现对底层不同方法的调用?那么今天呢,我们就又学习到了,我们可以通过我们的结构体继承的方式,结构结构体包含的方式,那么用指针强转的策略来。来做到对于一个结构体内部属性的提取,所以属性和方法我们都有了,未来如果各位我在这个结构体里再加些函数指针。

它不就相当于我们实现了C语言的多态,那么甚至?你想给这里的每一个加同样的方法,那么最终,你可以通过这种类型强转的方式调用同一个对象的内部的不同方法啊,那么这可以吗?答案是,当然可以。

你可以给这个结构体里面,包含一个我们包含函数指针,然后呢,匹配的共享内存,消息队列,信号量有不同的合作方法,然后呢,在我们定义创建这个对象时,我们可以让我们的指针指向底层不同的方法。当它做强转时,想要属性就强转。

数据类型想要方法直接调用perm里面的,那么函数指针所对应的方法,这样的话你就能够纯纯的用C语言实现一套多态体系了啊。甚至你这就你自己发明一套语言啊,好,当然这个就是有点扯啊,那么基本上就相当于是C语言模拟实现一下。那么如上就是我们所对应的叫做进程间通。

谢谢大家观看。如果需要完整代码都在github:xyt08 · GitHub

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-03-15 09:06:04       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-15 09:06:04       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-15 09:06:04       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-15 09:06:04       20 阅读

热门阅读

  1. 最短路 算法解析+例题

    2024-03-15 09:06:04       19 阅读
  2. html--bug

    html--bug

    2024-03-15 09:06:04      21 阅读
  3. c++字符串刷题:整数反转

    2024-03-15 09:06:04       19 阅读
  4. 如何查看并详细了解一个R包

    2024-03-15 09:06:04       22 阅读
  5. netstat命令 – 查看网络状态统计信息

    2024-03-15 09:06:04       19 阅读
  6. 计算机网络 传输层

    2024-03-15 09:06:04       25 阅读
  7. Vue:自定义消息通知组件

    2024-03-15 09:06:04       19 阅读
  8. NAT笔记

    2024-03-15 09:06:04       18 阅读
  9. springboot 自动装载原理

    2024-03-15 09:06:04       19 阅读