进程间通信(IPC)的概念:进程间通信是指在不同进程间进行信息传输或交换,IPC的方式通常有管道,消息队列,信号量,共享存储,Socket,Stream等
无名管道,UNIX系统最古老的形式
特点:①.半双工,具有固定的读写
②:只能用于父子进程和兄弟进程
③:.可以看做是一种普通文件,他的读写可以用read和write等函数,但不是普通文件,他只存在于内存当中。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(){
int fd[2];
char buf[128];
int pid;
char *writeData = "this is write_data";
if(pipe(fd) == -1){
printf("pipe create failed!\n");
}
pid = fork();
if(pid == 0){
sleep(1);
close(fd[1]);
read(fd[0],buf,128);
printf("read data: %s\n",buf);
exit(0);
}
close(fd[0]);
write(fd[1],writeData,strlen(writeData));
wait(NULL);
return 0;
}
有名管道
函数有:
mkfifo(path,mod); 创建管道
path路径名,mod模式
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdlib.h>
int main(){
if(mkfifo("./file",0600)==-1 && errno == EEXIST){
printf("mkfifo failed!\n");
perror("why");
exit(0);
}
printf("mkfifo success!\n");
return 0;
}
这是以创建一个文件来进行管道传输的,使用这个有名管道进行通信时,要么读,要么写,不能同时
读的代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
int main(){
char buf[30]={0};
int n_read;
int fd = open("./file",O_RDONLY);
printf("open success\n");
n_read = read(fd,buf,30);
while(n_read > 0){
sleep(1);
n_read = read(fd,buf,30);
printf(buf);
}
close(fd);
return 0;
}
写的代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
int main(){
int i;
char *str = "this is write data\n";
int fd = open("./file",O_WRONLY);
printf("open success\n");
for(i = 0; i<5;i++){
write(fd,str,strlen(str));
}
close(fd);
return 0;
}
3.消息队列,是消息的链接表,存放在内核中,一个消息队列由一个标识符即队列ID来标识。
特点:1.消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级
2.消息队列独立于发送与接收进程,进程终止时,消息队列及其内容并不会被删除。
3.消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
创建队列msgget
接收消息 msgrcv
发送消息 msgsnd
操作队列msgctl
收发消息的函数原型
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);
代码demo6
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.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);*
*/
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main(){
key_t key = ftok(".",1);
int msgId;
struct msgbuf msgread;
//创建队列
if(msgId = msgget(key,IPC_CREAT|0700) == -1){
printf("create queue failed\n");
}
//接收消息
msgrcv(msgId,&msgread,sizeof(msgread.mtext),888,0);
printf("receive:%s\n",msgread.mtext);
//再发消息
struct msgbuf msgsend = {999,"thanks!"};
msgsnd(msgId,&msgsend,strlen(msgsend.mtext),0);
//删除队列
msgctl(msgId,IPC_RMID,NULL);
return 0;
}
代码demo7
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
/*
nt 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);*
*/
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main(){
key_t key = ftok(".",1);
int msgId;
struct msgbuf msgwrite = {888,"aaa"};
//创建队列
if(msgId = msgget(key,IPC_CREAT|0700) == -1){
printf("create queue failed\n");
}
//发送消息
msgsnd(msgId,&msgwrite,strlen(msgwrite.mtext),0);
//再接收消息
struct msgbuf msgrece;
msgrcv(msgId,&msgrece,sizeof(msgrece.mtext),999,0);
printf("rece:%s\n",msgrece.mtext);
//删除队列
msgctl(msgId,IPC_RMID,NULL);
return 0;
}
4.共享内存
//创建或获取一个共享内存,成功则返回ID,失败返回-1

//映射共享内存(连接当前进程的地址空间和共享内存,成功返回指向共享内存的指针,失败返回-1)

//写入东西,就相当于直接copy内容到当前进程的映射地址即可

//断开连接共享内存

//删除共享内存

