Linux系统编程——进程间的通信

每个进程都是独立的实体。一个应用程序有时候要分多个进程协同合作才能完成任务。类似一个团队开发一个项目一样,需要多人合作,合作肯定少不了沟通。一个应用程序可能也要多个进程之间也一样,进程之间有自己的交流的语言——IPC(通信机制)

无名管道

  • 从名字可知,无名管道没有名字,无法open(),可以read()/write(),所以只能由父进程创建,子进程继承才能使用。
  • 读写分离,有两个文件描述符,一个读端,一个写端,所以是半双工工作方式
  • 只能用于亲缘进程之间通信(父子、兄弟)
  • 写操作无原子性,多进程写入就会造成数据覆盖,所以只能用于一对一通信。
  • 它是一种特殊文件,不存在于文件系统中,只存在于内存中;不能使用**lseek()**定位

创建无名管道:

man pipe

pipe, pipe2 - create pipe

#include <unistd.h>
int pipe(int pipefd[2]);
       
#define _GNU_SOURCE             /* See feature_test_macros(7) */
#include <fcntl.h>              /* Obtain O_* constant definitions */
#include <unistd.h>

int pipe2(int pipefd[2], int flags);

//pipefd[0] —— 读端
//pipefd[1] —— 写端

RETURN VALUE
    成功返回0, 错误返回-1, 以及 errno

写代码:

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

int main(int argc, char **argv)
{
    int pipefd[2];

    int ret = pipe(pipefd);
    if (ret < 0)        //创建管道失败
    {
        printf("faild to create pipe \r\n");
        return -1;
    }

    pid_t pid = fork();            //创建子进程

    if (pid < 0)             //创建失败
    {
        printf("failed to create child progress \r\n");
        return -1;
    }   

    char write_buf[] = "Hello! My fahter";

    if (pid == 0)            //子进程
    {
        while (1)
        {
            write(pipefd[1], write_buf, strlen(write_buf));         //向管道中写入消息    
            sleep(1);
        }
    }

    char read_buf[1024];
    if (pid > 0)
    {
        while(1)
        {
            read(pipefd[0], read_buf, strlen(write_buf));
            printf("child say : %s\r\n", read_buf);
            sleep(1);
        }
    }

    return 0;
}

有名管道

  • 因为有名字,存在普通文件系统中,可以使用open来获得文件描述符。open时使用O_WRONLY只打开写端O_RDONLY只打开读端O_WRONLY读写端都打开。读写两端可以被多个进程同时打开。

  • 普通文件一样的read()/write()读写

  • 写操作具备原子性,多进程同时写入数据不会造成数据互相覆盖。

  • First In First Out

  • 写进程向管道写数据,无进程读数据,写进程会堵塞。当有进程读数据,写进程恢复运行。

  • 读进程读取数据,管道中没有数据,读进程阻塞。当有进程写入数据,读进程读取数据以及恢复运行。

  • **open()时带O_NONBLOCK参数打开获取fcntl()**改变fifo模式为非阻塞则不会阻塞

  • 可以用于亲缘进程以及非亲缘进程之间的通信。

创建有名管道两种方式:

  • shell命令 : mkfifo <管道文件名>
  • int mkfifo(const char *pathname, mode_t mode)

mkfifo函数man手册描述:

man 3 mkfifo

mkfifo, mkfifoat - make a FIFO special file (a named pipe)


#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);
//pathname —— FIFO文件名
//mode —— FIFO权限

#include <fcntl.h>           /* Definition of AT_* constants */
#include <sys/stat.h>
int mkfifoat(int dirfd, const char *pathname, mode_t mode);

代码

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


