Linux进程间通信

xiaoxiao2021-02-27  213

原始用于进程间通信,只能是通过父子进程之间,或者兄弟进程之间,通信机制有管道、信号、跟踪:ptrace后来可以进行任意进程间的通信: 命名管道:推广自无名管道、消息队列、共享内存、信号量再后来不仅可以进行同一台计算机上的进程间通信,还可以不同计算机之间通过网络进行通信的机制socket(命名管道的推广)

管道 管道是用于父子进程或者兄弟进程之间的,只有在同一进程在相同模式下打开同一个文件,才共享同一个文件结构,一个管道是一个存在内存上的无形的文件,对这个文件的操作需要通过两个已打开文件来进行,分别代表管道的两端;管道是一种特殊的文件,并不属于某种特定的文件系统,而是自己构成一种独立的文件系统;管道是一个环形缓冲区;管道的设计其实就是一个生产者/消费者模式,一端不停的写,一端不停地读;如果有任意一端关闭,对于另外一端都没有存在的意义,都会给另一端发一个SIGPIPE信号; 1、无名管道 用于父子进程(兄弟进程)之间的,数据存在内存上;无名管道由系统调用pipe创建,一般用于fork产生一个子进程,方便父子进程之间的通信,此管道由父进程创建,子进程“与生俱来”; 2、有名管道 有名管道可以用于任意进程间进行通信,因为是有名管道,所以在磁盘上需要给对应的文件分配一个inode节点记录文件的名字和路径;数据还是放在内存上的;

信号 在所有进程间通信,只有信号是异步的。在task_struct中有一个指针struct signal_struct *signal,指向一个signal_struct结构,我们称这个结构为“信号向量表”,和中断向量表不同的是,中断向量表在系统空间中,所指向的中断处理程序也在系统空间中,虽然,信号向量表在系统空间,但是信号处理程序却一般在用户空间,“信号向量”有其特殊之处,它不仅可以指向一个信号处理程序,也可以指向SIG_DFL和SIG_IGN之一,分别表示应该对信号采取“默认”的反应或者忽略而不做任何处理。 对信号的检测与响应,总是发生在系统空间,通常发生在两种情况下: 1、当前进程由于系统调用、中断或者异常而进入系统空间,从系统空间返回到用户空间的前夕; 2、当前进程在内核中,进入睡眠以后刚被唤醒的时候,由于信号的存在而提前返回到用户空间; 防止信号嵌套处理: 当正在处理一个信号的时候,为了防止到来的信号,同时也要触发信号处理程序,造成嵌套信号处理,对资源的浪费,需要对到来的信号采用屏蔽的手段,当前的信号处理完成,还是会恢复处理中到来的信号的; 一个进程可以通过信号屏蔽位图来暂时扣押所接受到的信号; 信号一般只能发给属于同一个session以及同一个用户的进程,除非信号能获得特权级的权限; 信号处理是模拟中断处理的过程的,信号属于一种软中断,当发生信号的时候,需要从用户空间向内核空间的切换,那么这个时候,和中断、系统调用是一样的,我们需要把用户空间的信息通过SAVE_ALL将用户堆栈的信息压入系统堆栈中,又通过RESTORE_ALL进行恢复;

管道的缺陷: <1>所载送的格式是无格式字节流,如果发送两种不同的结构字节,那么如果进程间没有规定协议或者特殊的符号进行区别的话,就会发生TCP粘包; <2>管道的缓冲区的大小是有限的、静态的,当发送者写满了缓冲区,接受者没有及时读取,那么发送者只好停下俩睡眠,增强了管道机制的同步性要求,在某些特殊的场合中,这种机制就很有局限性,一般这种情况都将发送者对应端的描述符设置为O_NONBLOCK标志,告诉发送者缓冲区已满,就返回,不用睡眠; <3>相对于发送信息量少的时候,对于管道的开销很大,那么一个字节一个字节发送的时候,对于管道的利用率就降低了;

消息队列(报文传递) 消息队列是在进程之间传递二进制块数据的一种简单有效的方式,每个数据块都有一个特定的类型,接收方可以根据类型来有选择地接受数据,而不用像管道那样必须以先进先出的方式接受数据; “sem”–信号量 “msg”–报文传递 “shm”–共享内存 信号量、消息队列、共享内存的底层都调用的是sys_ipc();由函数sys_ipc中传入的call操作码的不同,分别处理三种进程间通信的12种不同的操作;

