linux多进程通信专题01

一、进程间通信概述:

  1、什么是进程间通信:由于进程之间的用户空间都是独立的;所以在用户空间进行通信是不可能的;因此借助于Linux内核实现进程之间通信。

         进程是CPU分配内存资源的最小单位;类似于小房子一样,线程类似于小房子中的具体物件,执行任务;

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
/*
    使用用户空间的变量实现父子进程之间通信
    结论:由于用于空间的变量都是独立的,子进程中的变量,继承父进程的变量,所以无法实现进程之间通信
*/
int main()
{
    int val = 0;
    pid_t pid = 0;
    pid = fork();
    if(pid< 0){
        printf("func fork failed\n");
        return -1;
    }
    if(pid == 0)  // child process
    {
        printf("child process runing\n");
        while(val != 1)
        {
            printf("waiting parent process run...\n");
            sleep(1);
        }
    }
    if(pid > 0)
    {
        printf("parent process run....\n");
        val = 1;
        printf("set val = 1 over\n");
    }
    wait(NULL);

    return 0;
}
View Code

     什么是线程间通信:线程是CPU执行任务的最小单位;执行具体的任务,可以在用户空间完成;在同一个进程中的多个线程共享进程的内存

      资源;类似于小房子中的具体物件;

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

int val = 0;

void* pFunc(void* arg)
{
    printf("new thread run...\n");
    while(val != 2)
    {
        sleep(1);
        printf("wait main thread set val = 2\n");
    }
    printf("main thread set val = 2 over....\n");

    return (void*)0;
}
int main()
{
    pthread_t tid;
    tid = pthread_create(&tid,NULL,pFunc,NULL);
    if(tid < 0)
    {
        printf("create new pthreaf d failed\n");
        return -1;
    }
    sleep(5);   // child enough time to run
    val = 2;
    printf("main thread set val =2 over\n");
    pthread_join(tid,NULL);   // wait relase child thread

    return 0;
}

/*
topeet@ubuntu:~/mycode/multprocess$ ./a.out 
new thread run...
wait main thread set val = 2
wait main thread set val = 2
wait main thread set val = 2
wait main thread set val = 2
main thread set val =2 over
*/
View Code

  2、进程间通信有那种方式:在Linux 内核中创建对象,用于进程间通信;不同的对象存在不同的通信方式;不同的应用场景,不同的实现机制;

   管道通信:无名管道、有名管道(文件系统中有名,有文件节点)   

   信号通信:信号发送、信号接收、信号处理

   IPC通信:共享内存、消息队列、信号灯

   socket通信:多用于两台主机上的进程之间通信  

  3、进程间通信学习思路:每一种通信方式都是基于文件IO思想

   open:创建进程通信的不同对象,函数形式不一样;

   write:向进程之间通信的对象中写操作;

   read:从进程对象中读取数据;

   close:进程间通信对象释放内存、资源;

二、无名管道:

   如果Linux内核中为进程之间通信的对象是管道,那么进程之间通信就是管道通信;如果Linux内核在文件系统中并且创建有文件节点,那么属于有名管道;相反如果没有创建文件节点,那么就是无名管道通信方式;

  很显然,如果在文件系统中没有创建文件节点,就是无名管道;我们先回顾下如果使用open函数创建一个文件都需要那些参数,open函数原型如下:

int open(const char *pathname, int flags, mode_t mode);

  1、const char* pathname:这个是包含文件路径的文件名称;

  2、int flags:操作文件的权限;

  3、操作文件的模式

  4、创建成功,返回文件描述符;

  接下来可以思考下,创建无名管道需要的参数:1、无名管道并且在文件系统中没有文件节点,因此不需要文件名;在操作管道时候,类似操作队列,只需要关注读写,因此不需要操作权限;创建无名管道函数原型如下:应用程序调用此程序后,内核将在内核空间创建一个管道队列,并将文件描述符返回:

int pipe(int pipefd[2]);

  1、返回值:成功返回0,失败返回-1;

  2、参数列表中的参数:返回两个文件描述符,pipef[0]用于读操作;pipefd[1]用于写操作;

  那么看第一个列子,在一个进程中创建一个管道,并读写操作:

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


