进程间通信

本文介绍了Linux系统中进程间通信的几种主要方式,包括无名管道和有名管道(FIFO)、消息队列、共享内存以及信号的概念、使用方法和示例。详细讨论了信号的处理函数、阻塞以及信号量在同步中的作用。此外,还给出了简易的生产者-消费者模型示例。
摘要由CSDN通过智能技术生成

概述

进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。

IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。

管道(无名管道)

管道: 通常指无名管道,是 UNIX 系统IPC最古老的形式。
特点:

  1. 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
  2. 它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
  3. 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
    原型:
#include <unistd.h>
int pipe(int fd[2]);    // 返回值:若成功返回0,失败返回-1

当一个管道建立时,它会创建两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。如下图:
在这里插入图片描述
要关闭管道只需将这两个文件描述符关闭即可。
例子
这是一个比较简单的通信例子

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>
int main()
{
    int fd[2];  //定义文件描述符
    pid_t pid;
    char buff[20];

    if(pipe(fd)<0){
        printf("Creat pipe error!\n");
    }

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


    return 0;
}

有名管道(FIFO)

FIFO,也称为命名管道,它是一种文件类型。
特点:

  • FIFO可以在无关的进程之间交换数据,与无名管道不同。
  • FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

原型:

#include <sys/stat.h>
// 返回值:成功返回0,出错返回-1
int mkfifo(const char *pathname, mode_t mode);

其中的 mode 参数与open函数中的 mode 相同。一旦创建了一个 FIFO,就可以用一般的文件I/O函数操作它。
当 open 一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别:

  • 若没有指定O_NONBLOCK(默认),只读 open 要阻塞到某个其他进程为写而打开此 FIFO。类似的,只写 open 要阻塞到某个其他进程为读而打开它。
  • 若指定了O_NONBLOCK,则只读 open 立即返回。而只写 open 将出错返回 -1 如果没有进程已经为读而打开该 FIFO,其errno置ENXIO。

例子
readfifo

#include    <sys/stat.h>
#include    <sys/types.h>
#include    <stdio.h>
#include    <errno.h>
#include    <fcntl.h>    // O_WRONLY
#include    <string.h>
#include    <unistd.h>
int main()
{
    char buf[30]={0};
    
    if(mkfifo("./fifo", 0600) < 0 && errno!=EEXIST) // 创建FIFO管道,如果文件错误码不是存在则失败
    {
        printf("mkfifo failed\n");
        perror("why");
    }

    int fd = open("./fifo",O_RDONLY);  
    
    if(fd<0){
        perror("open");
        return 1;
    }
    printf("open success\n");
 
    memset(buf,0,sizeof(buf));
    int nread = read(fd,buf,30);

    printf("read %d byte from fifo,context:%s\n",nread,buf);

    close(fd);
    return 0;
}

写fifo

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

int main()
{
    char *str = "message from fifo ";

    int fd = open("./fifo",O_WRONLY);  
    if(fd<0){
        perror("open");
        return 1;
    }
    printf("write open success\n");

    write(fd,str,strlen(str));

    close(fd);
    return 0;
}

消息队列

消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。
特点:

  1. 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
  2. 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
  3. 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。

具体原型参考以下链接:
消息队列函数详细说明
原型:

#include <sys/msg.h>
// 创建或打开消息队列:成功返回队列ID,失败返回-1
int msgget(key_t key, int flag);
// 添加消息:成功返回0,失败返回-1
int msgsnd(int msqid, const void *ptr, size_t size, int flag);
// 读取消息:成功返回消息数据的长度,失败返回-1
int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
// 控制消息队列:成功返回0,失败返回-1
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

在以下两种情况下,msgget将创建一个新的消息队列:

  • 如果没有与键值key相对应的消息队列,并且flag中包含了IPC_CREAT标志位。
  • key参数为IPC_PRIVATE