int main(int argc, char **argv)
{
    char write_buf[] = "Linux,RT-Thread,uCos,FreeRTOS";
    char read_buf[1024]; 

    int len = strlen(write_buf);

    if (access("test_fifo", F_OK))       //判断管道文件是否已经存在
    {
        int ret = mkfifo("test_fifo", 0644);              //创建管道
         if (ret < 0)
         {
             printf("fail to mkfifo\r\n");
            return -1;
        }
    }

    pid_t pid = fork();

    if (pid < 0)
    {
        printf("failed to fork\r\n");
        return -1;
    }

    if (pid == 0)      //子进程
    {
        int fifo = open("test_fifo",  O_WRONLY);          //只读方式打开
        write(fifo, write_buf, strlen(write_buf));         
        close(fifo);
    }

    if (pid > 0)     //父进程
    {
        int fifo = open("test_fifo", O_RDONLY);      //只写方式打开
        read(fifo, read_buf, len);                                  //从有名管道中读数据
        printf("read : %s\r\n", read_buf);
        close(fifo);
    }

    return 0;
}

执行程序在当前目录生成了有名管道文件test_fifo

在这里插入图片描述


信号

一种异步通信机制。软件上模拟中断机制。

头文件

#include <signal.h>        //信号相关头文件

查看信号种类

在这里插入图片描述

  • 非实时信号

    前面31个称为非实时信号,从unix系统继承下来的信号,特点:

    • 不排队,会相互嵌套
    • 目标进程没有及时响应,随后到达的该信号就会被丢弃、
    • 每个非实时信号对应一个系统事件,事件发生,将产生对应的非实时信号
    • 同时存在实时和非实时信号,实时信号的响应从大到小排队响应,非实时信号没有固定的次序。
  • 实时信号

    后面31个为实时信号:

    • 有顺序的排队从大到小响应,不嵌套
    • 同样信号发送多次,不丢次,依次响应
    • 没有特殊的系统事件对应
man 7 signal   //查看更详细的信号相关信息

信号产生自内核。信号的来源主要如下:

  • 用户通过输入设备让内核产生信号,如CTRL+C终止从终端运行的进程。

  • 进程执行出错,内核产生信号。如访问非法内存浮点数溢出等。

  • 进程通过系统调用kill()发送信号给另一个进程/raise()进程自己给自己发信号。

信号的处理

  • 默认处理,比如CTRL+C就会向连接到终端的运行的进程发送SIGINT信号,默认处理下进程就会终止运行。
  • 忽略信号,通过系统调用signal()设置忽略某个信号,不做处理。
  • 通过系统调用signal()注册信号处理函数,当收到该信号,执行用户自定义的处理操作。
  • 阻塞某些或某个信号,阻塞只是暂时挂起而不是忽略
  • 特殊的两个信号:SIGKILLSIGSTOP不能被忽略、阻塞以及捕捉
#include <signal.h>

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);  //设置信号处理函数或者忽略信号的函数
//signum —— 发送的信号
//handler —— 信号处理方式,可以是SIG_IGN、SIG_DEL或者函数

signal函数sighandler_t handler的取值:

  • SIG_IGN //忽略 信号
  • SIG_DFL, //默认处理方式
  • sighandler_t 类型的函数 //自定义处理方式
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>

void sigint_handler(int num)
{
    printf("hello gcc\r\n");
}
int main(int argc, char **argv)
{
    signal(SIGINT, sigint_handler);  //Ctrl+C发出SIGINT信号,默认处理是结束进程,一旦设置自己的处理函数,将不再执行默认处理即不终止进程
	//signal(SIGINT, SIG_DFL);  //默认处理即终止进程
    //sinal(SIGINT, SIG_IGN);   //不做任何处理
    while(1)
    {
        printf("hello linux\r\n");
        sleep(1);
    }
    return 0;
}

向进程发送信号:

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

int kill(pid_t pid, int sig);       //向某指定进程发送指定信号
//pid —— 信号要发送的信号(进程号)
//sig —— 要发送的信号(查看信号表)
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    pid_t pid = fork();   

    if (pid == 0)    //子进程
    {
        while(1)
        {
            printf("hello!!! linux\r\n");
            sleep(1);
        }
    }
    
    if (pid > 0)    //父进程
    {
        sleep(10);
        kill(pid, SIGSTOP);     // 向子进程发送信号
    }

    return 0;
}

