进程通信的方式

一、进程通信的方式

       

(114条消息) 用python3写一个弹弹球的小游戏(图形用户界面:tkinter)_BO_0125的博客-优快云博客
(114条消息) Python制作井字棋小游戏_壹屋安源的博客-优快云博客

(114条消息) 用python开发一个有趣的猜数字小游戏(实现简单的GUI界面学习)_weixin_42515907的博客-优快云博客_用python编写一个猜数字的游戏
 

(114条消息) 迷宫游戏(图形化界面)_ws_PersonalSpace的博客-优快云博客

(114条消息) 用Python实现开心消消乐小游戏_小白^-的博客-优快云博客

        (114条消息) 迷宫游戏(图形化界面)_ws_PersonalSpace的博客-优快云博客
(114条消息) CSP-化学方程式Python实现(含输入数据)_小王日记hh的博客-优快云博客
 

         管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
        命名管道FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
        消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
        共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
        信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
        套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
        信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
原文链接:(113条消息) 进程间通讯的7种方式_zhaohong_bo的专栏-优快云博客_进程间通信

二、管道

        管道,通常指无名管道。管道分为pipe(无名管道)和fifo(命名管道)两种,除了建立、打开、删除的方式不同外,这两种管道几乎是一样的。他们都是通过内核缓冲区实现数据传输。

特点:

它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。

它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。

它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

         pipe用于相关进程之间的通信,例如父进程和子进程,它通过pipe()系统调用来创建并打开,当最后一个使用它的进程关闭对他的引用时,pipe将自动撤销。

        FIFO即命名管道,在磁盘上有对应的节点,但没有数据块——换言之,只是拥有一个名字和相应的访问权限,通过mknode()系统调用或者mkfifo()函数来建立的。一旦建立,任何进程都可以通过文件名将其打开和进行读写,而不局限于父子进程,当然前提是进程对FIFO有适当的访问权。当不再被进程使用时,FIFO在内存中释放,但磁盘节点仍然存在。

        管道的实质是一个内核缓冲区,进程以先进先出的方式从缓冲区存取数据:管道一端的进程顺序地将进程数据写入缓冲区,另一端的进程则顺序地读取数据,该缓冲区可以看做一个循环队列,读和写的位置都是自动增加的,一个数据只能被读一次,读出以后再缓冲区都不复存在了。当缓冲区读空或者写满时,有一定的规则控制相应的读进程或写进程是否进入等待队列,当空的缓冲区有新数据写入或慢的缓冲区有数据读出时,就唤醒等待队列中的进程继续读写。
 

 3.1 无名管道

pipe的例子:父进程创建管道,并在管道中写入数据,而子进程从管道读出数据。

pipe:创建管道        在fork函数之前创建管道,从而保证两个进程处理的同一个管道。

文件描述符:0:1:2:出错        3:读端        4:写端

没数据的时候就会发生阻塞。

read()、close()、write()、pipe()、fork()

3.2 命名管道

access()、select()、open()

        和无名管道的主要区别在于,命名管道有一个名字,命名管道的名字对应于一个磁盘索引节点,有了这个文件名,任何进程有相应的权限都可以对它进行访问。而无名管道却不同,进程只能访问自己或祖先创建的管道,而不能访任意访问已经存在的管道——因为没有名字。FIFO中的内容都存放在内存

        以上命令在当前目录下创建了一个名为myfifo的命名管道。用ls -p命令查看文件的类型时,可以看到命名管道对应的文件名后有一条竖线"|",表示该文件不是普通文件而是命名管道。

        使用open()函数通过文件名可以打开已经创建的命名管道,而无名管道不能由open来打开。当一个命名管道不再被任何进程打开时,它没有消失,还可以再次被打开,就像打开一个磁盘文件一样。

        可以用删除普通文件的方法将其删除,实际删除的事磁盘上对应的节点信息。

例子:用命名管道实现聊天程序,一个张三端,一个李四端。两个程序都建立两个命名管道,fifo1,fifo2,张三写fifo1,李四读fifo1;李四写fifo2,张三读fifo2。

access():判断管道是否存在                open():打开管道

        int mkfifo(const char * pathname,mode_t mode);

        mkfifo()会依参数pathname建立特殊的FIFO文件,该文件必须不存在,而参数mode为该文件的权限(mode%~umask)