函数msgrcv在读取消息队列时,type参数有下面几种情况:

  • type == 0,返回队列中的第一个消息;
  • type > 0,返回队列中消息类型为 type 的第一个消息;
  • type < 0,返回队列中消息类型值小于或等于 type 绝对值的消息,如果有多个,则取类型值最小的消息。

可以看出,type值非 0 时用于以非先进先出次序读消息。也可以把 type 看做优先级的权值。(其他的参数解释,请自行Google之)

例子:
两边相互发送

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

struct msgbuf {
               long mtype;       /* message type, must be > 0 */
               char mtext[128];    /* message data */
               };

int main()
{
    struct msgbuf readBuf;

    int msgId = msgget(0x1234,IPC_CREAT|0666);   //创建消息队列key=0x1234,如果存在则返回标识符
    if(msgId == -1){
        printf("get que falied\n");
    }

    msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),888,0);  //接收来自888类型的一条消息
    printf("read from que:%s\n",readBuf.mtext);

    struct msgbuf sendBuf={988,"thank you for reach"};
    msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);  //非阻塞方式
    return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

struct msgbuf {
               long mtype;       /* message type, must be > 0 */
               char mtext[128];    /* message data */
               };

int main()
{
    struct msgbuf sendBuf={888,"this is message from quen"};
    struct msgbuf readBuf;

    int msgId = msgget(0x1234,IPC_CREAT|0777); //创建消息队列key=0x1234
     if(msgId == -1){
        printf("get que falied\n");
    }

    msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);  //非阻塞方式发送
    msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),988,0); //接收来自988类型的一条消息
    printf("read from que:%s\n",readBuf.mtext);
    return 0;
}

共享内存

共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区。
特点:

  1. 共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
  2. 因为多个进程可以同时操作,所以需要进行同步。
  3. 信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。

具体参数可参考以下链接
共享内存
原型:

#include <sys/shm.h>
// 创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
int shmget(key_t key, size_t size, int flag);
// 连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1
void *shmat(int shm_id, const void *addr, int flag);
// 断开与共享内存的连接:成功返回0,失败返回-1
int shmdt(void *addr); 
// 控制共享内存的相关信息:成功返回0,失败返回-1
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);

当用shmget函数创建一段共享内存时,必须指定其 size;而如果引用一个已存在的共享内存,则将 size 指定为0 。
当一段共享内存被创建以后,它并不能被任何进程访问。必须使用shmat函数连接该共享内存到当前进程的地址空间,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问。
shmdt函数是用来断开shmat建立的连接的。注意,这并不是从系统中删除该共享内存,只是当前进程不能再访问该共享内存而已。
shmctl函数可以对共享内存执行多种操作,根据参数 cmd 执行相应的操作。常用的是IPC_RMID(从系统中删除该共享内存)。

例子:
写入内存

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

int main()
{
    int shmid;
    key_t key;
    char *shmaddr;
    key = ftok(".",1); //得到IPC密钥,路径选择为当前路径,项目编号为1

    
    shmid = shmget(key,1024*4,IPC_CREAT|0666); //创建一个共享内存,4M, 权限可读可写
    if(shmid==-1){
        printf("shmget no ok\n");
        exit(-1);
    }

    shmaddr=shmat(shmid,0,0);        //连接共享内存.自动选择一个合适的内存空间 默认为读写权限

    printf("shmat ok\n");
    strcpy(shmaddr,"memocry cpy success\n");   //写入共享内存

    sleep(5);
    shmdt(shmaddr);                         //断开共享内存


    printf("Quit\n");
    return 0;
}

读出内存

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

int main()
{ 
    int shmid;
    key_t key;
    char *shmaddr;
    key = ftok(".",1);   //得到IPC密钥,路径选择为当前路径,项目编号为1

    shmid = shmget(key,1024*4,0);       //获取一个共享内存,4M, 权限可读可写
    if(shmid==-1){
        printf("shmget no ok\n");
        exit(-1);
    }

    shmaddr=shmat(shmid,0,0);       //连接共享内存

    printf("shmat ok\n");
    printf("data:%s",shmaddr);       //读出共享内存内容

    shmdt(shmaddr);                  //断开共享内存
    shmctl(shmid,IPC_RMID,0);               //从系统中删除共享内存 默认为0
    printf("Quit\n");
    return 0;
}