int main()
{
    char buf[] = "hello pipe";
    char val[128]={0};
    int fd[2];
    int ret;
    ret = pipe(fd);
    if(ret != 0){
        printf("func create pipe failed\n");
        return -1;
    }
    printf("create new filw describe fd[0] = %d,fd[1] = %d\n",fd[0],fd[1]);
    printf("create pipe success...\n");
    write(fd[1],buf,sizeof(buf));

    sleep(1);
    printf("write string over and read pipe\n");

    read(fd[0],val,16);
    printf("read data from pipe...\n");
    printf("data from buf is %s\n",val);

    return 0;
}
View Code

   在学习两个进程之间使用管道通信通信之前,我们先了解下管道的几个特性:

    1、管道如果为空,读管道操作将阻塞;

    2、读操作一次管道,管道数据将被清空;而读操作普通文件,读操作并不会影响数据;

    3、写入管道的字符大小有限制,65535

    4、管道通信适用于父子进程或者具有血缘关系的进程;

    5、创建的管道文件不会占用用户空间内存资源;

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <sys/types.h>
  4 #include <sys/wait.h>
  5 #include <string.h>
  6 
  7 int main()
  8 {
  9     int fd[2];                                                                              
 10     pid_t pid;
 11     int ret;
 12     char readBuf[64];
 13     char wBuf[] = "hello linux";
 14 
 15     ret = pipe(fd);
 16     if(ret !=0)
 17     {
 18         printf("func create pipe failed\n");
 19         return -2;
 20     }
 21     pid = fork();
 22     if(pid <0)
 23     {
 24         printf("func fork failed\n");
 25         return -1;
                                                                              9,11-14       Top
 19         return -2;
 20     }
 21     pid = fork();
 22     if(pid <0)
 23     {
 24         printf("func fork failed\n");
 25         return -1;
 26     }
 27     if(pid == 0) // child process
 28     {
 29         printf("child process runing...\n");
 30         memset(readBuf,0,64);
 31         read(fd[0],readBuf,64);
 32         printf("read data from parent %s\n",readBuf);
 33         
 34     }
 35     if(pid > 0){
 36         sleep(5);
 37         write(fd[1],wBuf,sizeof(wBuf));
 38         printf("Parent write data over\n");
 39     }
 40     wait(NULL);
 41 
 42     return 0;                                                                               
 43 }
                                                                              42,10-13      Bot
