进程间通信
管道
System V IPC
POSIX IPC
从一个进程连接到另一个进程的一个数据流称为一个“管道”

注:
补充:
管道是半双工通信
管道的本质是内核中的缓冲区
管道自带同步(没有数据读阻塞,缓冲区写满写阻塞)与互斥
多个进程只要能够访问同一管道就可以实现通信,不限于读写个数
管道的生命周期随进程,管道文件只是标识,删除后依然可以通信
- 匿名管道供具有血缘关系的进程,进行进程间通信(常见于父子进程)

#include <unistd.h>
功能:创建一无名管道
原型
int pipe(int fd[2]);
参数
fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
返回值:成功返回0,失败返回错误代码
RETURN VALUE
On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
用fork来共享管道原理

注:
站在文件描述符角度-深度理解管道

站在内核角度-管道本质

代码实列:
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int pipe_fd[2]={0};
if(pipe(pipe_fd)<0)
{
perror("pipe");
return 1;
}
printf("%d,%d\n",pipe_fd[0],pipe_fd[1]);
pid_t id=fork();
if(id<0)
{
perror("fork");
return 2;
}
else if(id==0)
{
close(pipe_fd[0]);
const char* msg="hello parent,i am child";
int count=5;
while(count)
{
write(pipe_fd[1],msg,strlen(msg));
sleep(1);
count--;
}
close(pipe_fd[1]);
exit(0);
}
else
{
close(pipe_fd[1]);
char buffer[64];
while(1)
{
ssize_t size=read(pipe_fd[0],buffer,sizeof(buffer)-1);
if(size>0)
{
buffer[size]=0;
printf("parent get messge from child# %s\n",buffer);
}
else if(size==0)
{
printf("pipe file close,child quit!\n");
break;
}
else
{
perror("read");
break;
}
}
int status=0;
if(waitpid(id,&status,0)>0)
{
printf("child quit,wait success!\n");
}
close(pipe_fd[0]);
}
return 0;
}
注意:读端从管道成功读取数据之后管道里面的数据会设置成无效
补充:
- 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
- 管道提供流式服务
- 一般而言,进程退出,管道释放,所以管道的生命周期随进程
- 一般而言,内核会对管道操作进行同步与互斥
- 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道
管道自带同步机制检验:



补充:


注:读取端关闭,一直写是毫无意义的,本质就是在浪费系统资源,写进程会立刻被OS终止掉(通过发送信号的方式),获取终止信号可以由waitpid获取
管道大小的检验:

结论:管道大小为64KB
注:用man 7 pipe查看PIPE_BUF
总结:
补充:

进程退出,曾经打开的文件也会被关掉。管道也是文件,管道的生命周期是随进程的
- 不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
- 命名管道是一种特殊类型的文件
注:
mkfifo filename


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

注:用man mkfifo查看命令行中的mkfifo;用man 3 mkfifo查看程序中的mkfifo
匿名管道与命名管道的区别:
总结:匿名管道与命名管道只是进程间的关系的问题(匿名管道进程间有血缘关系、命名管道进程间毫无关系),此外匿名管道有的管道特点命名管道也都有

总结:
共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据

注:

- system V共享内存达到通信的步骤:
- 申请共享内存
- 进程A和进程B分别挂接对应的共享内存到自己的地址空间(共享内存)
- 双方就看到了同一份资源,既可以进行正常通信了
注:上述的这些步骤,有对应的系统调用接口,给提供服务
shmget函数
功能:用来创建共享内存
原型
int shmget(key_t key, size_t size, int shmflg);
参数
key:这个共享内存段名字
size:共享内存大小
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1


注:




注:nattch指的是与当前共享内存关联的进程的个数
补充:

注意:“dest”状态
Linux下删除任何内容,都会先检查一下这个内容的引用计数(就是文件的使用数,n个进程使用,引用计数为n)。若引用计数为0,就会真正的删除该内容(这里就是删除共享内存)。不为0,表示仍有进程使用,则正在使用的进程可以正常使用,直至引用计数降为0后,系统才会将该内容真正意义上的删除掉。
对这里用共享内存来说同理,显示“dest”是表示该共享内存已经被删除但有进程还在使用它。这时操作系统将共享内存的mode标记为SHM_DEST,key标记为0x00000000,并对外显示status为“dest”。当用户调用shmctl的IPC_RMID时,系统会先查看这个共享内存的引用计数,如果引用计数为0,就会销毁这段共享内存,否者设置这段内存的mod的mode位为SHM_DEST,如果所有进程都不用则删除这段共享内存。


shmctl函数
功能:用于控制共享内存
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1


shmat函数
功能:将共享内存段连接到进程地址空间
原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
shmid: 共享内存标识
shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1

说明:
- shmaddr为NULL,核心自动选择一个地址
- shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
- shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)
- shmflg=SHM_RDONLY,表示连接操作用来只读共享内存

shmdt函数
功能:将共享内存段与当前进程脱离
原型
int shmdt(const void *shmaddr);
参数
shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段


注:System V进程间通信不需要用系统调用函数read、write的方式来实现通信

总结:
补充:
- 共享内存的大小,系统在分配shm的时候,是按照4KB为基本单位的(下图中的共享内存大小为4097,在OS中分配为4096+4096其中4095大小的空间会浪费)
- key:是一个用户层的唯一键值,核心作用是为了区分“唯一性”,不能用来进行IPC资源的操作(key类比于inode号)
- shmid:是一个系统给我们返回的IPC资源标识符,用来进行操作IPC资源(shmid类比于文件的fd)
- 用make和makefile编译多个源文件
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
__kernel_time_t shm_atime; /* last attach time */
__kernel_time_t shm_dtime; /* last detach time */
__kernel_time_t shm_ctime; /* last change time */
__kernel_ipc_pid_t shm_cpid; /* pid of creator */
__kernel_ipc_pid_t shm_lpid; /* pid of last operator */
unsigned short shm_nattch; /* no. of current attaches */
unsigned short shm_unused; /* compatibility */
void *shm_unused2; /* ditto - used by DIPC */
void *shm_unused3; /* unused */
};