Linux信号

详细信息可参考原稿:
Linux信号

1、signal信号

1、signal信号是Linux编程中非常重要的部分,接下来将详细介绍信号的基本概念、实现和使用,和与信号的几个系统调用(库函数)。
2、signal信号是进程之间相互传递消息的一种方法,信号全称为软中断信号,也有人称作软中断,从它的命名可以看出,它的实质和使用很象中断。

2、信号基本概念

  • 软中断信号(signal,又简称为信号)用来通知进程发生了事件。进程之间可以通过调用kill库函数发送软中断信号。Linux内核也可能给进程发送信号,通知进程发生了某个事件(例如内存越界)。
  • 注意,信号只是用来通知某进程发生了什么事件,无法给进程传递任何数据,进程对信号的处理方法有三种:
  • 1)第一种方法是,忽略某个信号,对该信号不做任何处理,就象未发生过一样。
  • 2)第二种是设置中断的处理函数,收到信号后,由该函数来处理。
  • 3)第三种方法是,对该信号的处理采用系统的默认操作,大部分的信号的默认操作是终止进程。

3、信号类型

发出信号的原因很多,这里按发出信号的原因简单分类,以了解各种信号:

信号名信号值默认处理动作发出信号的原因
SIGHUP1A终端挂起或者控制进程终止
SIGINT2A键盘中断Ctrl+c
SIGQUIT3C键盘的退出键被按下
SIGILL4C非法指令
SIGABRT56Cabort(3)发出的退出指令
SIGFPE68C浮点异常
SIGKILL9AEF采用kill -9 进程编号 强制杀死程序
SIGSEGV11C无效的内存引用
SIGPIPE13A管道破裂:写一个没有读端口的管道
SIGALRM14A由alarm(2)发出的信号
SIGTERM15A采用“kill 进程编号”或“killall 程序名”通知程序。
SIGUSR130,10,16A用户自定义信号1
SIGUSR231,12,17A用户自定义信号2
SIGCHLD20,17,18B子进程结束信号
SIGCONT19,18,25进程继续(曾被停止的进程)
SIGSTOP17,19,23DEF终止进程
SIGTSTP18,20,24D控制终端(tty)上按下停止键
SIGTTIN21,21,26D后台进程企图从控制终端读
SIGTTOU22,22,27D后台进程企图从控制终端写

别看有这么多信号,其实常用的也就那几个红色的信号

处理动作一项中的字母含义如下
A 缺省的动作是终止进程。
B 缺省的动作是忽略此信号,将该信号丢弃,不做处理。
C 缺省的动作是终止进程并进行内核映像转储(core dump),内核映像转储是指将进程数据在内存的映像和进程在内核结构中的部分内容以一定格式转储到文件系统,并且进程退出执行,这样做的好处是为程序员 提供了方便,使得他们可以得到进程当时执行时的数据值,允许他们确定转储的原因,并且可以调试他们的程序。
D 缺省的动作是停止进程,进入停止状况以后还能重新进行下去。1
E 信号不能被捕获。
F 信号不能被忽略。

4、signal函数

signal库函数可以设置程序对信号的处理方式。
函数声明:

sighandler_t signal(int signum, sighandler_t handler);
参数signum表示信号的编号。
参数handler表示信号的处理方式,有三种情况:
1)SIG_IGN:忽略参数signum所指的信号。
2)一个自定义的处理信号的函数,信号的编号为这个自定义函数的参数。
3)SIG_DFL:恢复参数signum所指信号的处理方法为默认值。 
一般不关心signal的返回值。

5、信号有什么用

