IPC学习

课下作业-IPC

要求:

研究Linux下IPC机制:原理,优缺点,每种机制至少给一个示例,提交研究博客的链接

  • 共享内存

  • 管道

  • FIFO

  • 信号

  • 消息队列

共享内存

  • 共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据[1]:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。
  • Linux的2.2.x内核支持多种共享内存方式,如mmap()系统调用,Posix共享内存,以及系统V共享内存。linux发行版本如Redhat 8.0支持mmap()系统调用及系统V共享内存,但还没实现Posix共享内存,本文将主要介绍mmap()系统调用及系统V共享内存API的原理及应用。

实例:

#include<stdio.h>
#include<unistd.h>

int main()
{
 int fd[2];  // 两个文件描述符
 pid_t pid;
 char buff[20];

 if(pipe(fd) < 0)  // 创建管道
     printf("Create Pipe Error!
");

 if((pid = fork()) < 0)  // 创建子进程
    printf("Fork Error!
");
 else if(pid > 0)  // 父进程
 {
     close(fd[0]); // 关闭读端
     write(fd[1], "hello world
", 12);
}
 else
 {
     close(fd[1]); // 关闭写端
    read(fd[0], buff, 20);
     printf("%s", buff);
 }

 return 0;
 }

管道

  • 管道是Linux支持的最初Unix IPC形式之一,具有以下特点:
  • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;
  • 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);
  • 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
  • 数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

实例:

static int    pfd1[2], pfd2[2];

void TELL_WAIT(void)
{
if (pipe(pfd1) < 0 || pipe(pfd2) < 0)
    printf("pipe error
");
}

void TELL_PARENT(pid_t pid)
{
if (write(pfd2[1], "c", 1) != 1)
    printf("write error
");
}

void WAIT_PARENT(void)
{
char    c;

if (read(pfd1[0], &c, 1) != 1)
    printf("read error
");

if (c != 'p')
{
    printf("WAIT_PARENT: incorrect data
");
    return ;
}

}

void TELL_CHILD(pid_t pid)
{
if (write(pfd1[1], "p", 1) != 1)
    printf("write error
");
}

void WAIT_CHILD(void)
{
char    c;

if (read(pfd2[0], &c, 1) != 1)
    printf("read error
");

if (c != 'c')
{
    printf("WAIT_CHILD: incorrect data
");
    return ;
}
}

FIFO

  • FIFO有时被称为命名管道,未命名的管道只能在两个相关的进程之间使用,而且这两个相关的进程还要有一个共同的祖先进程。但是,通过FIFO,不相关的进程之间也能交换数据。
  • FIFP的创建:
#include <sys/stat.h>

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

int mkfifoat(int fd, const char* path, mode_t mode);

实例:

ser.c:

#include <stdio.h>  
#include <errno.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <unistd.h>  
#include <fcntl.h>  
#include <string.h>  
#include <stdlib.h>  

#define MAXLINE 1024  
#define FIFO1 "/tmp/fifo.1"  
#define FIFO2 "/tmp/fifo.2"  

void Perror(const char *s)  
{  
perror(s);  
exit(EXIT_FAILURE);  
}  

void server(int readfd, int writefd)  
{  
/* send msg  */  
int i = 0;  
for (i; i<3; i++) {  
    char buff[MAXLINE] = {0};  
    sprintf(buff, "hello world %d", i);  
    int n = write(writefd, buff, strlen(buff));  
    sleep(1);  
}  
char buff[MAXLINE] = {0};  
int n = read(readfd, buff, MAXLINE);  
if (n > 0) {  
    printf("read from client:%s
", buff);  
}  
}  

int main()  
{  
int readfd, writefd;  

/* create two FIFO; OK if they already exist */  
if ((mkfifo(FIFO1, 0777) < 0) && (errno != EEXIST))  
    Perror("can't create FIFO1");  
if ((mkfifo(FIFO2, 0777) < 0) && (errno != EEXIST)) {  
    unlink(FIFO1); /* rm FIFO1 */  
    Perror("can't create FIFO2");  
}  
printf("create fifo success
");  

/* 要注意open的顺序 */  
readfd = open(FIFO2, O_RDONLY, 0);  
writefd = open(FIFO1, O_WRONLY, 0);  
printf("open fifo success
");  
  
/* 让FIFO在进程结束后自动删除 */  
unlink(FIFO1);  
unlink(FIFO2);  

server(readfd, writefd);  

return 0;  
}  

cli.c:

#include <stdio.h>  
#include <errno.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <unistd.h>  
#include <fcntl.h>  
#include <string.h>  
#include <stdlib.h>  

#define MAXLINE 1024  
#define FIFO1 "/tmp/fifo.1"  
#define FIFO2 "/tmp/fifo.2"  

void Perror(const char *s)  
{  
perror(s);  
exit(EXIT_FAILURE);  
}  

void client(int readfd, int writefd)  
{  
/* read msg  */  
int i = 0;  
for (i; i<3; i++) {  
    char buff[MAXLINE] = {0};  
    int n = read(readfd, buff, MAXLINE);  
    if (n > 0) {  
        printf("read from server:%s
", buff);  
    }  
}  
char *buff = "goodby server";  
write(writefd, buff, strlen(buff));  
}  

int main()  
{  
int readfd, writefd;  

/* create two FIFO; OK if they already exist */  
if ((mkfifo(FIFO1, 0777) < 0) && (errno != EEXIST))  
    Perror("can't create FIFO1");  
if ((mkfifo(FIFO2, 0777) < 0) && (errno != EEXIST)) {  
    unlink(FIFO1); /* rm FIFO1 */  
    Perror("can't create FIFO2");  
}  

/* 要注意open的顺序 */  
writefd = open(FIFO2, O_WRONLY);  
readfd = open(FIFO1, O_RDONLY);  

client(readfd, writefd);  

return 0;  
}  

信号

  • 信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。
  • 信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。

实例:

#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
void new_op(int,siginfo_t*,void*);
int main(int argc,char**argv)
{
struct sigaction act;
int sig;
sig=atoi(argv[1]);
sigemptyset(&act.sa_mask);
act.sa_flags=SA_SIGINFO;
act.sa_sigaction=new_op;
if(sigaction(sig,&act,NULL) < 0)
{
printf("install sigal error
");
}
while(1)
{
sleep(2);
printf("wait for the signal
");
}
}
void new_op(int signum,siginfo_t *info,void *myact)
{
printf("receive signal %d", signum);
sleep(5);
}

消息队列

  • 消息队列是消息的链表,存放在内核中并由消息队列标识符标识。在某个进程往一个队列写入消息之前,并不需要另外某个进程在该队列上等待消息的到达。这跟管道和FIFO是相反的,对后两者来说,除非读出者已存在,否则先有写入者是没有意义的。

实例:

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/ipc.h> //包含ftok  
#include <sys/msg.h>  
#include <sys/types.h>  
#include <sys/fcntl.h>  

#define MSG_W 0200  
#define BUF_SIZE 512  

typedef struct msgbuf  
{  
long mtype ;  
char mdata[BUF_SIZE] ;  
} mymsg_t ;  

int   
main(int argc, char** argv)  
{  
int            mqid ;    //消息队列的描述符  
size_t         msglen ;  //消息的长度  
long           msgtype ; //消息的类型  
mymsg_t*  ptr ;     //消息结构的指针  

//用户未按格式输入  
if (argc != 3)  
    puts("usage: send <pathname> <type>") ;  

msgtype = atoi(argv[2]) ;  

//获取已存在消息队列的描述符  
mqid = msgget(ftok(argv[1], 0), MSG_W) ;  

//构造一条消息  
ptr = calloc(sizeof(long) + msglen, sizeof(char)) ;  
ptr->mtype = msgtype ;  
snprintf(ptr->mdata, BUF_SIZE, "Hi,Boy~") ;  

//发送消息  
msglen = strlen(ptr->mdata) ;  
msgsnd(mqid, ptr, msglen, 0) ;  

exit(0) ;  
}
原文地址:https://www.cnblogs.com/fixedl/p/7900681.html