下面这两个是命令行指令
ipcs -m 在控制台能够查看现在共享内存的使用情况,
ipcrm -m XX 删除shmid == XX的这个共享内存
5.信号
5.1信号低级版
有分低级版和高级版
低级版就是没有进行消息的传输
代码如下
demo10,对命令行命令kill进行替换 ,可以使用kill -l 查看详细的对应字段
#include <stdio.h>
#include <signal.h>
void handler(int signum){
printf("input sinnum is : %d",signum);
switch(signum){
case 2:
printf("SIGINT\n");
break;
case 9:
printf("SIGKILL\n");
break;
case 10:
printf("SIGUSR1\n");
break;
}
printf("can't kill\n");
}
int main(){
signal(SIGINT,handler);
signal(SIGKILL,handler);
signal(SIGUSR1,handler);
while(1);
return 0;
}
demo11 ,下面的代码功能是,运行程序时传入signum和pid参数,对应的是你要在命令行执行的
kill -XX XX的指令 例如 kill -2 进程ID,原本执行的功能是中断,但替换后不会执行原有功能,而是执行我们自己编写的handler的功能
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
int main(int argc,char **argv){
int signum;
int pid;
char cmd[128] = {0};
//这里要把数组里的字符串转换为int,用atoi char to int
signum = atoi(argv[1]);
pid = atoi(argv[2]);
sprintf(cmd,"kill -%d %d",signum,pid);
printf(cmd);
system(cmd);
return 0;
}
5.2信号高级版
就是加入的消息的传输,个人认为有点操蛋。直接上代码吧。
函数原型
//函数原型
//int sigaction(int signum, const struct sigaction *act,
// struct sigaction *oldact);
/**
*
*struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
*/
具体使用代码
接收端:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
void handler(int signum,siginfo_t *info,void *context){
printf("get signum : %d\n",signum);
if(context != NULL){
printf("siginfo1 : %d\n",info->si_int);
printf("siginfo2 : %d\n",info->si_value.sival_int);
}
}
int main(){
struct sigaction act;
act.sa_sigaction = handler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGUSR1,&act,NULL);
while(1);
return 0;
}
发送端:
#include <stdio.h>
#include <signal.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("over\n");
return 0;
}
6.信号量集
①信号量用于进程间同步,若要在进程间传递胡数据需要结合共享内存。
②信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。(P操作:拿锁。V操作:放回锁)
③每次对信号量的 PV 操作不仅限于对信号量值加 1 或 减1 ,而且可以加加减任意正整数。
④支持信号量组。
//创建或获取一个信号量组,成功会返回信号量集 ID ,失败返回 -1
int semget(key_t key, int nsems, int semflg);
//对信号量组进行操作,改变信号量的值,成功返回 0,失败返回 -1 (用于 PV 操作)
int semop(int semid, struct sembuf *sops, unsigned nsops);
//控制信号量的相关信息 (用于给信号量初始化)
int semctl(int semid, int semnum, int cmd, ...);
示例代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>
//int semget(key_t key, int nsems, int semflg);
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) */
};
//
对信号量组进行操作,改变信号量的值,成功返回 0,失败返回 -1 (用于 PV 操作)
//int semop(int semid, struct sembuf *sops, unsigned nsops);
//拿锁
void getKey(int semid){
struct sembuf mysem1;
mysem1.sem_num = 0;
mysem1.sem_op = -1;
mysem1.sem_flg = SEM_UNDO;
semop(semid,&mysem1,1);
printf("getkey\n");
}
//还锁
void putKey(int semid){
struct sembuf mysem1;
mysem1.sem_num = 0;
mysem1.sem_op = 1;
mysem1.sem_flg = SEM_UNDO;
semop(semid,&mysem1,1);
printf("putKey\n");
}
int main(){
//创建一个信号量集
key_t key;
int semid = semget(key,0,IPC_CREAT|0700);
//int semctl(int semid, int semnum, int cmd, ...);
//初始化信号量集
union semun initsem;
initsem.val = 0;
semctl(semid,0,SETVAL,initsem);
//用父子进程来操作这个信号量
int pid = fork();
if(pid == 0){
printf("this is son\n");
//拿锁
getKey(semid);
exit(0);
}
printf("this is father\n");
//还锁
putKey(semid);
wait();
//拿锁
getKey(semid);
//还锁
putKey(semid);
return 0;
}