2. mkfifo使用方法与open类似,有名管道创建完后,在指定目录中生成文件。
mkfifo调用内核,内核在文件系统中生成文件名,并没有在内核中生成管道。只有在调用open代开有名管道时,才会在内核中创建管道。这个文件不占用磁盘空间,只有innode号,和套接字、字符设备文件、块设备文件一样。普通文件,符号链接文件及目录文件不仅有inode号,还占用磁盘空间。
创建有名管道mkfifo。跟普通文件一样使用,打开open,读read,写write,关闭close。

3.有名管道的特性

3.1 两个进程操作有名管道时,一个读,一个写。当读端RD,写端WR都打开时,程序才会运行,不然会阻塞。(就是管道读写都开打时,open函数才能打开管道)
3.2 有名字,存储于普通文件系统之中
3.3 任何具有相应权限的进程都可以使用open()来获取FIFO的文件描述符
3.4 跟普通文件一样:使用统一的read、write来读写
3.5 具有原子性,支持多写者同时进行写操作而数据不会相互践踏
3.6 First In First Out, 最先被写入FIFO的数据。最先被读写出来
3.7 跟普通文件不同,不能使用lseek()来定位

原文链接:https://blog.youkuaiyun.com/weixin_44522306/article/details/89475859

 三、消息队列

        消息队列,就是一个消息的链表,是一系列保存在内核中消息的列表。用户进程可以向消息队列添加消息,也可以向消息队列读取消息。消息队列与管道通信相比,其优势是对每个消息指定特定的消息类型,接收的时候不需要按照队列次序,而是可以根据自定义条件接收特定类型的消息。可以把消息看做一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息,对消息队列有读权限的进程可以从消息队列中读取消息。消息队列的常用函数如下表:

进程间通过消息队列通信,主要是:创建或打开消息队列,添加消息,读取消息和控制消息队列。 

(一)消息队列的概念
定义:是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。有写权限的进程可以向消息队列中添加新消息;有读权限的进程则可以从消息队列中读走消息。消息本质上是一种数据结构。暂且这样,先要会用,会用了可以继续研究内部实现过程。用都不会用,太纠结实现原理,在有限时间内,是得不偿失的。

(二)通信流程
下面只对用到的函数参数分析及代码解析,最后给一个编程思路。
下面涉及两个文件,msgsend.c与msgreceive.c
基本函数:msgrcv(接收)、msgsnd(发送)、msgget(新建)、msgctl(删除)

原文链接:https://blog.youkuaiyun.com/weixin_39956356/article/details/86652957

(1)新建消息队列

//创建消息队列--类比于open,其返回的标识id,用于msgrcv(接收)、msgsnd(发送)
int msgget(key_t key, int msgflg);

参数:
(1)key:消息队列关联的标识符,本质是整数,可以弄个整数强制转化下就可以了,如(key_t)1234
(2)msgflg:消息队列的建立标志存取权限

(2)消息队列的发送

//消息队列的发送--类比于write
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

参数:

  • msqid : 消息队列的标识码(msgget返回的id)
  • msgp : 指向消息缓冲区的指针,需要用户自己定义一个结构体,系统没有提供(满足第一元素要大于0,第二个是字符数组), 这里是void,要强制转换下,最好这样
  • msgsz : 消息的长短
  • msgflg: 标志位–如果为0,表示忽略该标志位
  • 返回值–On success, zero is returned. On error, -1 is returned

(114条消息) 进程通信之消息队列_我爱加菲猫-优快云博客_进程通信消息队列

四、共享内存

(114条消息) 共享内存(进程通信)_xy913741894的博客-优快云博客_共享内存

        共享内存允许两个或多个进程共享一个给定的存储区,这一段存储区可以被两个或两个以上的进程映射至自身的地址空间中,一个进程写入共享内存的信息,可以被其他使用这个共享内存的进程,通过一个简单的内存读取错做读出,从而实现了进程间的通信。

        采用共享内存进行通信的一个主要好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝,对于像管道和消息队里等通信方式,则需要再内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次:一次从输入文件到共享内存区,另一次从共享内存到输出文件。

         一般而言,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时在重新建立共享内存区域;而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件,因此,采用共享内存的通信方式效率非常高。

 共享内存有两种实现方式:1、内存映射 2、共享内存机制

1、shmget函数
该函数用来创建共享内存,它的原型为:

int shmget(key_ t key, size_t size, int shmflg);

第一个参数,与信号量的semget函数一样,程序需要提供一个参数key(非0整数),它有效地为共享内存段命名,shmget函数成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。调用失败返回-1.

第二个参数,size以字节为单位指定需要共享的内存容量

第三个参数,shmflg是权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。共享内存的权限标志与文件的读写权限一样,举例来说,0644,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。