阻塞或者解除阻塞一个或多个信号:

#include <signal.h>   //头文件

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

//how —— 阻塞的方式
SIG_BLOCK  //在原有阻塞信号基础上,增加set信号

SIG_UNBLOCK   //在原有阻塞信号基础上,解除set信号

SIG_SETMASK  //在原有阻塞信号基础上,替换为set信号
    
//set —— 新的信号
//oldset —— 原有的阻塞信号

信号发送和捕获函数plus版本

sigqueue - queue a signal and data to a process //向指向进程发送执行信号以及可以携带指定数据

#include <signal.h>

int sigqueue(pid_t pid, int sig, const union sigval value);   

union sigval联合体定义:
 union sigval {
    int   sival_int;
    void *sival_ptr;
};
sigaction, rt_sigaction - examine and change a signal action   //捕获以及更改捕捉到信号的操作

#include <signal.h>

int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);  //捕捉指定信号以及可以指定捕捉后的操作,还可以通过扩展函数来获取信号携带的额数据

//signum    捕捉的信号
sigaction结构体
struct sigaction {
	union {
	  __sighandler_t _sa_handler; //标准信号响应函数指针
	  void (*_sa_sigaction)(int, struct siginfo *, void *);     //扩展响应信号函数指针
	} _u;
	sigset_t sa_mask;  //信号掩码,使用信号集操作函数设置
	unsigned long sa_flags;
	void (*sa_restorer)(void);
};

//使用扩展信号响应函数,sa_flags必须设置SA_SIGINFO

System V IPC

包含以下三个:

  • 消息队列
  • 共享内存
  • 信号量

进程每打开一个IPC对象就会获得一个ID,ID是可变的,之后就可以使用ID来操作IPC对象。类似进程进程有PID,IPC对象也有类似——key键值key是唯一的。跟操作文件做对比,ID类似文件描述符,key类似文件路径。


ftok - convert a pathname and a project identifier to a System V IPC key (创建System V IPC对象的key)

#include <sys/types.h>
#include <sys/ipc.h>

key_t ftok(const char *pathname, int proj_id); //创建key的函数
//pathname —— 路径
// proj_id —— 整数
 
RETURN VALUE   //返回值
   On success, the generated key_t value is returned.  On failure -1 is returned, with errno indicating the error as for the  stat(2) (成功返回key值,失败返回-1)

信号量

RTOS中的信号量和Linux中信号的概念和作用差不多。

Linux三种信号量:

  • system-V信号量
  • posix有名信号量
  • posix无名信号量

信号量的作用:

  • 进程、线程之间的同步

  • 临界资源的互斥访问。

    访问临界资源前需要对资源申请,申请资源的操作就是P操作

    退出临界区时要释放响应资源,这个操作称为V操作。信号量实际上就是类似用一个变量表示资源数,P/V操作就是对该变量进行加/减一的操作。与变量加减不同的是信号量的加减具备原子性——不会被操作系统调度机制打断。

信号量的相关三个宏:

  • SEMMNI —— 系统信号量总数最大值
  • SEMMSL —— 每个信号量值的最大值
  • SEMMNS —— 系统中所有信号量中信号量元素的总数最大值
semget - get a System V semaphore set identifier(创建信号)

//头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);
//key —— 信号唯一关键标识,键值
//nsems —— 信号量的个数
/* 
   semflag —— 信号量创建的方式 
   IPC_CREAT —— key对应信号量不存在,创建
   IPC_EXCL  —— key对应信号量存在, 报错
   mode —— 信号量访问权限(八进制,如0777)
*/

semop, semtimedop - System V semaphore operations(对信号进行P/V操作、等零操作)

