Linux学习_IPC进程间通信
Posted Leslie X徐
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux学习_IPC进程间通信相关的知识,希望对你有一定的参考价值。
IPC进程间通信
进程之间数据通信的手段:
一、管道
- 匿名管道:只允许亲缘关系进程使用
- 使用pipe创建匿名管道
#include <unistd.h>
int pipe(int pipefd[2]);
成功返回0,失败返回-1
pipe执行后,将管道读端与pipe[0]绑定,写端与pipe[1]绑定。
注意pipe()在fork()前使用保证两个进程共用一根管道。
- 命名管道:任何进程都可以使用
- 使用mkfifo创建命名管道
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
注意:管道文件不能使用fopen打开,FILE*只能对于普通文件使用。
二、信号量
- 进程间的信号量,需要在内核上创建,由两个不同的进程共同访问,创建步骤如下:
- 在硬盘上创建文件,两个进程通过该文件密钥,在内核上访问同一个信号量。
- 获取文件密钥:使用函数ftok()
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
返回:密钥值
参数:
pathname: 准备创建密钥的文件名路径,此文件必须存在
proj_id: 范围0~255
The ftok() function uses the identity of the file named by the given
pathname (which must refer to an existing, accessible file) and the
least significant 8 bits of proj_id (which must be nonzero) to generate
a key_t type System V IPC key, suitable for use with msgget(2),
semget(2), or shmget(2).
- 根据密钥创建或者访问信号量
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
返回:成功返回信号量id,失败返回-1
参数:
nsems:准备创建的信号量文件数组容量
semflag:默认IPC_CREAT | 0664
- 初始化信号量:使用函数semctl
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
返回:成功返回信号量id,失败返回-1
参数:
semid:信号量id
semnum:等待操作的信号量的下标
cmd:具体操作
IPC_SET:
IPC_RMID:
注意:可变参,如果cmd传的是IPC_SET ,SETVAL就需要传入具体的初始化值,是一个联合体,需要自己声明。
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) */
};
- 操作信号量,可以随便加减,需要使用semop函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
返回:成功返回信号量id,失败返回-1
参数:
nsops:等待操作的信号量的数量
sops:信号量结构体指针,信号量加减完全取决于此结构体。
该结构体成员如下
unsigned short sem_num; /* 信号量下标 */
short sem_op; /* +n -n */
short sem_flg; /* operation flags信号量属性,默认SEM_UNDO,代表程序结束信号量由内核自动回收 */
注意:具体使用时需要根据结构体自己封装一个post wait操作。
三、消息队列
四、共享内存
- 需要通过一个文件创建密钥,再通过密钥访问内核中的一个共享内存
- 创建文件
- 获取密钥ftok
- 通过密钥获取共享内存id,使用shmget函数
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
key:密钥
size:准备创建的共享内存大小
shmflg:写 IPC_CREAT | 0664
- 将内核中的共享内存映射到内存中来,使用函数shmat
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:
shmid:共享内存id
shmaddr:0
想要映射的具体地址,可以传个数组首地址,也可以传一个堆空间的首地址。通常情况下,直接传0,代表内存地址由系统自己决定
shmflg:内存标记位,如果第二个参数传0的话,该参数直接传0即可,代表降低程序对硬件的耦合度
返回:
该函数将映射好的地址的首地址返回,返回的是一个void*
- 直接向共享内存所映射内存中写入数据即可
- 当共享内存不再使用时,注意删除共享内存,使用shmctl函数
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
cmd:IPC_RMID
buf:0
注意在销毁共享内存之前,先要解除共享内存的映射
使用函数shmdt
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
题1
要求:
- 创建两个文件
- 文件1:输入三角形/长方形的三条边长
- 文件2:根据文件1输入的数据数量,计算三角形/长方形的面积。
文件1:
/*
* 1.c
* 输入三角形/长方形的边长
*/
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int pipefd[2]={(int)*argv[1],(int)*argv[2]};
printf("pipefd: %d %d\\n",pipefd[0],pipefd[1]);
int i=4,arr[4];
printf("请输入边长(三角形则最后一条边长为0):\\n");
while(i--){
printf("请输入第%d条边长:",4-i);
scanf("%d",&arr[3-i]);
}
write(pipefd[1],arr,sizeof(arr));
return 0;
}
文件2:
/*
* 2.c
* 创建两个文件
* 文件1:输入三角形/长方形的三条边长
* 文件2:根据文件1输入的数据数量,计算三角形/长方形的面积。
*/
#include <stdio.h>
#include <unistd.h>
#include <wait.h>
char* path="./1";
int main(int argc, char **argv)
{
int pid, pipefd[2];
pipe(pipefd);
pid = fork();
if(pid==0){
execlp(path,path,&pipefd[0],&pipefd[1],NULL);
}
if(pid>0){
wait(0);
int arr1[4]={0};
read(pipefd[0],arr1,sizeof(arr1));
if(((arr1[3]==0)&&
(arr1[0]+arr1[1]>arr1[2] ||
arr1[0]+arr1[2]>arr1[1] ||
arr1[1]+arr1[2]>arr1[0])))
{printf("\\n三角形周长为:%d\\n",arr1[0]+arr1[1]+arr1[2]);}
else if
((arr1[3]!=0)&&
((arr1[0]==arr1[1] &&arr1[2]==arr1[3]) ||
(arr1[0]==arr1[3] &&arr1[1]==arr1[2]) ||
(arr1[0]==arr1[2] &&arr1[1]==arr1[3]) ))
{printf("\\n长方形周长为:%d\\n",arr1[0]+arr1[1]+arr1[2]+arr1[3]);}
else if(!(arr1[0]&arr1[1]&arr1[2]&arr1[3])){
printf("输入错误!\\n");
}
else{
printf("无法识别形状\\n");
}
}
return 0;
}
题目2:聊天
要求:使用命名管道实现进程间聊天
文件1_聊天:
/*
* 1_聊天.c
*
*/
#include <stdio.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
char* rfifo = "to1";
char* wfifo = "to2";
int rfd,wfd;
pid_t pid1,pid2;
pthread_t th;
char recv[128]={'\\0'};
char send[128]={'\\0'};
void sig_handler(int signum)
{
if(signum==SIGINT){
close(rfd);
close(wfd);
kill(pid2,SIGINT);
pthread_cancel(th);
printf("\\n1程序结束\\n");
exit(0);
}
}
void* th_fn(void* arg)
{
while(1){
if(read(rfd,recv,sizeof(recv))==0)break;
printf("\\n接收到内容:%s\\n",recv);
printf("请输入发送内容:");
fflush(stdout);
memset(recv,0,sizeof(recv));
}
return NULL;
}
int main(int argc, char **argv)
{
printf("按ctl+C结束聊天\\n");
mkfifo(rfifo,0666);
mkfifo(wfifo,0666);
wfd = open(wfifo,O_WRONLY);
rfd = open(rfifo,O_RDONLY);
pid1=getpid();
write(wfd,&pid1,sizeof(pid1));
read(rfd,&pid2,sizeof(pid2));
printf("与2号聊天: %d\\n" ,pid2);
pthread_create(&th,0,th_fn,0);
pthread_detach(th);
while(1){
printf("请输入发送内容:");
scanf("%s",send); while(getchar()!=10);
write(wfd,send,strlen(send));
memset(send,0,sizeof(send));
signal(SIGINT,sig_handler);
}
return 0;
}
文件2_聊天:
/*
* 2_聊天.c
*
*/
#include <stdio.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
char* rfifo = "to2";
char* wfifo = "to1";
int rfd,wfd;
pid_t pid1,pid2;
pthread_t th;
char recv[128]={'\\0'};
char send[128]={'\\0'};
void sig_handler(int signum)
{
if(signum==SIGINT){
close(rfd);remove(rfifo);
close(wfd);remove(wfifo);
pthread_cancel(th);
printf("\\n2程序结束\\n");
exit(0);
}
}
void* th_fn(void* arg)
{
while(1){
printf("请输入发送内容:");
scanf("%s",send); while(getchar()!=10);
write(wfd,send,strlen(send));
memset(send,0,sizeof(send));
}
}
int main(int argc, char **argv)
{
mkfifo(rfifo,0666);
mkfifo(wfifo,0666);
rfd = open(rfifo,O_RDONLY);
wfd = open(wfifo,O_WRONLY);
pid2=getpid();
write(wfd,&pid2,sizeof(pid2));
read(rfd,&pid1,sizeof(pid1));
printf("与1号聊天: %d\\n" ,pid1);
pthread_create(&th,0,th_fn,0);
pthread_detach(th);
while(1){
if(read(rfd,recv,sizeof(recv))==0)break;
printf("\\n接收到内容:%s\\n",recv);
printf("请输入发送内容:");
fflush(stdout);
memset(recv,0,sizeof(recv));
signal(SIGINT,sig_handler);
}
return 0;
}
题目3:进程间信号量
要求:
- 创建一对父子进程
- 使用进程间信号量实现
- A-B-A-B交替打印
- 代码1:使用一个信号量
/*
* sem_test1.c
*
* 创建一对父子进程
* 使用进程间信号量实现
* A-B-A-B交替打印
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <stdlib.h>
char* keysem = "./sem_test1";
int id;
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
void sig_handler(int signum){
switch(signum){
case SIGINT:
semctl(id,0,IPC_RMID,0);
printf("程序结束!\\n");
sleep(2);
exit(0);
default :break;
}
}
void semPost(int semid,int sempos,int val){
struct sembuf buf = {0};
buf.sem_num = sempos;
buf.sem_op = (val>=0)?val:(-val);
buf.sem_flg = SEM_UNDO;
semop(semid,&buf,1);
}
void semWait(int semid,int sempos,int val){
struct sembuf buf = {0};
buf.sem_num = sempos;
buf.sem_op = (val<0)?val:(-val);
buf.sem_flg = SEM_UNDO;
以上是关于Linux学习_IPC进程间通信的主要内容,如果未能解决你的问题,请参考以下文章