2、shmat函数
第一次创建完共享内存时,它还不能被任何进程访问,shmat函数的作用就是用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间。它的原型如下:

void *shmat(int shm_id, const void *shm_addr, int shmflg);

第一个参数,shm_id是由shmget函数返回的共享内存标识。
第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。
第三个参数,shm_flg是一组标志位,通常为0。

3、shmdt函数
该函数用于将共享内存从当前进程中分离。注意,将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用。它的原型如下:

int shmdt(const void *shmaddr);

参数shmaddr是shmat函数返回的地址指针,调用成功时返回0,失败时返回-1.

4、shmctl函数
与信号量的semctl函数一样,用来控制共享内存,它的原型如下:

int shmctl(int shm_id, int command, struct shmid_ds *buf);

第一个参数,shm_id是shmget函数返回的共享内存标识符。
第二个参数,command是要采取的操作,它可以取下面的三个值 :
IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID:删除共享内存段

第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。
 

五、信号量

        信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。

1、特点
        信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。支持信号量组。

2、原型
        最简单的信号量是只能取 0 和 1 的变量,这也是信号量最常见的一种形式,叫做二值信号量(Binary Semaphore)。而可以取多个正整数的信号量被称为通用信号量。

        Linux 下的信号量函数都是在通用的信号量数组上进行操作,而不是在一个单一的二值信号量上进行操作。

P操作:信号量值减一,如果值小于0,则挂起该进程,否则直接使用资源
V操作:信号量值加一,如果值依旧小于等于0,说明之前有进程在等待使用这个资源,因此需要唤醒一个等待进程

semget创建或者获得一个信号集



semop对信号集进行P,V操作



semctl对信号集控制,比如删除等等

由于信号量本身并不具备传输数据的能力,它多用于完成互斥,同步等特性,因此,这个实例将结合共享内存信号量

共享内存也是SystemV标准下IPC通信机制的一种,它比我们前面所讲的都要快,但是它的缺点则是不提供同步,互斥机制,因此,我们可以结合自己实现的信号量来提供此机制。

六、信号

1.概念:
1)信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式

2)信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。

3)如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被 取消时才被传递给进程。

2.信号的发送
1)用户按某些按键时产生信号。
2)硬件产生异常时产生信号。
3)进程用kill函数将信号发送给另一个进程。
4)用户用kill系统命令将信号发送给其他进程。

3.信号的处理:

1)忽略信号:对信号不做任何处理,但是有两个信号不能忽略:即SIGKILL及SIGSTOP。

2)捕捉信号:定义信号处理函数,当信号发生时,执行相应的处理函数。

3)执行缺省操作:Linux对每种信号都规定了默认操作

4.常见信号:

SIGINT:ctrl+c 终止信号

SIGQUIT:ctrl+\ 退出信号

SIGSTOP:ctrl+z 暂停信号

SIGSCONT:ctrl+z 继续信号

SIGALRM:闹钟信号 收到此信号后定时结束,结束进程

SIGCHLD:子进程状态改变,父进程收到信号

SIGKILL:杀死信号

5.相关函数:
1)int kill(pid_t pid, int sig);
  功能:信号发送
  参数:pid:指定进程
  sig:要发送的信号
  返回值:成功 0;失败 -1
2)int raise(int sig);
  功能:进程向自己发送信号
  参数:sig:信号
  返回值:成功 0;失败 -1
3)unsigned int alarm(unsigned int seconds)
  功能:在进程中设置一个定时器
  参数:seconds:定时时间,单位为秒
  返回值:如果调用此alarm()前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。

注意:一个进程只能有一个闹钟时间。如果在调用alarm时已设置过闹钟时间,则之前的闹钟时间被新值所代替

4)int pause(void);
  功能:用于将调用进程挂起直到收到信号为止。

5)void (*signal(int signum, void (*handler)(int)))(int);
  或者:
  typedef void (*sighandler_t)(int);
  sighandler_t signal(int signum, sighandler_t handler);
  功能:信号处理函数
  参数:signum:要处理的信号//不能是SIGKILL和SIGSTOP
    handler:SIG_IGN:忽略该信号。
    SIG_DFL:采用系统默认方式处理信号。
    自定义的信号处理函数指针
  返回值:成功:设置之前的信号处理方式;失败:SIG_ERR

6)void abort(void);

给自己发送异常终止信号,(6.SIGABRO)终止并产生core文件。

原文链接:

(114条消息) 信号量(进程通信)_xy913741894的博客-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_42475191

谢谢老板

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值