1、 服务程序运行在后台,如果想让中止它,强行杀掉不是个好办法,因为程序被杀的时候,程序突然死亡,没有释放资源,会影响系统的稳定,用Ctrl+c中止与杀程序是相同的效果。
2、如果能向后台程序发送一个信号,后台程序收到这个信号后,调用一个函数,在函数中编写释放资源的代码,程序就可以有计划的退出,安全而体面。
3、信号还可以用于网络服务程序抓包等。

6. 可靠信号与不可靠信号

信号值 1 ~ 32 为不可靠信号 信号会丢失
信号值 34 ~ 64 为可靠信号 信号不会丢失

7. 应用实例

在实际开发中,在main函数开始的位置,程序员会先屏蔽掉全部的信号。


//屏蔽掉全部的信号
for (int ii=0;ii<100;ii++) signal(ii,SIG_IGN);

1. 这么做的目的是不希望程序被干扰。然后,再设置程序员关心的信号的处理函数。
2. 程序员关心的信号有三个:SIGINT、SIGTERM和SIGKILL。
3. 程序在运行的进程中,如果按Ctrl+c,将向程序发出SIGINT信号,信号编号是2。
4. 采用“kill 进程编号”或“killall 程序名”向程序发出的是SIGTERM信号,编号是15。
5. 采用“kill -9 进程编号”向程序发出的是SIGKILL信号,编号是9,此信号不能被忽略,也无法捕获,程序将突然死亡。
6. 所以,程序员只要设置SIGINT和SIGTERM两个信号的处理函数就可以了,这两个信号可以使用同一个处理函数,函数的代码是释放资源。

例子

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
 
void EXIT(int sig)
{ 
  printf("收到了信号%d,程序退出。\n",sig);
  
  // 在这里添加释放资源的代码
    
  exit(0);   // 程序退出。
}
 
int main()
{
  // 屏蔽全部的信号
  for (int ii=0;ii<100;ii++)
 
  signal(ii,SIG_IGN);
 
  // 设置SIGINT和SIGTERM的处理函数
  // 第一个参数也可以用对应的信号值表示 例如signal(2,EXIT);
  signal(SIGINT,EXIT);
  signal(SIGTERM,EXIT);
 
  while (1)  // 一个死循环
  {
    sleep(10);
  }
}

8、信号处理函数被中断

  • 当一个信号到达后,调用处理函数,如果这时候有其他的信号发生,会中断之前的处理函数,等新的信号处理函数执行完毕后在继续执行之前的处理函数
  • 如果是同一个信号的话会排队阻塞

例子


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h> 
            
// 子进程退出时调用的函数
void hdfunc(int sig)
{          
 
   printf("sig=%d\n",sig);   
    
   for (int jj=1;jj<6;jj++) {
    printf("sig(%d)=%d\n",sig,jj);  
    
    sleep(1); 
   }
}
 
int main()
{
 
   signal(2,hdfunc);
   signal(15,hdfunc);
 
 
   for (int ii=1;ii<100;ii++) {
 
   printf("main:%d\n",ii);
   sleep(1);
 
   }
   return 0;
}

如果不希望在接到信号时中断当前的处理函数,也不希望忽略该信号,而是延时一段时间再处理这个信号,这个时候要用到信号的阻塞。

9、信号的阻塞

  1. 信号的阻塞和忽略信号是不同的,被阻塞的信号也不会影响进程的行为,信号只是暂时被阻止传递
  2. 进程忽略一个信号时,信号会被传递出去,但进程会将信号丢失

例子


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h> 
            
// 子进程退出时调用的函数
void hdfunc(int sig)
{          
   sigset_t set;//信号集
   sigemptyset(&set);
   sigaddset(&set,15);
   sigprocmask(SIG_BLOCK,&set,NULL);//阻塞信号
  
   printf("sig=%d\n",sig);   
    
   for (int jj=1;jj<6;jj++) {
    printf("sig(%d)=%d\n",sig,jj);  
    
    sleep(1); 
   }
 
  //sigprocmask(SIG_UNBLOCK,&set,NULL);//解除阻塞
}
 