/* 头文件 */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semop(int semid, struct sembuf *sops, size_t nsops);  
/*semid —— 信号量 ID */
/*sembuf —— 信号量集合的结构指针 */
/* nsops —— sops的长度*/

struct sembuf{
    short sem_num; //信号量元素序号(数组下标,从0开始)。
    short sem_op; //sem_op 成员的值是信号量在一次操作中需要改变的数值。通常只会用到两个值,一个是-1 (P操作),尝试获取信号量;一个是+1 (V操作),释放信号量。
    short sem_flg; //通常设为:SEM_UNDO,程序结束,信号量为 semop 调用前的值。
};
semctl - System V semaphore control operations(设置或获取信号的相关属性)

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

int semctl(int semid, int semnum, int cmd, ...);
/* smeid —— 信号量ID */
/* semnum —— 信号量序号,数组下标 */
/*
   cmd —— 信号量的操作方式:
   IPC_STAT —— 获取信号量的属性
   IPC_SET  —— 设置信号量的属性
   IPC_RMID —— 立即删除该信号,semnum将被忽略
   IPC_INFO —— 获得关于信号的系统限制信息
   SEM_STAT —— 同IPC_STAT
   GETALL  —— 返回所有信号量元素的值
   GETNCNT —— 返回正阻塞在对信号两元素P操作的进程总数
   GETPID —— 返回最后一个对该信号量元素操作的进程的PID
   GETVAL —— 返回该信号量的值
   GETZCNT —— 返回正阻塞该信号量元素等零操作的进程总数
   SETALL  —— 设置所有信号量元素的值 
*/
/*
  不同cmd对应的返回值:
      GETNCNT —— semncnt
      GETPID —— sempid
      GETVAL —— semval
      GETZCNT —— semzcnt
  ...
*/   
如若用到第四个参数,用户必须自己定义:
union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                (Linux-specific) */
};

进程间的同步:

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

union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO                            (Linux-specific) */
};
 
int main(int argc, char **argv)
{
  	int semid = semget((key_t)1234, 1, 0666|IPC_CREAT);//创建信号量集  

  	if(semid < 0)
 	 {
   		 printf("failed to get semid\r\n");
   		 return -1;
  	}
  
  	union semun sem_num;
  	sem_num.val = 0;
  	semctl(semid, 0, SETALL, sem_num);               //设置信号量值为1
  
  	struct sembuf sem_p = {
      	0,      //信号量元素序号
      	-1,     //信号量操作一次变化的值
      	0       
  	}; 
  
  	struct sembuf sem_v = {
     	 0,      //信号量元素序号
      	1,     //信号量操作一次变化的值
      	0       //
  	}; 

  	pid_t pid = fork();   
  	if(pid == 0)
  	{
      	while(1)
     	{
        	printf("I am child\r\n");
        	sleep(2);
        	semop(semid, &sem_v, 1);
     	}
  	} 
  	else
  	{
    	while(1)
    	{
     		 semop(semid, &sem_p, 1);
      		printf("I am father\r\n");
    	}
  	} 
  
  return 0;      
}

消息队列

消息队列类似管道,也可以说是带数据标识的特殊管道,可以读出制定数据
msgget - get a System V message queue identifier //获得一个system V的消息队列ID

//头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflg);
//key —— 消息队列键值
/*
  msgflag —— IPC_CREAT 不存在就创建
          —— IPC_EXCL  存在则报错
          —— mode 访问权限
*/

           
RETURN VALUE
  成功返回值将是消息队列标识符(非负整数),失败为-1,errno指示错误。

msgrcv, msgsnd - System V message queue operations 

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

      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); //从消息队列中读出消息
		//msgid —— 消息队列id
	    //msgp —— 发送or接受消息缓冲区
	    //msgsz —— 发送or接受数据的大小
	    //msgtyp —— 数据的类型

msgp参数定义如下:
           /* msgp —— 消息必须组织成如下结构体 */
           struct msgbuf {
               long mtype;       /* message type, must be > 0 */
               char mtext[1];    /* message data */
          };