asmlinkage int sys_ipc (uint call, int first, int second, int third, void __user *ptr, long fifth) { int version, ret; version = call >> 16; /* hack for backward compatibility */ call &= 0xffff; switch (call) { case SEMOP: return sys_semtimedop (first, (struct sembuf __user *)ptr, second, NULL); case SEMTIMEDOP: return sys_semtimedop(first, (struct sembuf __user *)ptr, second, (const struct timespec __user *)fifth); case SEMGET: return sys_semget (first, second, third); case SEMCTL: { union semun fourth; if (!ptr) return -EINVAL; if (get_user(fourth.__pad, (void __user * __user *) ptr)) return -EFAULT; return sys_semctl (first, second, third, fourth); } case MSGSND: return sys_msgsnd (first, (struct msgbuf __user *) ptr, second, third); case MSGRCV: switch (version) { case 0: { struct ipc_kludge tmp; if (!ptr) return -EINVAL; if (copy_from_user(&tmp, (struct ipc_kludge __user *) ptr, sizeof (tmp))) return -EFAULT; return sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, third); } default: return sys_msgrcv (first, (struct msgbuf __user *) ptr, second, fifth, third); } case MSGGET: return sys_msgget ((key_t) first, second); case MSGCTL: return sys_msgctl (first, second, (struct msqid_ds __user *) ptr); case SHMAT: switch (version) { default: { ulong raddr; ret = do_shmat (first, (char __user *) ptr, second, &raddr); if (ret) return ret; return put_user (raddr, (ulong __user *) third); } case 1: /* iBCS2 emulator entry point */ if (!segment_eq(get_fs(), get_ds())) return -EINVAL; /* The "(ulong *) third" is valid _only_ because of the kernel segment thing */ return do_shmat (first, (char __user *) ptr, second, (ulong *) third); } case SHMDT: return sys_shmdt ((char __user *)ptr); case SHMGET: return sys_shmget (first, second, third); case SHMCTL: return sys_shmctl (first, second, (struct shmid_ds __user *) ptr); default: return -ENOSYS; } }

消息队列是通过一个key键值进行表示的,不是通过文件名,所以并不纳入文件系统,不占用文件号;每个进程可以通过msgget创建一个消息队列,也可以获得一个key对应的访问途径,发送进程可以通过库函数msgsnd往消息队列中添加消息,接收进程可以通过msgrcv接受指定队列中的消息; <1>当标志位为MSGGET时,底层调用sys_msgget ,这个函数有两种不同的的作用,是通过函数中的标志位IPC_PRIVATE来判断的,当IPC_PRIVATE为1时创建,为0时查找,都返回队列对应的标识号; 每个队列对应一个队列号,这个队列号是全局唯一标识的,打开文件号只局限于一个进程中; 一个已经建立的消息队列并不是谁都可以访问的,只有属于同一用户或者同一组的,或者超级用户,才有权限访问; <2>库函数msgsnd——报文发送 通过函数msgget创建或者获得一个队列,那么通过函数msgsnd就可以往获得的队列中发送数据;队列中的每一个报文段占一个页面,页面的开头是结构msg_msgseg,真正的报文段大小是页面的大小减去结构msg_msgseg; <3>库函数msggrcv——报文接受 <4>库函数msgctl ——报文机制的控制和设置 管道的缺点之一是缺乏对管道的控制手段,也缺乏获取其状态信息(如已经有多少个字节在管道中等待着读取),MSGCTL就提供了这样的功能;

共享内存 两个或者多个进程可以访问同一块内存空间;共享内存是最高效的IPC机制,因为共享内存并不需要缓存数据;这种高效带来的问题就是,我们必须用其它的辅助手段来同步进程对共享内存的访问,否则会产生竟态条件。 <1>shmget()—-创建一段新的共享内存或者获取一段已经存在的共享内存; int shmget(key_t key,size_t size,int shmflg); key参数是一个键值,用来标识一段全局唯一的共享内存,size参数指定共享内存的大小,单位是字节; shmget中shmflg有两个特殊的标志:SHM_HUGETLB,类似于mmap的MAP_HUGETLB标志,系统将使用“大页面”来为共享内存分配空间;SHM_NORESERVE,类似于mmap的MAP_NORESERVE标志,对该共享内存执行写操作将触发SIGSEGV信号; <2>shmat()—-将内存区映射到本进程中的虚拟空间; <3>shmdt()—-将关联到shm_addr处的共享内存从进程中分离; <4>shmctl()—-对共享内存区进行管理和控制;

信号量 进程间的同步机制;程序对共享资源的访问的代码只是很短的一段,就是这一段代码引起了进程之间的竞态条件,我们称这段关键代码为临界区,共享资源叫做临界资源; 信号量中的P、V操作,现有信号量SV; P—–如果SV大于0,就减1,如果为0,则挂起; V—–如果有进程因为等待信号量SV而挂起,就唤醒;如果没有,则将SV加1;

转载请注明原文地址: https://www.6miu.com/read-9518.html

最新回复(0)