通信机制在linux(linux中断机制)

这篇文章给大家聊聊关于通信机制在linux,以及linux中断机制对应的知识点,希望对各位有所帮助,不要忘了收藏本站哦。

linux系统的进程间通信有哪几种方式

一、方式

1、管道(Pipe)及有名管道( mkpipe):

管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;

2、信号(Signal):

信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身。

linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction。

实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数。

3、消息队列(Message):

消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。

4、共享内存:

使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。

5、信号量(semaphore):

主要作为进程间以及同一进程不同线程之间的同步手段。

6、套接口(Socket):

更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。

二、概念

进程间通信概念:

IPC—-InterProcess Communication

每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到所以进程之间要交换数据必须通过内核。

在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信。

扩展资料

1)无名管道:

管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)。

管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,构成两进程间通信的一个媒介。

数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

2)有名管道:

不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信(能够访问该路径的进程以及FIFO的创建进程之间)。

因此,通过FIFO不相关的进程也能交换数据。值得注意的是,FIFO严格遵循先进先出(first in first out),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。它们不支持诸如lseek()等文件定位操作。

Linux进程间通信

linux下进程间通信的几种主要手段简介:

一般文件的I/O函数都可以用于管道,如close、read、write等等。

实例1:用于shell

管道可用于输入输出重定向,它将一个命令的输出直接定向到另一个命令的输入。比如,当在某个shell程序(Bourne shell或C shell等)键入who│wc-l后,相应shell程序将创建who以及wc两个进程和这两个进程间的管道。

实例二:用于具有亲缘关系的进程间通信

管道的主要局限性正体现在它的特点上:

有名管道的创建

小结:

管道常用于两个方面:(1)在shell中时常会用到管道(作为输入输入的重定向),在这种应用方式下,管道的创建对于用户来说是透明的;(2)用于具有亲缘关系的进程间通信,用户自己创建管道,并完成读写操作。

FIFO可以说是管道的推广,克服了管道无名字的限制,使得无亲缘关系的进程同样可以采用先进先出的通信机制进行通信。

管道和FIFO的数据是字节流,应用程序之间必须事先确定特定的传输"协议",采用传播具有特定意义的消息。

要灵活应用管道及FIFO,理解它们的读写规则是关键。

信号生命周期

信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。

可以从两个不同的分类角度对信号进行分类:(1)可靠性方面:可靠信号与不可靠信号;(2)与时间的关系上:实时信号与非实时信号。

(1)可靠信号与不可靠信号

不可靠信号:Linux下的不可靠信号问题主要指的是信号可能丢失。

可靠信号:信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号,可靠信号克服了信号可能丢失的问题。Linux在支持新版本的信号安装函数sigation()以及信号发送函数sigqueue()的同时,仍然支持早期的signal()信号安装函数,支持信号发送函数kill()。

对于目前linux的两个信号安装函数:signal()及sigaction()来说,它们都不能把SIGRTMIN以前的信号变成可靠信号(都不支持排队,仍有可能丢失,仍然是不可靠信号),而且对SIGRTMIN以后的信号都支持排队。这两个函数的最大区别在于,经过sigaction安装的信号都能传递信息给信号处理函数(对所有信号这一点都成立),而经过signal安装的信号却不能向信号处理函数传递信息。对于信号发送函数来说也是一样的。

(2)实时信号与非实时信号

前32种信号已经有了预定义值,每个信号有了确定的用途及含义,并且每种信号都有各自的缺省动作。如按键盘的CTRL ^C时,会产生SIGINT信号,对该信号的默认反应就是进程终止。后32个信号表示实时信号,等同于前面阐述的可靠信号。这保证了发送的多个实时信号都被接收。实时信号是POSIX标准的一部分,可用于应用进程。非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。

发送信号的主要函数有:kill()、raise()、 sigqueue()、alarm()、setitimer()以及abort()。

调用成功返回 0;否则,返回-1。

sigqueue()是比较新的发送信号系统调用,主要是针对实时信号提出的(当然也支持前32种),支持信号带有参数,与函数sigaction()配合使用。

