进程间通信

来源:互联网 发布:知乎电子书导入kindle 编辑:程序博客网 时间:2024/06/04 01:19
进程间通信


   
进程间通信方式分为:无名管道、有名管道、IPC对象(共享内存、消息队列,信号灯)。
    无名管道主要用于实现线程见得通信以及具有亲缘关系的进程见得通信。
    有名管道主要用于无亲缘关系进程间的通信。
    共享内存可以实现无亲缘关系的进程间通信,用一块共享内存区来实现数据的读写通信,进程可以直接在共享区进行读写等操作,通信效率高,没有数据的拷贝。
   

无名管道实现进程间通信流程:

    
(1)、创建无名管道    //int fd[2]  pipe(fd)

     (2)、创建子进程      //fork()

     (3)、在父进程中关闭写端,进行读管道操作,读取管道数据,关闭读端。退出父进程。   //close(fd[1]) read 、close、exit

     (4)、在子进程中关闭读端,进行写管道操作,将数据写入管道,关闭写端,退出子进程。//close、write、close、exit
   
    特点:无名管道是半双工的通信工具,实现父子进程间的通信,信息流通是单向的,只能从读端读数据,写端写数据,管道数据存在于内存中,程序结束内存释放。  
   
代码实现:

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

int main()
{
    int fd[2];
    int ret = pipe(fd);     //创建无名管道
    if(ret < 0)
    {
        perror("pipe error!");
        return -1;
    }
   
    pid_t pid = fork();     //创建子进程
   
    if(pid < 0)
    {
        perror("fork error!");
        return -1;
    }

    if(pid > 0)
    {                                         //父进程关闭读端,从写端写入内容
        printf("I am parent process\n");        
        close(fd[0]);   //  close  read
        char buf[] = "hello world";
        write(fd[1] , buf , sizeof(buf));
        printf("data has been wrote\n");
        close(fd[1]);
        exit(0);
    }

    else
    {                                     //子进程关闭写端,从读端读取数据
        printf("I am child process\n");
        close(fd[1]);              //close write
        char buf[1024] = {0};
        read(fd[0] , buf , sizeof(buf));
        printf("data from parent process:%s\n",buf);
        exit(0);
    }
    return 0;
}
   
有名管道实现进程间通信流程:

     (1)、创建有名管道    //mkfifo()

     (2)、读管道的进程用只读方式打开管道文件,读取文件数据  

     (3)、写管道的进程用只写的方式打开管道文件,将数据写入文件

     (4)、关闭管道文件    //关闭文件描述符
   
    特点:实现两个无亲缘关系的进程间通信,管道数据(管道文件)存在于磁盘上,在文件系统中可见。
    
代码实现:
     //进程A
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

int main()
{
    int ret = mkfifo("myfifo" , 0666);       //创建有名管道,参数为管道文件名和文件权限
    if(0 == ret || EEXIST == errno)
    {
        printf("mkfifo OK!\n");
    }
    else
    {
        perror("mkfifo error");
        return -1;
    }
    int fd = open("myfifo" , O_RDONLY); //进程A以只读打开管道
    if(fd < 0)
    {
        perror("open myfifo error!");
        return -1;
    }
    char buf[1024];

    read(fd , buf , sizeof(buf));       //读取管道数据
    printf("read:%s\n",buf);
    close(fd);
    return 0;
}
//进程B
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

int main()
{
    int ret = mkfifo("myfifo" , 0666);
    if(0 == ret || EEXIST == errno)
    {
        printf("mkfifo OK!\n");
    }
    else
    {
        perror("mkfifo error");
        return -1;
    }
    int fd = open("myfifo" , O_WRONLY);  //进程B以只写方式打开管道
    if(fd < 0)
    {
        perror("open myfifo error!");
        return -1;
    }
    char buf[] = "hello world";
    write(fd , buf , sizeof(buf));    //写入数据
    close(fd);
    return 0;
}



   
共享内存实现进程间通信流程:



     (1)、通过ftok函数获取键值 key   //ftok()  

     (2)、创建共享内存               //shmget()

     (3)、映射共享内存               //shmat()

     (4)、读进程进行读操作,从共享内存区读出数据,写进程进行写操作,给共享内存区写入数据。

     (5)、读写进程取消映射           //shmdt()

     (6)、读进程删除共享内存。        //shmctl()
   
    特点:高效的进程间通信方式,进程可以直接读写内存,不需要进行数据的拷贝 

          多个进程共享一段内存,需要有名信号量或者信号实现同步机制,否则出现内存访问冲突的问题。
   
I 、代码实现:通过有名信号量实现进程A和进程B之间同步的通信

    //进程A
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>
#include <fcntl.h>