msgflg主要有下面选项:
    IPC_NOWAIT   //非阻塞方式读写数据 
    MSG_COPY (since Linux 3.8)//从msgtyp指定的序号位置获取一个消息(消息序号从0开始),一般与IPC_NOWAIT搭配使用,没有消息立即返回。MSG_COPY标志是为内核检查点恢复功能的实现添加的,是有用的。只有在内核使用CONFIG_CHECKPOINT_RESTORE选项构建时才能使用
    MSG_EXCEPT //读出标识符不等msgtyp的第一个消息
    MSG_NOERROR //消息大小比msgsz大,截断消息而不报错 
    
RETURN VALUE
   失败返回-1,成功 msgsnd() 返回 0, msgrcv() 返回读取的字节数

msg_recv.c:

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

int main(int argc, char **argv)
{
    key_t key = ftok(MSG_PATHNAME, MSG_PRJID);    //创建消息队列的key
    int msgid = msgget(key, 0666 | IPC_CREAT);    //获取消息队列的id
    if(msgid < 0)
    {
        printf("failed to get msgid\r\n");
        return -1;
    }

    struct msgbuf my_msgbuf;
    my_msgbuf.mtype = MSG_TYPE;                //消息类型标识符
    memset(my_msgbuf.mtext, 0, MSGBUF_SIZE);   //清空消息缓冲区

    while(1)
    {
        msgrcv(msgid, &my_msgbuf, sizeof(my_msgbuf), MSG_TYPE, 0);
        printf("%s", my_msgbuf.mtext);
        // sleep(1);
    }

    return 0;
}

msg_send.c:

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

int main(int argc, char **argv)
{

    key_t key = ftok(MSG_PATHNAME, MSG_PRJID);    //创建消息队列的key
    int msgid = msgget(key, 0666 | IPC_CREAT);    //获取消息队列的id
    if(msgid < 0)
    {
        printf("failed to get msgid\r\n");
        return -1;
    }

    struct msgbuf my_msgbuf;
    my_msgbuf.mtype = MSG_TYPE;                //消息类型标识符
    memset(my_msgbuf.mtext, 0, MSGBUF_SIZE);   //清空消息缓冲区  

    int cnt = 0;
    while(1)
    {
        sprintf(my_msgbuf.mtext, "Linux is running %d\r\n", cnt++);
        msgsnd(msgid, &my_msgbuf, sizeof(my_msgbuf), 0);
        printf("sending message\r\n");
        sleep(1);
    }

    return 0;
}

common.h:

#ifndef _COMMON_H_
#define _COMMON_H_

#define MSG_PATHNAME     "."
#define MSG_PRJID        1314

#define MSGBUF_SIZE      1024        //消息队列消息缓冲区大小
#define MSG_TYPE         1

/* 消息结构体 */
struct msgbuf {
    long mtype;       /* message type, must be > 0 */
    char mtext[MSGBUF_SIZE];    /* message data */
};

#endif

共享内存

  • 效率最高的IPC
  • 将某块物理内存多次映射到不同进程的虚拟空间,造成多个进程虚拟内存空间部分重叠,从而能快速交换数据
  • 类似共享资源,一般不单独使用,配合信号量、互斥锁等进行临界区保护,防止多进程写入造成数据践踏

共享内存相关API

shmget - allocates a System V shared memory segment //分配System V 共享内存段
//头文件
#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);
//key —— shm键值
//size —— shm大小
/*
 shmflg —— 获取共享内存方式
     IPC_CREAT —— 不存在则创建
     IPC_EXCL —— 存在则报错
     SHM_HUGETLB(since Linux 2.6) —— 使用"大页面"分配shm
     SHM_HUGE_2MB、SHM_HUGE_1GB(since Linux 3.8) —— 与SHM_HUGETLB来选择hugetlb page sizes,相应的是2MB、1GB
     SHM_NORESERVE(since Linux 2.6.15) —— 不在swap分区中为shm保留内存空间
     mode —— shm访问权限
*/
shmat, shmdt - System V shared memory operations  

