前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >System|IPC|Rethinking IPC

System|IPC|Rethinking IPC

作者头像
朝闻君
发布2021-11-22 10:53:14
7490
发布2021-11-22 10:53:14
举报
文章被收录于专栏:用户9199536的专栏

IPC,进程间通信,是打破地址空间隔离的必经之路。本文按照个人理解对于IPC进行了一些分类与整理。

目录

Fork Based

匿名管道/匿名共享内存/互斥量

Path Based

命名管道/命名共享内存/消息队列/信号量/文件锁

PID Based

信号

Socket Based(IP:Port versus FilePath)

internet socket(with loopback)/unix domain socket

IPC

进程通过独占地址空间实现了隔离,然而,某些时候,我们希望进程之间协作。

  • 模块化: 数据库单独在一个进程中,可以被复用
  • 加速计算: 不同进程专注于特定的计算任务,性能更好
  • 信息共享: 直接共享已经计算好的数据,避免重复计算

两个(或多个)不同的进程,通过内核或其他共享资源进行通信,来传递控制信息或数据。


Fork Based

本节介绍仅能在fork的父子进程间进行通信的IPC机制。

匿名管道

常见于shell,fd[1]用于写入数据,fd[0]用于读出数据。in-memory的pipe文件系统

代码语言:javascript
复制
int pipe(int fd[2]);

匿名共享内存

在mmap时通过匿名flag指定。

代码语言:javascript
复制
mmap (NULL, BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);

互斥量

设置pshared为PTHREAD_PROCESS_SHARED使得其在进程间共享(默认PRIVATE)

代码语言:javascript
复制
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr , int pshared);

Path Based

本节介绍可以通过文件路径名指定的IPC机制

命名管道

在pipe的基础上增加了路径名,使得外部可见。in-memory的pipe文件系统

代码语言:javascript
复制
int mkfifo(const char * pathname, mode_t mode)

命名共享内存

mmap时通过指定具名文件的fd,使得其外部可见

代码语言:javascript
复制
mmap (NULL, BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0))

shmget利用ftok映射文件名,使得其外部可见

代码语言:javascript
复制
key_t ftok( const char * fname, int id )
shmget(key_t key, size_t size, int shmflg)

消息队列

send msgp的第一个成员是type,在rcv时指定msgtyp,其他成员为消息体。

msgget利用ftok映射文件名,使得其外部可见

代码语言:javascript
复制
key_t ftok( const char * fname, int id )
int msgget(key_t key, int msgflg); 
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

信号量

常作为进程间同步机制配合共享内存,semop加减信号量。

当进程需要减信号量而无法获取时,可以排队等待其他进程加信号量之后唤醒。

semget利用ftok映射文件名,使得其外部可见

代码语言:javascript
复制
 int semid = semget(key_t key, int nsems, int semflg)
 int semctl(int semid, int semnum, int cmd, ...)
 int semop(int semid, struct sembuf *sops, unsigned nsops);

文件锁

FFS中引入的文件系统原子性原语。 lock.l_type为F_UNLCK时解锁,否则加锁。

cmd以F_SETLK非阻塞获取锁,F_SETLKW阻塞

代码语言:javascript
复制
int fcntl(int fd,int cmd, struct flock*);

PID Based

本节介绍直接指定PID的IPC机制

信号

最基本的就是kill发信号,signal处理信号

代码语言:javascript
复制
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
int kill(pid_t pid, int sig);

然后用掩码来屏蔽/接收信号。其中how对sigset执行以下操作

  • SIG_BLOCK (union)
  • SIG_UNBLOCK (intersection)
  • SIG_SETMASK (equality)
代码语言:javascript
复制
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);
int sigprocmask(int how, const sigset_t *set,sigset_t *oset);

为了防止unblock和pause之间插入信号,导致信号丢失,pause无法唤醒,引入sigsuspend实现原子性。

  • 修改为新的sigmask
  • pause
  • 恢复原本的sigmask
代码语言:javascript
复制
int sigsuspend(const sigset_t *sigmask);

Socket Based

本节介绍通过socket进行的IPC机制

internet socket(with loopback)

经典的socket、bind、listen/accept、read/write、close通信。

socket的好处在于可以进行远程通信,而本地通信时进行loopback不走网卡。

这是因为在网络层,通过查询路由表,系统已经发现了目标地址就是本机,所以直接消费而不进行转发,因此不进入链路层。

代码语言:javascript
复制
int socket(int family, int type, int protocol);//family = AF_INET

在bind时其地址为ip:port

代码语言:javascript
复制
struct sockaddr_in  
{  
  short int sin_family;          /* AF_INET */  
  unsigned short int sin_port;   /* port number */  
  struct in_addr sin_addr;       /* internet address */  
}  

unix domain socket

internet socket需要走网络协议栈,所以提供了很多无必要的开销例如checksum。

代码语言:javascript
复制
int socket(int family, int type, int protocol);//family = AF_UNIX

在bind时其地址为sockaddr_un,为文件路径名,而非ip:port,因此比loopback更快。本质上这类归到上面path based也可以。

代码语言:javascript
复制
struct sockaddr_un {
               sa_family_t sun_family;               /* AF_UNIX */
               char        sun_path[108];            /* Pathname */
           };

释放connection socket需要提供路径名,而data socket则和正常的socket同样用法

代码语言:javascript
复制
  unlink(SOCKET_NAME);

连接时需要提供路径名

代码语言:javascript
复制
        struct sockaddr_un addr;
           int ret;
           int data_socket;
           data_socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
           if (data_socket == -1) {
               perror("socket");
               exit(EXIT_FAILURE);
           }
           memset(&addr, 0, sizeof(struct sockaddr_un));
           addr.sun_family = AF_UNIX;
           strncpy(addr.sun_path, SOCKET_NAME, sizeof(addr.sun_path) - 1);
           ret = connect (data_socket, (const struct sockaddr *) &addr,
                          sizeof(struct sockaddr_un));
           if (ret == -1) {
               fprintf(stderr, "The server is down.\n");
               exit(EXIT_FAILURE);
           }

前沿

微内核在IPC方面做出了很大改进,例如

  • LRPC(SOSP 89)
  • seL4 IPC(SOSP 09)
  • XPC(ISCA 19)

读起来太累了,把linux的写完完事儿。

Reference

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目录
  • IPC
  • Fork Based
  • Path Based
  • PID Based
  • Socket Based
  • Reference
相关产品与服务
消息队列 CMQ
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档