int main()
{
 
   signal(2,hdfunc);
   signal(15,hdfunc);
 
 
   for (int ii=1;ii<100;ii++) {`在这里插入代码片`
 
   printf("main=%d\n",ii);
   sleep(1);
 
 
   }
   return 0;
}

10、功能强大的sigaction

例子


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
 
void hdfunc(int sig)
{
   printf("sig=%d\n",sig);
 
   for (int jj=0;jj<5;jj++) {
    printf("jj(%d)=%d\n",sig,jj);
 
    sleep(1);
   }
}
 
int main()
{
 
 //   signal (2,hdfunc);
 //   signal (15,hdfunc);
 
    struct sigaction stact;
    memset(&stact,0,sizeof(stact));   //初始化
 
    stact.sa_handler = hdfunc;        //指定信号处理函数
    sigaddset (&stact.sa_mask,2);     //指定需要阻塞的信号
    sigaddset (&stact.sa_mask,15);    //指定需要阻塞的信号
 
    stact.sa_flags=SA_RESTART;        //如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
 
    sigaction (2,&stact,NULL);        //设置信号2的处理行为
    sigaction (15,&stact,NULL);       //设置信号15的处理行为
 
    char str[5];
    memset (str,0,sizeof(str));
    scanf("%s",str);
    printf("str=%s\n",str);
/*
    for (int ii=1;ii<100;ii++) {
      printf("ii=%d\n",ii);
      sleep(1);
    }
*/
    return 0;
}

高级版本读写实验
read

#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

void  handler(int signum, siginfo_t *info, void *context)
{
    printf("get signum %d\n",signum);
    if(context!=NULL){
        printf("get data = %d\n",info->si_int ); 
    }
}


int main()
{
    struct sigaction act;

    printf("pid = %d\n",getpid());
    act.sa_sigaction = handler;
    act.sa_flags = SA_SIGINFO;
    sigaction(SIGUSR1,&act,NULL);
    while(1){

    }  
    return 0;
}

write

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

int main(int argc,char **argv)
{
    int signum;
    int pid;

    signum = atoi(argv[1]);
    pid   =  atoi(argv[2]);

    union sigval value;
    value.sival_int = 100;

    sigqueue(pid,signum,value);
    printf("done\n");
    return 0;
}

这个demo可解决共享内存多进程同时写造成的问题

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

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

void pGetKey(int id){
    struct sembuf set;
    set.sem_num=0;
    set.sem_op=-1;
    set.sem_flg=SEM_UNDO;

    semop(id,&set,1);
    printf("getkey\n");
}
void pPutBackKey(int id){
    struct sembuf set;
    set.sem_num=0;
    set.sem_op=1;
    set.sem_flg=SEM_UNDO;

    semop(id,&set,1);
    printf("Put back key\n");
}

int main(int argc,char **argv)
{
    key_t key;
    int semid;

    key = ftok(".",2); //得到IPC密钥

    semid = semget(key,1,IPC_CREAT|0666); //获取创建信号量

    union semun initsem;
    initsem.val=0;
    semctl(semid,0,SETVAL,initsem);  //初始化信号量

    int pid = fork();
    if(pid>0){
        pGetKey(semid);   //阻塞等待密钥
        printf("this is father\n");
        pPutBackKey(semid);
    }
    else if(pid ==0)
    {
        printf("this is child\n");
        pPutBackKey(semid);
        
    }
    else{
        printf("fork error\n");
    }
    return 0;
}

11、发送信号

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

int main(int argc,char **argv)
{
    int signum;
    int pid;

    signum = atoi(argv[1]);
    pid = atoi(argv[2]);

    printf("num = %d,pid=%d\n",signum,pid);
    kill(pid,signum);
    printf("send signal ok\n");
    return 0;
}

简易生产者与消费者