sigqueue的第一个参数是指定接收信号的进程ID,第二个参数确定即将发送的信号,第三个参数是一个联合数据结构union sigval,指定了信号传递的参数,即通常所说的4字节值。

sigqueue()比kill()传递了更多的附加信息,但sigqueue()只能向一个进程发送信号。sigqueue()比kill()传递了更多的附加信息,但sigqueue()只能向一个进程发送信号。

inux主要有两个函数实现信号的安装: signal()、 sigaction()。其中signal()在可靠信号系统调用的基础上实现,是库函数。它只有两个参数,不支持信号传递信息,主要是用于前32种非实时信号的安装;而sigaction()是较新的函数(由两个系统调用实现:sys_signal以及sys_rt_sigaction),有三个参数,支持信号传递信息,主要用来与 sigqueue()系统调用配合使用,当然,sigaction()同样支持非实时信号的安装。sigaction()优于signal()主要体现在支持信号带有参数。

消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息。消息队列是随内核持续的

消息队列的内核持续性要求每个消息队列都在系统范围内对应唯一的键值,所以,要获得一个消息队列的描述字,只需提供该消息队列的键值即可;

消息队列与管道以及有名管道相比,具有更大的灵活性,首先,它提供有格式字节流,有利于减少开发人员的工作量;其次,消息具有类型,在实际应用中,可作为优先级使用。这两点是管道以及有名管道所不能比的。同样,消息队列可以在几个进程间复用,而不管这几个进程是否具有亲缘关系,这一点与有名管道很相似;但消息队列是随内核持续的,与有名管道(随进程持续)相比,生命力更强,应用空间更大。

信号灯与其他进程间通信方式不大相同,它主要提供对进程间共享资源访问控制机制。相当于内存中的标志,进程可以根据它判定是否能够访问某些共享资源,同时,进程也可以修改该标志。除了用于访问控制外,还可用于进程同步。信号灯有以下两种类型:

int semop(int semid, struct sembuf*sops, unsigned nsops); semid是信号灯集ID,sops指向数组的每一个sembuf结构都刻画一个在特定信号灯上的操作。

int semctl(int semid,int semnum,int cmd,union semun arg)

该系统调用实现对信号灯的各种控制操作,参数semid指定信号灯集,参数cmd指定具体的操作类型;参数semnum指定对哪个信号灯操作,只对几个特殊的cmd操作有意义;arg用于设置或返回信号灯信息。

进程间需要共享的数据被放在一个叫做IPC共享内存区域的地方,所有需要访问该共享区域的进程都要把该共享区域映射到本进程的地址空间中去。系统V共享内存通过shmget获得或创建一个IPC共享内存区域,并返回相应的标识符。内核在保证shmget获得或创建一个共享内存区,初始化该共享内存区相应的shmid_kernel结构注同时,还将在特殊文件系统shm中,创建并打开一个同名文件,并在内存中建立起该文件的相应dentry及inode结构,新打开的文件不属于任何一个进程(任何进程都可以访问该共享内存区)。所有这一切都是系统调用shmget完成的。

shmget()用来获得共享内存区域的ID,如果不存在指定的共享区域就创建相应的区域。shmat()把共享内存区域映射到调用进程的地址空间中去,这样,进程就可以方便地对共享区域进行访问操作。shmdt()调用用来解除进程对共享内存区域的映射。shmctl实现对共享内存区域的控制操作。这里我们不对这些系统调用作具体的介绍,读者可参考相应的手册页面,后面的范例中将给出它们的调用方法。

注:shmget的内部实现包含了许多重要的系统V共享内存机制;shmat在把共享内存区域映射到进程空间时,并不真正改变进程的页表。当进程第一次访问内存映射区域访问时,会因为没有物理页表的分配而导致一个缺页异常,然后内核再根据相应的存储管理机制为共享内存映射区域分配相应的页表。

详解Linux内核通信netlink机制

详解Linux内核通信netlink机制