//头文件
#include <sys/types.h>
#include <sys/shm.h>


void *shmat(int shmid, const void *shmaddr, int shmflg);//映射shm
//shmid —— shm的id
//shmaddr —— 映射内存首地址,通常为NULL让系统自动分配
/*
  shmflg —— shm映射方式
     SHM_EXEC   —— 可以执行shm内容,,但必须拥有执行权限
     SHM_RDONLY —— 只读方式映射 无法以只写方式映射
     SHM_REMAP  —— 重新映射 此时shmaddr不能为NULL
     SHM_RND   —— 自动选择比shmaddr小的最大页对齐地址
  如果shmaddr不为NULL,SHM_RND必须被设置;如果没有设置shmaddr,shmaddr必须为严格的页对齐地址
*/
int shmdt(const void *shmaddr);  //解除shm映射
//shmaddr —— shmat映射成功返回的内存地址

RETURN VALUE
	成功 shmat() 返回内存地址;失败返回(void *) -1 以及指示错误的errno 
	成功 shmdt() 返回0; 失败返回 -1, 以及 指示错误errno

shmctl - System V shared memory control//设置shm相关属性

#include <sys/ipc.h>
#include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf); //通常用来删除shm
//shmid —— 共享内存id
/* 
  cmd —— shm的操作方式;一般为IPC_RMID,删除shm
      IPC_STAT —— 获取shm的状态
      IPC_SET —— 设置shm的状态
      IPC_RMID —— 删除shm
*/
//buf —— 属性信息结构体指针。一般为NULL

struct shmid_ds {
               struct ipc_perm shm_perm;    /* Ownership and permissions */
               size_t          shm_segsz;   /* Size of segment (bytes) */
               time_t          shm_atime;   /* Last attach time */
               time_t          shm_dtime;   /* Last detach time */
               time_t          shm_ctime;   /* Last change time */
               pid_t           shm_cpid;    /* PID of creator */
               pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
               shmatt_t        shm_nattch;  /* No. of current attaches */
               ...
           };
//更多信息可以查看man手册

shm_recv.c :

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include "common.h"

int main(int argc, char **argv)
{
    key_t key = ftok(SHM_PATHNAME, SHM_PRJID);             //创建key
    int shmid = shmget(key, SHM_SIZE, 0666 | IPC_CREAT);   // 获得shmid

    char *pbuf = (char *)shmat(shmid, NULL, 0);
    memset(pbuf, 0, SHM_SIZE);  

    char cnt = 0;
    while(1)
    {
        printf(pbuf, "linux message :", cnt++);
        sleep(1);
    }

    shmdt(pbuf);        //解除映射
    return 0;
}

shm_send.c:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include "common.h"

int main(int argc, char **argv)
{
    key_t key = ftok(SHM_PATHNAME, SHM_PRJID);             //创建key
    int shmid = shmget(key, SHM_SIZE, 0666 | IPC_CREAT);   // 获得shmid
    if(shmid < 0)
    {
        printf("failed to get shmid\r\n");
        return -1;
    }

    char *pbuf = (char *)shmat(shmid, NULL, 0);
    memset(pbuf, 0, SHM_SIZE);  

    char cnt = 0;
    while(1)
    {
        sprintf(pbuf, "linux message : %d\r\n", cnt++);
        printf("sending message\r\n");
        sleep(1);
    }

    shmdt(pbuf);        //解除映射
    return 0;
}

common.h:

#ifndef _COMMON_H_
#define _COMMON_H_

#define SHM_PATHNAME   "."
#define SHM_PRJID      1024

#define SHM_SIZE      1024

#endif

猜你喜欢

转载自blog.csdn.net/qq_36413982/article/details/106355716