该程序没有引入线程方面的东西,读和写为互斥量,两者无法同时发生

//producer.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define TEXT_SZ 2048   //定义2M大小
struct shared_use_st 
{
	int written_by_you;
	char some_text[TEXT_SZ];
};
int main()
{
	int running = 1;
	void *shared_memory = (void *)0;
	struct shared_use_st *shared_stuff;
	char buffer[BUFSIZ];
	int shmid;

//IPC密钥设置为1234,内存大小为2052,权限为可读可写

	shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);

	if (shmid == -1) 
	{
		fprintf(stderr, "shmget failed\n");
		exit(EXIT_FAILURE);
    }

	shared_memory = shmat(shmid, (void *)0, 0);//连接共享内存.自动选择一个合适的内存空间 默认为读写权限
	if (shared_memory == (void *)-1) 
	{
		fprintf(stderr, "shmat failed\n");
		exit(EXIT_FAILURE);
	}

	printf("Memory attached at %X\n", (int)shared_memory);

	shared_stuff = (struct shared_use_st *)shared_memory;
	while(running) 
	{
		while(shared_stuff->written_by_you == 1) //判断是否已经写入
		{
			sleep(1);            
			printf("waiting for client...\n");
		}
		printf("Enter some text: ");
        //BUFSIZ为内存缓存,默认8192 8M 
		fgets(buffer, BUFSIZ, stdin);
        
		strncpy(shared_stuff->some_text, buffer, TEXT_SZ);
		shared_stuff->written_by_you = 1;

		if (strncmp(buffer, "end", 3) == 0) 
		{
			running = 0;
		}
	}

	if (shmdt(shared_memory) == -1)  //断开共享内存
	{
		fprintf(stderr, "shmdt failed\n");
		exit(EXIT_FAILURE);
	}
	printf("producer exit.\n");
	exit(EXIT_SUCCESS);
}

//customer.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define TEXT_SZ 2048   //定义2M大小
struct shared_use_st 
{
	int written_by_you;
	char some_text[TEXT_SZ];
};

int main()
{
	int running = 1;
	void *shared_memory = (void *)0;
	struct shared_use_st *shared_stuff;
	int shmid;	
	srand((unsigned int)getpid());    //将pid作为随机数种子传入
    //IPC密钥设置为1234,内存大小为2052,权限为可读可写
	shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);
	if (shmid == -1) 
	{
		fprintf(stderr, "shmget failed\n");
		exit(EXIT_FAILURE);
	}

	shared_memory = shmat(shmid, (void *)0, 0);//连接共享内存.自动选择一个合适的内存空间 默认为读写权限
	if (shared_memory == (void *)-1) {
		fprintf(stderr, "shmat failed\n");
		exit(EXIT_FAILURE);
	}
	printf("Memory attached at %X\n", (int)shared_memory);
	shared_stuff = (struct shared_use_st *)shared_memory;
	shared_stuff->written_by_you = 0;  //清空读写位 ,使得默认为未写入状态
	while(running) 
	{
		if (shared_stuff->written_by_you) //是否为已经写入
		{
			printf("You wrote: %s", shared_stuff->some_text);
			sleep( rand() % 4 );  //随机产生0-3的随机数
			shared_stuff->written_by_you = 0;   //清空读写位 ,使得默认为未写入状态
			if (strncmp(shared_stuff->some_text, "end", 3) == 0) 
			{
				running = 0;
			}
		}
	}
	if (shmdt(shared_memory) == -1)         //断开共享内存
	{
		fprintf(stderr, "shmdt failed\n");
		exit(EXIT_FAILURE);
	}
	if (shmctl(shmid, IPC_RMID, 0) == -1)     //从系统中删除共享内存 默认为0
	{
		fprintf(stderr, "shmctl(IPC_RMID) failed\n");
		exit(EXIT_FAILURE);
	}	
	printf("customer exit.\n");
	exit(EXIT_SUCCESS);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值