linux两个进程通过共享内存通信例子

文章详细描述了Linux进程中两个进程如何通过共享内存进行通信,以及如何使用信号灯集来管理对共享内存的并发访问,确保数据同步和避免竞态条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

linux进程通过共享内存通信的例子

例子1:两个进程通过共享内存通信,一个进程向共享内存中写入数据,另一个进程从共享内存中读出数据

文件1 创建进程1,实现功能,打印共享内存中的数据

#include
#include
#include
#include
#include
#include
#include <string.h>

int main(int argc, const char *argv[])
{
key_t key;
int shmid;
char *p = NULL;

//    key_t ftok(const char *pathname, int proj_id);;
key = ftok("./app",'b'); //获取唯一的 key 值,
if(key < 0)
{
    perror("fail ftok ");
    exit(1);
}

shmid = shmget(key, 128, IPC_CREAT|IPC_EXCL|0777);//创建/打开共享内存,返回id根据id映射
if(shmid < 0)
{
    if(errno == EEXIST)//文件存在时,直接打开文件获取shmid
    {
        printf("file eexist");
        shmid = shmget(key,128,0777);
    }
    else
    {
        perror("shmget fail ");
        exit(1);
    }
}
p = (char *)shmat(shmid,NULL,0);//映射,返回地址,根据地址操作
if( p == (char *)(-1) )
{
    perror("shmat fail ");
    exit(1);
}

while(1)
{
    sleep(1);
    printf("P:%s",p);
    if(strstr(p,"quit") != NULL) //接收到 quit 结束循环
    {
        break;
    }
}
shmdt(p);//解除映射

//函数原型 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmctl(shmid,IPC_RMID,NULL); //删除
return 0;

}
  文件2 创建进程2 实现功能,获取终端输入的数据写到共享内存中,这两个进程需要同时启动才可以实现通信

#include
#include
#include
#include
#include
#include
#include <string.h>

int main(int argc, const char *argv[])
{
key_t key;
int shmid;
char *p = NULL;
// key_t ftok(const char *pathname, int proj_id);;
key = ftok(“./app”,‘b’); //创建key值,
if(key < 0)
{
perror("fail ftok ");
exit(1);
}
shmid = shmget(key,128,IPC_CREAT|IPC_EXCL|0777);//创建/打开共享内存,返回id根据id映射
if(shmid < 0)
{
if(errno == EEXIST)//文件存在时,直接打开文件获取shmid
{
printf(“file eexist”);
shmid = shmget(key,128,0777); //共享内存存在时,直接打开
}
else
{
perror("shmget fail ");
exit(1);
}
}
p = (char *)shmat(shmid,NULL,0);//映射,返回地址,根据地址操作
if( p == (char *)(-1) )
{
perror("shmat fail ");
exit(1);
}
while(1) //接收到 quit 结束循环
{
//read 时p中 内容不会清空
//read(0,p,10);//终端读数据,写入p指向的空间,读取数据时不会清空
fgets(p,10,stdin);
if(strstr(p,“quit”) != NULL)
{
break;
}
}
shmdt§;//解除映射

//函数原型 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmctl(shmid,IPC_RMID,NULL); //删除
return 0;

}
测试:在进程2终端下输入数据,会在进程1 的终端下打印出来,但是一直打印(输入quit退出循环)

例子2 :使用信号灯集访问临界资源(共享内存)处理上面的一直循环打印的问题

通过创建包含 2 个信号灯的灯集, 分别控制两个进程访问共享内存 ( 2值信号灯 )

文件 1 进程1 通过终端输入数据写入共享内存, 两个信号灯,先允许通过终端 输入数据进程操作

#include
#include
#include
#include
#include
#include
#include <string.h>
#include

int semid; //信号灯集id
union semnum
{
int val; //信号灯值
};

union semnum mynum;

struct sembuf mybuf; //定义操作信号灯的结构,PV操作,哪个灯

//参数 信号灯集id 和 是哪个信号灯
void sem_p(int semid, unsigned short num) //P操作函数
{
mybuf.sem_num = num; //第一个信号灯,(信号灯编号)
mybuf.sem_op = -1; //进行P操作, 为 1 时表示V操作
mybuf.sem_flg = 0; //阻塞,表示semop函数的操作是阻塞的,直到成功为止
semop(semid, &mybuf, 1); //最后一个参数,表示操作信号灯的个数
}

//参数 信号灯集id 和 是哪个信号灯
void sem_v(int semid, unsigned short num) //V操作函数
{
mybuf.sem_num = num;
mybuf.sem_op = 1; //1 表示V 操作
mybuf.sem_flg = 0; //阻塞,表示semop函数的操作是阻塞的,直到操作成功为止
semop(semid, &mybuf, 1); //操作的 mybuf 全部变量信号灯集
}