int main()
{
    sem_t *pSem1 = sem_open("sem1" , O_CREAT | O_RDWR , 0666 , 1);   //创建有名信号量1
    if(SEM_FAILED == pSem1)
    {
        perror("sem1 open error!");
        return -1;
    }

    sem_t *pSem2 = sem_open("sem2" , O_CREAT | O_RDWR , 0666 , 0);  //创建有名信号量2
    if(SEM_FAILED == pSem2)
    {
        perror("sem2 open error!");
        return -1;
    }
    key_t key = ftok("." , 15);                                    // 通过ftok()函数获取键值key
    if(key == -1)
    {
        perror("ftok error!");
        return -1;
    }
    printf("ftok OK!\n");

    int shmID = shmget(key , 100 , IPC_CREAT | 0666);             //创建共享内存

    if(-1 == shmID)
    {
        printf("shmget error!\n");
        return -1;
    }
    char*p = (char*)shmat(shmID , NULL , 0);                  //映射共享内存
    if((char*)-1 == p)
    {
        perror("shmat error!");
        return -1;
    }
    int count = 5;
    while(count--)
    {                                                    
        sem_wait(pSem2);
        printf("data from process1:%s\n", p);       //用信号量实现AB进程数据同步读写
        memset(p , 0 ,100);
        char buf[1024] = {0};
        printf("process2:\n");
        gets(buf);
        strcpy(p , buf);
        sem_post(pSem1);
    }
    shmdt(p);                                     //取消映射

    shmctl(shmID , IPC_RMID , NULL);              //删除共享内存
    return 0;
}
  //进程B
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>
#include <fcntl.h>

int main()
{
    sem_t *pSem1 = sem_open("sem1" , O_CREAT | O_RDWR , 0666 ,1);  //创建有名信号量1
    if(SEM_FAILED == pSem1)
    {
        perror("sem1_open error!");
        return -1;
    }
    sem_t *pSem2 = sem_open("sem2" , O_CREAT | O_RDWR , 0666 ,0);  //创建有名信号量2
    if(SEM_FAILED == pSem2)
    {
        perror("sem2_open error!");
        return -1;
    }

    key_t key = ftok("." , 15);
    if(key == -1)
    {
        perror("ftok error!");
        return -1;
    }
    printf("ftok OK!\n");

    int shmID = shmget(key , 100 , IPC_CREAT | 0666);    //创建共享内存

    if(-1 == shmID)
    {
        printf("shmget error!\n");
        return -1;
    }
    char*p = (char*)shmat(shmID , NULL , 0);           //映射共享内存
    if((char*)-1 == p)
    if((char*)-1 == p)
    {
        perror("shmat error!");
        return -1;
    }
    memset(p , 0 , 100);
    int count = 5;
    while(count--)
    {
        sem_wait(pSem1);                 //用信号量实现AB进程数据同步读写
        if(0 != strlen(p))
        {
            printf("data from process2:%s\n",p);
        }
        memset(p , 0 , 100);
        char buf[1024];
        printf("process1:\n");
        gets(buf);
        strcpy(p , buf);
        sem_post(pSem2);
    }
    shmdt(p);                    //取消映射
    return 0;
}
II 、代码实现:通过信号实现进程A和进程B之间的同步通信

//进程A
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <semaphore.h>
#include <fcntl.h>
#include <signal.h>

void sigFun(int sigNo)     //信号功能函数
{
    //do nothing
}

int main()
{
    signal(SIGUSR1 , sigFun);      //当接收到信号SIGUSR1执行sigFun()函数
    key_t key = ftok("." , 15);
    if(key == -1)
    {
        perror("ftok error!");
        return -1;
    }
    printf("ftok OK!\n");

    int shmID = shmget(key , 100 , IPC_CREAT | 0666); //创建共享内存

    if(-1 == shmID)
    {
        printf("shmget error!\n");
        return -1;
    }
    char*p = (char*)shmat(shmID , NULL , 0);        //映射共享内存

    if((char*)-1 == p)
    {
        perror("shmat error!");
        return -1;
    }
        memset(p , 0 , sizeof(p));
        pid_t pid = getpid();    //得到A进程进程号
        *((pid_t *)p+1) = pid;   //将进程号放入共享内存区
    int count = 5;
    while(count--)
    {
        char *q = (char *)((pid_t *)p+2);
        pause();                        //进程暂停、等待信号到来
        printf("from process1:%s\n" , q);       //读取进程B发送的数据
        memset(q , 0 , 100-2*sizeof(pid_t));
        printf("process2:\n");                    
        gets(q);                           //写数据
        kill(*((pid_t*)p) , SIGUSR1);   //向进程B发信号
    }
    shmdt(p);                        //取消映射

    shmctl(shmID , IPC_RMID , NULL);  //删除共享内存
    return 0;
}
//进程B
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>
#include <fcntl.h>

void sigFun(int sigNo)
{
    //do nothing
}

