unix环境高级编程--进程间通信机制

1.进程间通信机制

a) 管道(管道引用结束,管道中内容全部删除)【读写内核缓冲区】

i. 缺点:只能承载无格式字节流;缓冲区大小受限

ii. 匿名管道(数据通过内核流通、双向流通需要两条管道(或popen,pclose))

1. 半双工、只能在具有血缘关系的进程之间使用

2. 匿名管道会在关闭两端之后由系统自动销毁回收

3. 使用示例

Int fd[2]; //创建数组

Pipe(fd); //创建管道

Pid_t pid=fork(); //创建子进程(血缘关系的进程)

If(pid>0){ //如果是父进程

Close(fd[0]); //关闭读通道

Write(fd[1],XXXX); //

Wait(); //等待子进程结束后再结束父进程,以防孤儿进程(忽略sigClD变僵死进程)

}

Else{

Close(fd[1]);

Read(fd[0],XXXX);

}

iii. 命名管道(FIFO)(管道满时的阻塞问题:如果一直不读管道,写端将阻塞)

1. 命名管道是一种特殊类型的文件,它在系统中以文件名的形式存在,但行为和匿名管道类似。本质是不相关进程读写同个特殊文件

2. 读写可选,但边读边写,显然会数据交叉,半双工

3. 命名管道安全问题。多个进程向命名管道中写入数据,导致这些数据交叉失效。解决方法:1.当数据量小于管道缓存时,写端们要么全部写入,要么一个字节都不要写,这样数据就不会交叉了。2.同步使用管道

4. Open调用设置成阻塞(防止其他进程在对FIFO进行写操作);也可以设置成非阻塞

5. 使用示例

//.cpp

Int resfd=mkfifo(“c:/my_fifo”,0777); //创建命名管道

Int pipefd=Open(“c:/my_fifo”,O_WRONLY); //打开命名管道

Write(pipefd,buffer,buffer_size); //往管道中写东西

//.cpp

Int pipefd=open(“c:/my_fifo”,O_RDONLY); //打开已经创建的命名管道

Read(pipefd,buffer,buffer_size); //往管道中读东西

b) 消息队列

i. 优点:

1. 传输数据块,而不是无格式字节流;可以有选择的接收数据,而不是所有数据都接收(管道)

2. 没有缓冲区大小受限这一说

3. 不相关进程通信

4. 无管道的同步阻塞问题

ii. 缺点:

1. 消息队列中的消息不接收,就会一直存在在那儿(管道在最后一个进程引用结束后会自动删除(至少是里面的数据是这样)),直至显式的删除消息队列或者删除,接收完消息

iii. 使用示例

//接收.进程

Int msgfd=msgget((key_t)1234, 0666 | IPC_CREAT); //创建或获取管道

Msgrcv(msgid, (void*)&data, BUFSIZ, msgtype, 0); //获取消息

//发送.进程

Int msgfd=msgget((key_t)1234, 0666 | IPC_CREAT); //创建或获取管道

Msgsnd(msgid, (void*)&data, BUFSIZ, msgtype, 0); //发送消息

 

c) 共享内存【类似单缓冲的缓冲区

i. 类似于单缓冲的缓冲区,一人写,大家都读写不了,一人读,大家都写不了,进程一多,同步多,性能差

ii. 将同一块内存映射到不同进程的地址。不同进程通过操作同一块内存来满足进程间通信的需求。因此共享内存上做的操作将立即反应到其他进程,所以需要使用信号量,来同步的使用共享内存

iii. 包括Mmap系统调用,Posix共享内存,以及系统V共享内存

1. Mmap实现步骤:是将普通文件映射到内存中,那么大家共同操作这个文件,就相当于共同操作了一块内存。

2. 系统V共享内存实现步骤:

a) Mmap是通过映射一个普通文件实现共享内存的,而系统V则是通过映射特殊文件系统shm中的文件实现进程间的共享内存通信的

b) shmget创建共享内存区(IPC内存区);用shmat将共享内存区映射到具体的进程空间;用完之后,用shmdt撤销映射操作。

c) 使用示例

shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT); //创建共享内存

Void *shm = shmat(shmid, (void*)0, 0);//将共享内存连接到当前进程的地址空间

struct shared_use_st *shared=(struct shared_use_st*)shm;//转换格式

strncpy(shared->text, buffer, TEXT_SZ);//就像文件一样写

shmdt(shm);//把共享内存从当前进程中分离

进程间通信机制如命名管道、消息队列、信号量、共享内存都不是进程的变量,进程结束不会销毁(有key_t);而线程的同步手段互斥量、条件变量、屏障、自旋锁、读写锁都是进程自己的自动变量或全局变量会在进程结束之后,自动销毁。

d) 信号

e) 套接字

 

f) 进程间通讯机制选择和一些看法IPC只用socket】【类似双缓冲的缓冲区

i. 管道:匿名管道需要血缘关系才能用,命名管道是个特殊文件,使用结束后,文件内容清空,但文件不删除。无论是匿名还是命名,总之管道是半双工的,进程如果需要双向通信,那还得创建两条管道,太麻烦,我不选

ii. 消息队列:和socketpipe相比,不是文件描述符,有自己的系统调用函数,无法使用IO复用(selectpollepoll)总觉得不够友好。而且消息队列需要自己关闭,如果不关闭的,消息一直会存在在那儿,如果程序崩溃,没有正确关闭消息队列,那一堆垃圾消息都在那儿放着了,所以我不选

iii. 共享内存:很快,但是需要信号量来帮助同步,太麻烦,且共享内存的使用基本近似于停——等,有人在写,大家都干看着

iv.  套接字:以收发字节流这一通信模式下,还有比TCP更好用的IPC吗?

1. pipe相比,socket是双向的

2. 和共享内存相比,socket自带同步(port端口就一个)

3. 和消息队列相比,socket是一个文件描述符,也就是说可以使用epollIO复用;进程结束之后会自动关闭文件(套接字也是文件描述符),也就是说套接字无需主动关闭,自动回收。这就说明即便程序意外退出,也不会像消息队列一样,留一堆垃圾消息给系统

4. 分布式、跨主机进程通信、跨语言(javacTCP无语言)

你可能感兴趣的:(unix环境编程与网络编程)