netlink socket是一种用于内核态和用户态进程之间进行数据传输的特殊IPC机制。它通过为内核模块提供一组特殊的API,并为用户程序提供了一组标准的socket接口的方式,实现了一种全双工的通讯连接。

Netlink socket使用地址族AF_NETLINK,每一个netlink socket在内核头文件include/linux/netlink.h中定义自己的协议类型。它提供了一种异步通讯方式,与其他socket API一样,它提供了一个socket队列来缓冲或平滑瞬时的消息高峰。发送netlink消息的系统调用在把消息加入到接收者的消息队列后,会触发接收者的接收处理函数。接收者在接收处理函数上下文中,可以决定立即处理消息还是把消息放在队列中,在以后其他上下文去处理它。

内核中实现系统调用的代码都是在编译时静态链接到内核的,因此,在动态加载模块中去包含一个系统调用的做法是不合适的。使用netlink socket时,动态加载模块中的netlink程序不会和linux内核中的netlink部分产生任何编译时依赖关系。

Netlink优于系统调用、ioctls和proc文件系统的另一个特点就是它支持多点传送。一个进程可以把消息传输给一个netlink组地址,然后任意多个进程都可以监听那个组地址,并接收消息。这种机制为内核到用户态的事件分发提供了一种近乎完美的解决方案。

系统调用和ioctl都属于单工方式的IPC,也就是说,这种IPC会话的发起者只能是用户态程序。然而,如果内核有一个紧急的消息想要通知给用户态程序时,该怎么办呢?如果直接使用这些IPC的话,是没办法做到这点的。通常情况下,应用程序会周期性地轮询内核以获取状态的改变,然而,高频度的轮询势必会增加系统的负载。Netlink通过允许内核初始化会话的方式完美地解决了此问题,我们称之为netlink socket的双工特性。

Netlink Socket的API包括标准的socket API函数-socket(), sendmsg(), recvmsg()和close()。使用socket()函数创建一个socket,输入:int socket(int domain, int type, int protocol)。跟TCP/IP中的socket一样,netlink的bind()函数把一个本地socket地址与一个打开的socket进行关联,netlink地址结构体如下。另一个结构体 struct sockaddr_nl nladdr作为目的地址。如果这个netlink消息是发往内核的话,nl_pid属性和nl_groups属性都应该设置为0。如果这个消息是发往另一个进程的单点传输消息,nl_pid应该设置为接收者进程的PID,nl_groups应该设置为0。netlink消息同样也需要它自身的消息头,这样做是为了给所有协议类型的netlink消息提供一个通用的背景。

内核空间的netlink API接口由内核中的netlink核心代码支持,在net/core/af_netlink.c中实现。从内核的角度来说,API接口与用户空间的API是不一样的。内核模块通过这些API访问netlink socket并且与用户空间的程序进行通讯。在用户空间,我们通过socket()调用来创建一个netlink socket,而在内核空间,我们调用如下的API:struct sock* netlink_kernel_create(int unit, void(*input)(struct sock*sk, int len))。参数uint是netlink协议类型,例如NETLINK_TEST。函数指针,input,是netlink socket在收到消息时调用的处理消息的回调函数指针。在内核创建了一个NETLINK_TEST类型的netlink socket后,无论什么时候,只要用户程序发送一个NETLINK_TEST类型的netlink消息到内核的话,通过netlink_kernel_create()函数注册的回调函数input()都会被调用。

使用skb= skb_recv_datagram(nl_sk)来接收消息,nl_sk是netlink_kernel_create()函数返回的netlink socket,然后,只需要处理skb->data指针指向的netlink消息就可以了。从内核中发送netlink消息就像从用户空间发送消息一样,内核在发送netlink消息时也需要设置源netlink地址和目的netlink地址。假设结构体struct sk_buff* skb指向存储着要发送的netlink消息的缓冲区,源地址可以这样设置。从内核空间关闭netlink socket,netlink_kernel_create()函数返回的netlink socket为struct sock*nl_sk,我们可以通过访问下面的API来从内核空间关闭这个netlink socket:sock_release(nl_sk->socket);

阅读剩余
THE END