注:
IPC资源在内核中是数组维护的(是通过类似于C++中的切片的方式用ipc_perm来找到共享内存、消息队列、信号量)
补充:
共享内存实现通信的原理是因为所有进程操作映射同一块物理内存
共享内存的操作是非进程安全的
共享内存只有在当前映射链接数为0时,才会真正被删除
共享内存生命周期随内核,只要不删除,就一直存在于内核中,除非重启系统
共享内存是将同一块物理内存映射到各个进程虚拟地址空间,可以直接通过虚拟地址访问,相较于其它方式少了两步内核态与用户态之间的数据拷贝因此速度最快
ipcs 查看进程间通信资源/ipcrm 删除进程间通信资源:


- 让进程看到同一份资源(内存空间)——临界资源
- 进程内的所有的代码,不是全部的代码都是访问临界资源,而是只有一部分在访问,可能造成数据不一致问题的是这部分少量的代码——临界区
- 为了避免数据不一致,保护临界资源,需要对临界区代码进行某种保护(互斥)
- 互斥:一部分空间任何时候,有且只有一个进程在进行访问,串行化的执行(eg:锁、二元信号量)
- 加锁和解锁是有对应的代码的。本质是对临界去进行加锁和解锁,完成互斥操作


补充:
- 原子性(感性认识):要么做了,要么没做
- 信号量:本质是一个计数器。用来描述临界资源中资源数目的计数器
- PV原语:
- P操作:申请信号量(成功:一定有一个临界资源给你使用)
- V操作:释放信号量
- 多个信号量不能操作同一个count值(信号量不等于count)
- 信号量可以保护临界资源的安全性(操作计数器时是有多条语句构成,有可能多份配资源出去,不是原子性的)
- 信号量本身就是一个临界资源,要保护其他临界资源,先得保护自己的安全(PV操作是原子的)
- 信号量在多进程环境下,可以通过semget,semctl,ftok()等系统接口来保证信号量被多个进程看到
- 如果信号量的计数器的值是:1 (1、0:二元信号量(互斥语义))


我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',
我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是
MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO
我正在尝试使用以下代码通过将ffmpeg实用程序作为子进程运行并获取其输出并解析它来确定视频分辨率:IO.popen'ffmpeg-i'+path_to_filedo|ffmpegIO|#myparsegoeshereend...但是ffmpeg输出仍然连接到标准输出并且ffmepgIO.readlines是空的。ffmpeg实用程序是否需要一些特殊处理?或者还有其他方法可以获得ffmpeg输出吗?我在WinXP和FedoraLinux下测试了这段代码-结果是一样的。 最佳答案 要跟进mouviciel的评论,您需要使用类似pope
我目前正在用Ruby编写一个项目,它使用ActiveRecordgem进行数据库交互,我正在尝试使用ActiveRecord::Base.logger记录所有数据库事件具有以下代码的属性ActiveRecord::Base.logger=Logger.new(File.open('logs/database.log','a'))这适用于迁移等(出于某种原因似乎需要启用日志记录,因为它在禁用时会出现NilClass错误)但是当我尝试运行包含调用ActiveRecord对象的线程守护程序的项目时脚本失败并出现以下错误/System/Library/Frameworks/Ruby.frame
我想从rubyrake脚本运行一个可执行文件,比如foo.exe我希望将foo.exe的STDOUT和STDERR输出直接写入我正在运行rake任务的控制台.当进程完成时,我想将退出代码捕获到一个变量中。我如何实现这一目标?我一直在玩backticks、process.spawn、system但我无法获得我想要的所有行为,只有部分更新:我在Windows上,在标准命令提示符下,而不是cygwin 最佳答案 system获取您想要的STDOUT行为。它还返回true作为零退出代码,这可能很有用。$?填充了有关最后一次system调
A/ctohttp://wiki.nginx.org/CoreModule#usermaster进程曾经以root用户运行,是否可以以不同的用户运行nginxmaster进程? 最佳答案 只需以非root身份运行init脚本(即/etc/init.d/nginxstart),就可以用不同的用户运行nginxmaster进程。如果这真的是你想要做的,你将需要确保日志和pid目录(通常是/var/log/nginx&/var/run/nginx.pid)对该用户是可写的,并且您所有的listen调用都是针对大于1024的端口(因为绑定(
我有一个应用程序正在从Ruby迁移到JRuby(由于需要通过Java提供更好的Web服务安全支持)。我使用的gem之一是daemons创建后台作业。问题在于它使用fork+exec来创建后台进程,但这对JRuby来说是禁忌。那么-是否有用于创建后台作业的替代gem/wrapper?我目前的想法是只从shell脚本调用rake并让rake任务永远运行......提前致谢,克里斯。更新我们目前正在使用几个与Java线程相关的包装器,即https://github.com/jmettraux/rufus-scheduler和https://github.com/philostler/acts
在尝试实现应用auto_orient的过程之后!对于我的图片,我收到此错误:ArgumentError(noimagesinthisimagelist):app/uploaders/image_uploader.rb:36:in`fix_exif_rotation'app/controllers/posts_controller.rb:12:in`create'Carrierwave在没有进程的情况下工作正常,但在添加进程后尝试上传图像时抛出错误。流程如下:process:fix_exif_rotationdeffix_exif_rotationmanipulate!do|image|