int main()
{
    signal(SIGUSR1 , sigFun);
    key_t key = ftok("." , 15);
    if(key == -1)
    {
        perror("ftok error!");
        return -1;
    }
    printf("ftok OK!\n");

    int shmID = shmget(key , 100 , IPC_CREAT | 0666);

    if(-1 == shmID)
    {
        printf("shmget error!\n");
        return -1;
    }
    char*p = (char*)shmat(shmID , NULL , 0);
    if((char*)-1 == p)
    {
        perror("shmat error!");
        return -1;
    }
    pid_t pid = getpid();
    *((pid_t*)p) = pid;
    int count = 5;
    while(count--)
    {
        char *q = (char *)((pid_t *)p+2);
        if(0 < strlen(q))
        {
            printf("data from process2:%s\n",q); //读取进程A数据
        }
        memset(q , 0 , 100-2*sizeof(pid_t));
        printf("process1:\n");
        gets(q);                          //写数据
        kill(*((pid_t*)p+1) ,SIGUSR1);   //给进程A发信号
        pause();                         //进程B暂停等待进程A信号
    }
    shmdt(p);                      //删除映射
    return 0;
}

信号灯实现进程间的通信

代码实现:
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>

union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
};

int main()
{
    //创建信号灯  
    key_t key = ftok(".", 13);
    if(key < 0)
    {
        perror("ftok error!");
        return -1;
    }

    int semId = semget(key, 2, IPC_CREAT | 0666);
    if(semId < 0)
    {
        perror("semget error!");
        return -1;
    }
    printf("semget ok!\n");

    union semun sem1, sem2;
    sem1.val = 1;
    sem2.val = 0;
    //对信号灯初始化   
    semctl(semId, 0, SETVAL, sem1);
    semctl(semId, 1, SETVAL, sem2);

    struct sembuf buf;
    memset(&buf, 0, sizeof(buf));
    pid_t pid = fork();
    if(pid < 0)
    {
        perror("fork error!");
        return -1;
    }
    else if(0 == pid)
    {
        printf("child!\n");
        int i = 5;
        while(i--)
        {
            buf.sem_num = 0;
            buf.sem_op = -1;
            buf.sem_flg = SEM_UNDO;
            //p操作
            semop(semId, &buf, 1);
            printf("hello!\n");
            sleep(1);
            buf.sem_num = 1;
            buf.sem_op = 1;
            buf.sem_flg = SEM_UNDO;
            //v操作
            semop(semId, &buf, 1);
        }

        exit(0);
    }
    else
    {
        printf("parnet!\n");
        int i = 5;
        while(i--)
        {
            buf.sem_num = 1;
            buf.sem_op = -1;
            buf.sem_flg = SEM_UNDO;
             //p操作
            semop(semId, &buf, 1);
            printf("world!\n");
            sleep(1);
            buf.sem_num = 0;
            buf.sem_op = 1;
            buf.sem_flg = SEM_UNDO;
            //v操作
            semop(semId, &buf, 1);
        }

        wait(NULL);
        //删除信号灯
        semctl(semId, 0, IPC_RMID);
        semctl(semId, 1, IPC_RMID);
        exit(0);
    }
    return 0;
}
消息队列实现进程间通信

  //进程A
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

#define MSG_SIZE 20
struct msgbuf
{
    long mtype;
    char mtext[MSG_SIZE];
};
int main()
{
    //创建或者打开消息队列
    key_t key = ftok(".", 15);
    if(key < 0)
    {
        perror("ftok error!");
        return -1;
    }

    int msgId = msgget(key, IPC_CREAT | 0666);
    if(msgId < 0)
    {
        perror("msgget error!");
        return -1;
    }
    printf("msgget ok!\n");


    struct msgbuf buf;
    memset(&buf, 0, sizeof(buf));

    //读消息
    msgrcv(msgId, (void *)&buf, MSG_SIZE, 2, 0);
    printf("type %ld, text %s\n", buf.mtype, buf.mtext);

    //删除消息队列
    msgctl(msgId, IPC_RMID, NULL);
    return 0;
}
//进程B

#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

#define MSG_SIZE 20
struct msgbuf
{
    long mtype;
    char mtext[MSG_SIZE];
};
int main()
{
    //创建或者打开消息队列
    key_t key = ftok(".", 15);
    if(key < 0)
    {
        perror("ftok error!");
        return -1;
    }

    int msgId = msgget(key, IPC_CREAT | 0666);
    if(msgId < 0)
    {
        perror("msgget error!");
        return -1;
    }
    printf("msgget ok!\n");


    struct msgbuf buf;
    memset(&buf, 0, sizeof(buf));

    buf.mtype = 3;
    strcpy(buf.mtext, "hello world");
    //添加消息到队尾
    msgsnd(msgId, (void *)&buf, MSG_SIZE, 0);

    memset(&buf, 0, sizeof(buf));

    buf.mtype = 2;
    strcpy(buf.mtext, "aaaaaaa");
    //添加消息到队尾
    msgsnd(msgId, (void *)&buf, MSG_SIZE, 0);

    return 0;
}