int main(int argc, const char *argv[])
{
key_t key;
int shmid;
char *p = NULL;

//    key_t ftok(const char *pathname, int proj_id);;
key = ftok("./app",'b'); //创建key值,
if(key < 0)
{
    perror("fail ftok ");
    exit(1);
}

//IPC_EXCL | IPC_CREAT 信号灯不存在就创建
semid = semget(key, 2, IPC_CREAT|IPC_EXCL|0666); //创建信号灯,IPC_EXCL 问信号灯存不存在
if(semid < 0)
{
    if(errno == EEXIST)//存在时,只需要打开即可
    {
        semid = semget(key,2,0666); //打开信号灯
    }
    else
    {
        perror("semget fail ");       exit(1);
    }
}
else
{
    mynum.val = 0;  //设置信号灯值
    semctl(semid,0,SETVAL,mynum); //初始化一个信号灯
    
    mynum.val = 1;
    semctl(semid,1,SETVAL,mynum);
}

shmid = shmget(key,128,IPC_CREAT|IPC_EXCL|0777);//创建/打开共享内存,返回id根据id映射
if(shmid < 0)
{
    if(errno == EEXIST)//文件存在时,直接打开文件获取shmid
    {
        printf("file eexist");
        shmid = shmget(key,128,0777); // 直接打开共享内存
    }
    else
    {
        perror("shmget fail ");
        exit(1);
    }
}

p = (char *)shmat(shmid,NULL,0);//映射,返回地址,根据地址操作
if( p == (char *)(-1) ) //判断共享内存是否正确
{
    perror("shmat fail ");
    exit(1);
}
while(1)
{
    sem_p(semid, 1); // P 操作
    //read 时 p 指向的共享内存中内容不会清空
    //read(0,p,10);//终端读数据,写入p指向的空间,读取数据时不会清空
    fgets(p,10,stdin);
    sem_v(semid, 0); // V 操作
    if(strstr(p,"quit") != NULL) //当输入 "quit" 退出循环
    {
        break;
    }
}
shmdt(p);//解除映射

//int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmctl(shmid,IPC_RMID,NULL); //删除共享内存
semctl(semid,0,IPC_RMID);//删除信号灯
return 0;

}
文件 2 进程2 读取共享内存中的数据, 通过信号灯控制, 当写操作完成时读取一次, 然后等待下次写入

#include
#include
#include
#include
#include
#include
#include <string.h>
#include

int semid; //信号灯集id
union semnum
{
int val; //信号灯值
};

union semnum mynum;

struct sembuf mybuf; //定义操作信号灯的结构

//参数 信号灯集id 和 是哪个信号灯
void sem_p(int semid, unsigned short num) //P操作函数
{
mybuf.sem_num = num; //第一个信号灯,(信号灯编号)
mybuf.sem_op = -1; //进行P操作, 为 1 时表示V操作
mybuf.sem_flg = 0; //阻塞,表示semop函数操作是阻塞的,直到成功为止  //函数semop是对信号灯进行PV操作的函数,通过上面的结构体,参数,确定是P操作还是V操作,确定是对哪个信号灯的PV 操作,simid表示操作的是那个信号灯集
semop(semid, &mybuf, 1); //最后一个参数,表示操作信号灯的个数
}

//参数 信号灯集id 和 是哪个信号灯
void sem_v(int semid, unsigned short num) //V操作函数
{
mybuf.sem_num = num;
mybuf.sem_op = 1; //1 表示V 操作
mybuf.sem_flg = 0; //阻塞,表示semop函数操作是阻塞的,直到成功为止
semop(semid, &mybuf, 1); //操作的 mybuf 全部变量信号灯集
}

int main(int argc, const char *argv[])
{
key_t key;
int shmid;
char *p = NULL;

//    key_t ftok(const char *pathname, int proj_id);;
key = ftok("./app",'b'); //创建key值,
if(key < 0)
{
    perror("fail ftok ");
    exit(1);
}

//IPC_EXCL | IPC_CREAT 信号灯不存在就创建
semid = semget(key, 2, IPC_CREAT|IPC_EXCL|0666); //创建信号灯,IPC_EXCL 问信号灯存不存在
if(semid < 0)
{
    if(errno == EEXIST)//存在时,只需要打开即可
    {
        semid = semget(key,2,0666); //打开信号灯
    }
    else
    {
        perror("semget fail ");
    }
}
else
{
    mynum.val = 0;  //设置信号灯值
    semctl(semid,0,SETVAL,mynum); //初始化一个信号灯
    
    mynum.val = 1;
    semctl(semid,1,SETVAL,mynum);
}

shmid = shmget(key,128,IPC_CREAT|IPC_EXCL|0777);//创建/打开共享内存,返回id根据id映射
if(shmid < 0)
{
    if(errno == EEXIST)//文件存在时,直接打开文件获取shmid
    {
        printf("file eexist");
        shmid = shmget(key,128,0777);
    }
    else
    {
        perror("shmget fail ");
        exit(1);
    }
}
p = (char *)shmat(shmid,NULL,0);//映射,返回地址,根据地址操作
if( p == (char *)(-1) )
{
    perror("shmat fail ");
    exit(1);
}
while(1)
{
    sleep(1);
    sem_p(semid, 0);
    printf("P:%s",p);
    sem_v(semid, 1);    
//    write(0,p,10);//向终端写数据,把p指向的空间的内容
    if(strstr(p,"quit") != NULL)
    {
        break;
    }
}
shmdt(p);//解除映射

//int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmctl(shmid,IPC_RMID,NULL); //删除
semctl(semid,0,IPC_RMID);//删除信号灯
return 0;

}
测试:

例子2中通过两个2值信号灯实现同步操作,先把写数据到共享内存的信号灯初始化值为1,读共享内存的信号灯初始化为0,让写操作的进程先操作共享内存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值