View Code

     测试管道的最大缓冲字节:65535

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <sys/types.h>
  4 #include <sys/wait.h>
  5 #include <string.h>                                                                         
  6 
  7 int main()
  8 {
  9     int fd[2];
 10     pid_t pid;
 11     int ret;
 12     int cnt = 0;
 13     char readBuf[64];
 14     char wBuf[] = "hello linux";
 15     char c = 'a';
 16     ret = pipe(fd);
 17     if(ret !=0)
 18     {
 19         printf("func create pipe failed\n");
 20         return -2;
 21     }
 22     // write data to pipe
 23     for(cnt =0;cnt<9000000;cnt++)
 24     {
 25         write(fd[1],&c,1);
  7 int main()
  8 {
  9     int fd[2];
 10     pid_t pid;
 11     int ret;
 12     int cnt = 0;
 13     char readBuf[64];
 14     char wBuf[] = "hello linux";
 15     char c = 'a';
 16     ret = pipe(fd);
 17     if(ret !=0)
 18     {
 19         printf("func create pipe failed\n");
 20         return -2;
 21     }
 22     // write data to pipe
 23     for(cnt =0;cnt<9000000;cnt++)
 24     {
 25         write(fd[1],&c,1);
 26         if(cnt > 65500)
 27                 printf("cnt char is :%d\n",cnt);
 28     }
 29 
 30     return 0;
 31 }            

/*
cnt在65535处阻塞了,并且该进程处于睡眠态
*/                                                                               
View Code

    读操作将清空管道中的数据:程序将阻塞在第二次读操作处

  1 #include <stdio.h>                                                                          
  2 #include <unistd.h>
  3 #include <sys/types.h>
  4 #include <sys/wait.h>
  5 #include <string.h>
  6 
  7 int main()
  8 {
  9     int fd[2];
 10     pid_t pid;
 11     int ret;
 12     int cnt = 0;
 13     char readBuf[64];
 14     char wBuf[] = "hello linux";
 15     char c = 'a';
 16     ret = pipe(fd);
 17     if(ret !=0)
 18     {
 19         printf("func create pipe failed\n");
 20         return -2;
 21     }
 22     // write only once
 23     write(fd[1],wBuf,sizeof(wBuf));
 24     sleep(1);
 25     // read data first
 14     char wBuf[] = "hello linux";
 15     char c = 'a';
 16     ret = pipe(fd);
 17     if(ret !=0)
 18     {
 19         printf("func create pipe failed\n");
 20         return -2;
 21     }
 22     // write only once
 23     write(fd[1],wBuf,sizeof(wBuf));
 24     sleep(1);
 25     // read data first
 26     memset(readBuf,0,64);
 27     read(fd[0],readBuf,64);
 28     printf("first read data readBuf is %s\n",readBuf);
 29     memset(readBuf,0,64);
 30     // read data second //process will wait this and go to sleep status
 31     read(fd[0],readBuf,64);
 32     printf("second read data readBuf : %s\n",readBuf);
 33     
 34     close(fd[0]);
 35     close(fd[1]);
 36 
 37     return 0;
 38 }                                                                                           
View Code

三、有名管道:

  在有名管道中我们有提到,所谓有名管道是在文件系统中有创建文件节点;或者说有名管道是借助一个已经存再的文件进行多个线程之间通信;

  在使用有名管道之前,我们需要创建一个文件,函数原型如下:

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

  const char* pathname: 包含有文件路径的文件名称;

  mode_t mode:创建文件的权限;

  创建的该特殊文件,当使用open函数打开该文件时,内核会创建一个管道,通过文件节点,返回管道的文件描述符;

  如果一个程序在读操作该文件时,读操作会阻塞直到写操作开始,读操作才会返回;同样的当写该文件时,写操作也会阻塞,直到读操作开始,写操作才开始;

prw-rw-r-- 1 liu liu    0 3月  15 14:23 mkfifo  // 管道文件前面是“p”

-rwxrwxr-x 1 liu liu 8400 3月  15 14:23 a.out // 普通文件前面是“-”

  创建一个特殊的文件用于管道通信:

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <sys/types.h>
  4 #include <sys/wait.h>
  5 #include <string.h>
  6 #include <sys/stat.h>
  7 
  8 int main()
  9 {
 10     int fd[2];
 11     pid_t pid;
 12     int ret;
 13     int cnt = 0;
 14     char readBuf[64];
 15     char wBuf[] = "hello linux";
 16     char c = 'a';
 17     // create special fifo file                                                             
 18     ret = mkfifo("./mkfifo",0666);
 19     if(ret != 0)
 20         printf("mkfifo failed\n");
 21         return -1;
 22     //write data to pipe
 23     
 24     return 0;
 25 }   
-- INSERT --                                                                  17,29-32      All
View Code

  向管道写数据: 

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{

    int fd;
    int ret;
    int cnt = 0;
    char wBuf[] = "hello linux";
    //write data to pipe
    fd = open("./mkfifo",O_WRONLY);
    if(fd < 0)
    {
        printf("open file mkfifo failed\n");
        return -1;
    }
    printf("open mkfifo success\n");

    write(fd,wBuf,sizeof(wBuf));
    printf("write data over\n");
     return 0;
}
View Code

  从管道读数据:

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

int main()
{

    int fd;
    int ret;
    int cnt = 0;
    char readBuf[64] ={0};
    char wBuf[] = "hello linux";
    //read  data from file mkfifo 
    fd = open("./mkfifo",O_RDONLY);
    if(fd < 0)
    {
        printf("open file mkfifo failed\n");
        return -1;
    }
    printf("open mkfifo success\n");
    read(fd,readBuf,64);
    printf("read data is %s from mkfifo\n",readBuf);
    printf("write data over\n");
     return 0;
}
          
View Code

  可以在编译的时候,查看另一个程序的运行状态,一个程序打开时,将会阻塞,等待另一个进程打开管道;

  从上面可以看出来,有名管道使用文件节点,在打开文件节点的时候,内核创建了管道,共进程之间通信、数据交换;因此有名管道补充了无名管道的缺点;从而有名管道可适用范围更广;不仅仅局限于父子进程之间、具有血缘关系进程;

四、信号通信

  通信模型:

  在前面我们有说到过,两个进程之间通信,在用户空间是不可以实现的,需要间接或者直接借用linux内核完成进程之间通信;借用linux内核的方式类型(对象)不同,通信方式,模型也是不同的;

  在linux内核中有64种信号,包括可靠信号与不可靠信号、可阻塞信号与不可阻塞信号;信号在linux内核已经存在的;

  进程A与进程B之间通信,进程A需要告诉linux内核哪个进程、发送什么信号;当然了,进程B需要存在,可以睡眠、可以暂停(while、sleep、pause);

  进程A发送信号,停止进程B:进程Bwhile()循环运行,直到进程B接收到linux内核发送过来的终止信号;

  进程A:使用kill()来给其他进程发送信号,raise()给自己发送信号,alarm() 延时发送信号;

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


int main(int argc,char* argv[])
{
    int sig;
    int pid;

    if(argc < 3){
        printf("not enough parameter...try again\n");
        return -1;
    }
    sig = atoi(argv[1]);
    pid = atoi(argv[2]);
    printf("argv[1]:%d...argv[2]:%d\n",sig,pid);
    kill(pid, sig);

    return 0;
}
View Code

    发送信号:kill()、raise()、alarm()

 接收信号:发送的信号,进程默认接收处理;while()、sleep()、pause()

 处理信号:signal()、sigaction()

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


int main(int argc,char* argv[])
{
    printf("raise before ...\n");
    raise(9);
    printf("raise after ....\n");

    return 0;
}
View Code

  alarm():让内核延时发送信号给当前进程;只能发送sigalrm信号终止当前进程;发送给当前进程,不能让当前进程终止;该函数非阻塞;

  进程中存在的状态:R(运行状态)、T(暂停状态)、S(睡眠状态)、Z(僵死状态)

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

int main()
{
    int i = 0;
    printf("alarm before...\n");
    alarm(10);  // after 10s and send sigalrm signal,进程结束
    printf("alarm after ...\n");

    while(i < 15)
    {
        printf("main process runing %d..\n",i);
        sleep(1);
        i++;
    }
    
    return 0;
}
View Code

  处理信号:

  在使用signal()处理信号时,根据参数不同有三种不同的处理方式:

    1、忽略发送过来的信号;

    2、进程默认信号的处理方式(结束进程、暂停进程等);

    3、自定义信号的响应方式;

  忽略信号:

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

int main()
{
    int i = 0;
    printf("alarm before...\n");
    alarm(10);  // after 10s and send sigalrm signal
    printf("alarm after ...\n");
    // 忽略alarm发送过来的信号,进程正常运行    
    signal(SIGALRM,SIG_IGN);  // ignore signal 14(SIGALRM)
    while(i < 15)
    {
        printf("main process runing %d..\n",i);
        sleep(1);
        i++;
    }

    return 0;
}
View Code

  恢复信号默认处理方式:在前面忽略后,在恢复默认处理方式:

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

int main()
{
    int i = 0;
    printf("alarm before...\n");
    alarm(10);  // after 10s and send sigalrm signal
    printf("alarm after ...\n");

    signal(SIGALRM,SIG_IGN);  // ignore signal 14(SIGALRM)
    printf("recover signal deal method SIG_DFL\n");
    signal(SIGALRM, SIG_DFL); // 信号默认处理方式,程序终止
    while(i < 15)
    {
        printf("main process runing %d..\n",i);
        sleep(1);
        i++;
    }

    return 0;
}
View Code

  自定义信号的处理方式:

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

void sigFunc(int signum)
{
    printf("signal call back function signum:%d\n",signum);
    return;
}

int main()
{
    int i = 0;
    printf("alarm before...\n");
    alarm(10);  // after 10s and send sigalrm signal
    printf("alarm after ...\n");

    signal(SIGALRM, sigFunc);  // ignore signal 14(SIGALRM)
    while(i < 15)
    {
        printf("main process runing %d..\n",i);
        sleep(1);
        i++;
    }

    return 0;
}
View Code

  自定义信号处理方式在父子进程中简单应用:父进程处理自己的任务,子进程给父进程发送信号然后退出,父进程去处理信号;

  由于wait()函数是阻塞的,所以当父进程等待子进程退出时,不能执行其他任务,因此可以处理子进程退出时发送的信号,以避免僵尸进程

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/wait.h>

void sigFunc(int signum)
{
    printf("signal call back function signum:%d\n",signum);
    return;
}

void sigFunc1(int signum)  // deal signal SIGCHLD
{
    printf("deal with SIGCHLD...\n");
    wait(NULL);

}
int main()
{
    pid_t pid;
    pid = fork();
    if(pid < 0){
    if(pid < 0){
        printf("create new child process failed\n");
        return -1;
    }
    if(pid > 0)  // parent process
    {
        int i=0;
        signal(SIGUSR1, sigFunc);
        signal(SIGCHLD, sigFunc1);
        while(i < 20)
        {
            printf("parent runing.. %d s\n",i);
            sleep(1);
            i++;
        }
    }
    if(pid == 0)  // child process
    {
        sleep(2);
        kill(getppid(),SIGUSR1);
        printf("child process exit...\n");
        exit(0);   // send signal to parent SIGCHLD
    }
}
View Code

五、IPC通信

  在前面我们有提到过,进程间通信使用内核中不同的对象进行通信。前面有无名管道、有名管道、信号;在IPC进程间通信也是用内核中的IPC对象,IPC对象包含有共享内存、消息队列、信号灯;同样也是基于文件IO思想:

文件IO IPC
open Msgget、shmget、Semget
read、write msgsnd、msgrecv、shmat、semop
close msgctrl、shmctrl、semctrl

   共享内存:在使用shmget()创建了共享内存后,可以使用命令ipcs -m查看内核中的所有共享内存;以及使用ipcrm -m shmid删除指定的共享内存;ipcs -q查看创建的消息队列;ipcs -s查看创建的信号灯对象;

  1、IPC-共享内存通信

  创建共享内存,并查看创建的共享内存:

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <errno.h>

int main()
{
    int shmid;
    int key;
    key = ftok("./a.c",'e');
    if(key < 0)
    {
        printf("create key falure\n");
        return -1;
    }
    // shmid = shmget(IPC_PRIVATE, 128, 6060); 
    shmid = shmget(key,128,6060);
    if(shmid < 0)
    {
        printf("get share memory failed\n");
        return -1;
    }    
    printf("get share memory sucess shmid=%d\n",shmid);
    system("ipcs -m");


    return 0;
}
View Code

  创建共享内存时,使用IPC_PRIVATE宏和ftok()函数填充key值,IPC_PRIVATE创建的共享内存的key值总是0;使用ftok()函数填充key值由Linux内核分配;

 

   对于这两种技术的出现,类似于有名管道和无名管道,IPC_PRIVATE创建的共享内存适用于父子进程之间通信,或者具有血缘关系的进程之间通信;

  使用ftok()创建的key值适用于非父子进程、不具有血缘关系的进程之间通信,适用更广;

  向共享内存中写入数据后,数据会一直保存在内存中,除非删除数据或者Linux内核停止重启;

     读写共享内存操作:多次读操作后,数据依然在内存中 

    创建好的共享内存实在Linux内核空间中,如果使用write或者read操作共享内存,会多次进出内核;Linux为我们操作共享内存提供了shmat(),将Linux内核中的共享内存使用该函数映射到用户空间,因此多个进程可以在用户空间中操作共享内存;

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <errno.h>

int main()
{
    char *p;
    int shmid;
    int key;
    key = ftok("./a.c", 'e');
    if(key < 0)
    {
        printf("create key falure\n");
        return -1;
    }
    // shmid = shmget(IPC_PRIVATE, 128, 6060); 
    shmid = shmget(key,128,606);
    if(shmid < 0)
    {
        printf("get share memory failed\n");
        return -1;
    }    
    printf("get share memory sucess shmid=%d\n",shmid);
    system("ipcs -m");
    
    // shmat()
    p = (char*)shmat(shmid, NULL, 0);
    if(p == NULL)
    {
        printf("share memory function failure\n");
        return -1;
    }
    
    // write share memory 
    fgets(p, 128, stdin);
    
    // read share memory
    printf("share memory data:%s\n",p);
    printf("second read share memory data:%s", p);
    return 0;
}
View Code

  上面可以将共享内存映射到用户空间,可以使用shmdt()释放掉用户空间中的内存;内核中的共享内存可以使用shmctl();

  shmctl():根据不同的参数选择:1、获取共享内存的属性;2、设置共享内存的属性;3、删除共享内存;

  删除映射到用户空间的共享内存以及内核空间的共享内存:

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

int main()
{
    char *p;
    int shmid;
    int key;
    key = ftok("./a.c", 'e');
    if(key < 0)
    {
        printf("create key falure\n");
        return -1;
    }
    // shmid = shmget(IPC_PRIVATE, 128, 6060); 
    shmid = shmget(key,128,606);
    if(shmid < 0)
    {
        printf("get share memory failed\n");
        return -1;
    }    
    printf("get share memory sucess shmid=%d\n",shmid);
    system("ipcs -m");
    
    // shmat()
    p = (char*)shmat(shmid, NULL, 0);
    if(p == NULL)
    {
        printf("share memory function failure\n");
        return -1;
    }
    
    // write share memory 
    fgets(p, 128, stdin);
    
    // read share memory
    printf("share memory data:%s\n",p);
    printf("second read share memory data:%s", p);
    
    // release user memory
    shmdt(p);
    //memcpy(p,"hello", 20); // 删除后再拷贝数据,出现段错误
    
    shmctl(shmid, IPC_RMID, NULL);
    system("ipcs -m");
    return 0;
}
View Code

 使用共享内存:父子进程之间通信

    首先创建一个共享内存对象shmget(),将该对象内存映射到用户空间ftok(),使用fork()创建两个进程,父进程写一次数据发送一个信号SIGUSR1,并且pause()等待子进程发送来的信号SIGUSR2,父进程再写入数据;

    子进程读一次共享内存后,发送一个信号SIGUSR2给父进程,然后pause()等待父进程发送信号,子进程继续运行;

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>


void pfunc01(int num)
{
}
void pfunc02(int num){}

int main()
{
    char *p;
    int shmid;
    int key;
    pid_t pid;
    if(key < 0)
    {
        printf("create key falure\n");
        return -1;
    }
    shmid = shmget(IPC_PRIVATE, 128,IPC_CREAT | 0777); 
    // shmid = shmget(key,128,606);
    if(shmid < 0)
    {
        printf("get share memory failed\n");
        return -1;
    }    
    printf("get share memory sucess shmid=%d\n",shmid);
    system("ipcs -m");
    // shmat()
    p = (char*)shmat(shmid, NULL, 0);
    if(p == NULL)
    {
        printf("share memory function failure\n");
        return -1;
    }
    
    pid = fork();
    if(pid < 0){
        printf("create new process failure\n");
        return -1;
    }
    if(pid > 0){  // parent process
        printf("parent process start to write:\n");
        signal(SIGUSR2, pfunc01);
        while(1){
            printf("parent write data to share momery:\n");
            fgets(p, 128, stdin);
            kill(pid,SIGUSR1);
            pause();
        }
    }
    if(pid == 0){ // child process
        printf("child process start to read:\n");
        signal(SIGUSR1, pfunc02);
        while(1){
            printf("child start to read data from share memory:\n");
            printf("child read data: %s\n",p);
            kill(getppid(), SIGUSR2);
            pause();
        }
    }

    // release user memory
    shmdt(p);
    //memcpy(p,"hello", 20); // 删除后再拷贝数据,出现段错误
    /// 出现段错误,是访问内存超过范围
    
    system("ipcs -m");
    return 0;
}
View Code

  使用共享内存:两个没有血缘关系进程之间通信

  在上面的例子中,父子进程很容易获取到对象的pid号,但是没有血缘关系进程,我们需要首先对方知道对方进程的pid号码;因此需要首先发送pid号到共享内存中,对方读取后,再将自己的pid写入共享内存中,等待双方都知道后,在开始通信;

  没有血缘关系的进程也可以使用共享内存通信:

  server.c端,单工通信,需要先运行server端

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <signal.h>


struct writeBuf{
    pid_t pid;
    char buf[124];
};
void pfunc(int num){}

int main()
{
    int shmid;
    int key;
    pid_t pid;
    struct writeBuf* wBuf;
    
    key = ftok("./b.c", 'a');
    if(key < 0){
        printf("create key failed\n");
        return -1;
    }
    printf("shmget: create key valuce is %x\n", key);
    shmid = shmget(key, 128, IPC_CREAT|0777);    
    if(shmid < 0){
        printf("create share memory failed\n");
        return -2;
    }
    printf("get shmid is %d\n", shmid);
    wBuf = (struct writeBuf*)shmat(shmid,NULL,0);
    
    signal(SIGUSR1, pfunc);

    // write pid to share memory
    wBuf->pid = getpid();
    printf("process pid is %d\n", getpid());
    pause();
    // read pid from share memory
    pid = wBuf->pid;
//    kill(pid, SIGUSR2);
    printf("get pid from other process %d\n", pid);

    while(1)
    {
        fgets(wBuf->buf,124,stdin);
        kill(pid,SIGUSR2);
        pause();
    }

    system("ipcs -m");


    return 0;
}
View Code

  client端

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <signal.h>


struct writeBuf{
    pid_t pid;
    char buf[124];
};
void pfunc(int num){}

int main()
{
    int shmid;
    int key;
    pid_t pid;
    struct writeBuf* wBuf;
    
    key = ftok("./b.c", 'a');
    if(key < 0){
        printf("create key failed\n");
        return -1;
    }
    printf("shmget: create key valuce is %x\n", key);
    shmid = shmget(key, 128, IPC_CREAT|0777);    
    if(shmid < 0){
        printf("create share memory failed\n");
        return -2;
    }
    printf("get shmid is %d\n", shmid);
    wBuf =(struct writeBuf* )shmat(shmid,NULL,0);
    
    signal(SIGUSR2, pfunc);

    // read pid to share memory
    pid =  wBuf->pid;
    printf("get other pid is %d\n", pid);
//    kill(pid, SIGUSR1);

    // write pid from share memory
    wBuf->pid = getpid();
    kill(pid, SIGUSR1);  
    printf("this processpid is %d\n", getpid());
//    while(1)

    while(1)
    {
  //      fgets(wBuf->buf,124,stdin);
        pause();
        printf("from other process data: %s\n", wBuf->buf);
        kill(pid,SIGUSR1);
//        pause();
    }

    system("ipcs -m");
    return 0;
}
View Code

2、IPC-消息队列通信

  消息队列通信模型:前面提到的通信对象有共享内存,这里将共享内存对象换成消息队列。消息队列中根据消息结构体参数不同消息有类型;

  查询内核中存在的消息队里使用ipcs -q,删除ipcrm -q msgid

  简单使用消息队列通信:

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

struct msgBuf{
    long type;
    char buf[128];
};

int main()
{
    int msgid;
    int key;
    int ret = 0;
    struct msgBuf msg, msgs, msgRcv;
    char mBuf[64] = "hello linux msgget";
    char mSnd[64] = "this is send string...";
    int mLen = strlen(mBuf);
    int Slen = strlen(mSnd);
    msg.type = 10;
    memcpy(msg.buf, mBuf, mLen);

    msgs.type = 12;
    memcpy(msgs.buf, mSnd, Slen);

    //key = IPC_PRIVATE;
    key = ftok("./b.c",  'a');
    if(key < 0){
        printf("create key valuce failed\n");
        return -1;
    }
    printf("create key value is %x\n",key);
    msgid = msgget(key, IPC_CREAT| 0777);
    if(msgid < 0){
        printf("get message queue failure\n");
        return -2;
    }

    // snd data to message queue
    msgsnd(msgid, (void*)&msg, mLen, IPC_NOWAIT);
    msgsnd(msgid, (void*)&msgs, Slen, IPC_NOWAIT);

    system("ipcs -q");

    msgrcv(msgid, (void* )&msgRcv, 128,10,IPC_NOWAIT);
    printf("msgRcv data is %s\n", msgRcv.buf);
    memset(msgRcv.buf, 0, 64);
    msgrcv(msgid, (void*)&msgs, 128, 12, IPC_NOWAIT);
    printf("msgRcv msg snd data is %s\n", msgs.buf);

    ret = msgctl(msgid, IPC_RMID, NULL);
    if(ret < 0){
        printf("delete msg id failed\n");
    }
    printf("delete msg id success...\n");
    system("ipcs -q");
    return 0;
}
View Code

  使用消息队列通信:

  server端写操作消息队列:

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

struct msgBuf{
    long type;
    char buf[128];
};

int main()
{
    int msgid;
    int key;
    int ret = 0;
    struct msgBuf msgSnd;

    msgSnd.type = 10;
    //key = IPC_PRIVATE;
    key = ftok("./b.c",  'a');
    if(key < 0){
        printf("create key valuce failed\n");
        return -1;
    }
    //printf("create key value is %x\n",key);
    msgid = msgget(key, IPC_CREAT| 0777);
    if(msgid < 0){
        printf("get message queue failure\n");
        return -2;
    }
    while(1){
        memset(msgSnd.buf,0, 128);
        printf("please input send message: \n");
        fgets(msgSnd.buf, 128, stdin);
        // snd data to message queue
        msgsnd(msgid, (void*)&msgSnd, strlen(msgSnd.buf), IPC_NOWAIT);
    }

    return 0;
}
View Code

  client端读操作消息队列:

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

struct msgBuf{
    long type;
    char buf[128];
};

int main()
{
    int msgid;
    int key;
    int ret = 0;
    struct msgBuf msgRcv;

    msgRcv.type = 10;
    //key = IPC_PRIVATE;
    key = ftok("./b.c",  'a');
    if(key < 0){
        printf("create key valuce failed\n");
        return -1;
    }
    //printf("create key value is %x\n",key);
    msgid = msgget(key, IPC_CREAT| 0777);
    if(msgid < 0){
        printf("get message queue failure\n");
        return -2;
    }
        memset(msgRcv.buf,0, 128);
        // snd data to message queue
        msgrcv(msgid, (void*)&msgRcv, 128, 10, 0);
        printf("receive data from message: %s\n", msgRcv.buf);
    }

    return 0;
}
View Code

3、IPC-信号灯通信

   信号灯是信号量的集合;

sem_init()、sem_wait()、sem_post()
  定义信号量sem_t sem、初始化信号量sem_init()、信号量P操作sem_wait()、信号量V操作sem_post();

  创建信号灯:(指定参数说明信号集中信号量的个数),集合ID
    semget()
  删除信号量:
    semctl() 

#include <stdio.h>
#include <sys/sem.h>
#include <sys/ipc.h>

int main()
{
    int semid;

    semid = semget(IPC_PRIVATE, 3, 0777);
    if(semid<0){
        printf("create sem failed\n");
        return -1;
    }
    printf("create sem id %d\n", semid);


    system("ipcs -s");

    semctl(semid, 0, IPC_RMID); // 修改信号灯semnum编号

    system("ipcs -s");

    return 0;
}
View Code

  

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


sem_t sem;  // 信号量线程同步
void* thread_func(void* arg)
{
    // V 操作 wait
    sem_wait(&sem);
    int i=0;
    for(i=0;i<10;i++){
        usleep(100);
        printf("new thread running %d...\n",i);
    }
    return NULL;
}

int main()
{
    pthread_t tid;
    int ret = 0;
    int j=0;
    void* retVal;
    // 信号量初始化
    sem_init(&sem, 0, 0);  // pshared非0用于进程之间同步,信号量初始化为0

    ret = pthread_create(&tid, NULL,thread_func, NULL);
    if(ret < 0){
        printf(" create new thread failed\n");
        return -1;
  }

    for(j=0;j< 10;j++){
        usleep(100);
        printf("main thread running %d...\n", j);
    }
    // V操作
    sem_post(&sem);

    pthread_join(tid, &retVal);

    return 0;
}
View Code

 

 

  

  

 

posted @ 2021-03-14 12:58  笑不出花的旦旦  阅读(124)  评论(0)    收藏  举报