IPC的方式通常有管道(无名管道,和命名管道),消息队列,信号量,共享内存,socket,streams,
其中socket 和 streams 支持不同主机之间的通信
一.管道
是半双工的,
只能亲缘关系的进程之间通信
读写可以使用read,write等函数
#include<unistd.h>
int pipe(int fd[2]);//成功返回0.失败返回-1
管道建立时,他会创建两个文件描述符
fd[0],为读打开,
fd[1],为写打开。
要关闭管道只需要将这两个文件描述符关闭即可。
示例代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
int main()
{
int fd[2];
int pid;
char *buf = "李思源是GAY";
char readbuf[128];
//判断
if(pipe(fd) == -1)
{
printf("管道创建失败\n");
}
//创建子进程
pid = fork();
if(pid<0)
{
printf("进程创建失败\n");
}
else if(pid>0)
{
printf("this is father process\n");
close(fd[0]);//关闭读
write(fd[1],buf,strlen(buf));
wait();
}
else
{
printf("this is child process\n");
close(fd[1]);//关闭写
read(fd[0],readbuf,100);
printf("菊苑703谁是GAY:%s\n",buf);
exit(0);
}
return 0;
}
二.有名管道(mkfifo)
有名管道依赖于文件系统,是一个特殊的文件,有名管道和普通文件一样,具有存放的路径,文件的权限,和属性。有名管道存放信息在内存中,两个进程结束后就会自动消失。
1.创建有名管道。
使用mkfifo函数创建有名管道,有两个参数。
第一个参数是要创建的管道文件名。
第二个参数是,赋予的权限。
extern int mkfifo(__const char *__path, __mode_t __mode);
使用mkfifo创建的文件,也可以使用读写一般文件的方式去读取。
2.示例代码
read.c
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include <errno.h>
#include <fcntl.h>
int main()
{
char buf[30] = {0};
if((mkfifo("./fife",0600) == -1) && errno != EEXIST)//创建管道
{
printf("mkfifo failuer\n");
perror("why");//打印错误
}
int fd = open("./fife",O_RDNOLY);//以只读的方式打开此文件
printf("open fife success\n");
int nread = read(fd,buf,30);//读取文件
printf("read is %d byte.fifo.context:%s\n ",nread,buf);
close(fd);//关闭
return 0;
}
write.c
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include <errno.h>
#include <fcntl.h>
int main()
{
char *buf = "siyuan is gay";//写入的数据
inr fd;
fd = open ("./fife",O_WRONLY);//以只写的方式打开此文件
printf("write open fife\n");
write(fd,buf,strlen(buf));//写入数据
close(fd);//关闭此文件
return 0;
}
三.消息队列
消息队列是消息的连接表,存放在内核中,一个消息队列由一个标识符来标识。
进程终止时消息队列里面的内容不会消失。
1.头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
2.创建/访问一个消息队列。
创建/访问消息队列需要使用msgget函数,函数原型如下。
int msgget(key_t key, int msgflag);
参数一.key 需要使用ftok()函数来获取键值。
参数二.msgflag 权限 IPC_CREAT 如果消息队列不存在则创建,存在则打开返回。
IPC_EXCL 如果消息队列不存在则创建,存在则出错。
返回值.失败返回-1,成功返回非负整数
3.添加消息
把消息添加到队列(发送)需要使用msgsnd()函数,函数原型如下。
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
msqid 由msgget函数返回的消息队列标识符。
msgp:发送的消息。
msgsz:消息的长度。
msgflg 默认为0;
成功返回0,失败返回-1.
4.接受消息
从消息队列获取消息(接受)需要使用msgrcv()函数,函数原型如下。
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
msqid 消息队列标识符
msgp 消息缓冲区
msgsz 接受消息的大小
msgfig 通常为0
4.ftok
通常是将文件的索引节点取出,然后在前面加上子序号就得到key_t的值。
key_t ftok(const char*pathname,int id);
6.删除队列
从内核中删除msgqid标识的消息队列使用msgctl()函数,原型如下。
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数一为消息队列标识符
参数二为指令,通常用IPC_RMID
6.示例代码
msgGet.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<string.h>
struct msgbuf
{
long mtye;
char mtext[100];
};
int main()
{
struct msgbuf getbuf;
int msgid;
//创建一个消息队列,
key_t key;
key = ftok(".",20)
printf("key::%x",key);
int msgid = msgget(key,IPC_CREAT|0777);
//msgget返回-1,创建失败。
if(msgid == -1)
{
printf("fail\n");
}
//接收数据
msgrcv(msgid,&getbuf,sizeof(getbuf.mtext),888,0);
printf("juyuan 703 de gay is:%s",getbuf.mtext);
msgctl(msgid,IPC_EMID,NULL);
return 0;
}
msgSend.c
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<stdlib.h>
#include<stdio.h>
struct msgbuf
{
long mtye;//消息类型>0
char mtext[100];//文本,随便写多少字节都可以
};
int main()
{
struct mtye sendbuf = {888,"siyua is GAY"};
int msgid;
key_t key;
key = ftok(".",20)
printf("key::%x",key);
msgid = msgget(key,IPV_CREAT|0777);
if(msgid == -1)
{
printf("fail");
}
msgsnd(msgid,&sendbuf,strlen(sendbuf.mtext),0);
msgctl(msgid,IPC_EMID,NULL);
return 0;
}
四.共享内存
共享内存就是允许两个不相关的进程访问同一块逻辑内存。
简单来说就类似于一个桌子,桌子上面写的内容我写的时候你就可以直接看到。
共享内存基本使用:
1.头文件
#inlcude<sys/ipc.h>
#include<sys/shm.h>
2.创建共享内存
要使用共享内存首先要使用shmget()函数来创建一块内存,shmget()函数的原型如下。
int shmget(key_t key,size_t size,int shmfig);
第一个参数 key_t key :通过使用ftok()函数来获取。
第二个参数 size_t size:创建内存的大小。
第三个参数 int shmfig:权限,比如IPC_CREAT 创建一块内存。
shmget的返回值为一个标识符。
3.挂载共享内存
函数原型如下。
void *shmat(int shmid, const void *shmaddr,int shmfig);
参数一 shmid:为shmget()函数返回的标识符。
参数二 shmaddr:为要关联的内存地址,一般传入0,系统会自动匹配一块内存。
4.卸载挂载内存。
当进程不需要共享内存时使用shmdt()函数,该函数不会删除共享内存,只是将该进程脱离,函数原型如下。
int shmdt(const void *shmaddr);
参数为shmget()函数的返回值。
5.释放共享内存
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
参数一为shmget()函数的返回值。
参数儿为执行的命令,IPC_RMID 为删除共性内存。
6.示例代码
shmw.c
#include<sys/ipc.h>
#include<sys/shm.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int shmid;
char * shmaddr;
key_t key;
key = ftok(".",1);
//创建共享内存,权限可读可写
shmid = shmget(key,1024*4,IPC_CREAT|0600);
//判断是否创建成功
if(shmid == -1)
{
printf("shmget fail\n");
}
//挂载共享内存
shmaddr = shmat(shmid,0,0);
printf("shmat ok");
//将内容拷进共享内存中
strcpy(shmaddr,"siyuan is gay");
sleep(5);
//卸载挂载点
shmdt(shmaddr);
return 0;
}
shmr.c
#include<sys/ipc.h>
#include<sys/shm.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int shmid;
char *shmaddr;
key_t key;
key = ftok(".",1)
shmid = shmget(key,1024*4,IPC_CREAT|0600);
if(shmid == -1)
{
printf("creat fail\n");
exit(-1);
}
shmaddr = shmat(shmid,0,0);
printf("shmget succese\n");
//直接打印共享内存的内容
printf("data = \s\n",shmaddr);
shmdt(shmid);
printf("quit");
shmctl(shmid,IPC_RMID,0);
return 0;
}
五.信号
信号是进程之间相互传递消息的一种方法,信号被称为软中断信号,也可以成为软中断。例如ctrl+c用来中断程序。
2.头文件
#include <signal.h>
3.函数原型
sighandler_t signal(int signum,sighangler_t handler);
参数一:为处理的信号 ,可以使用kill -l,在中断查看。
参数二:信号的操作,处理方式,可以取以下3种
SIG_IGN 忽略信号
SIG_DFL 恢复对信号的默认处理。
sighandler_t类型的函数指针 handler必须在使用signal前进行声明。
4.信号高级(sigaction)
为什么要有高级版的信号呢?
使用signal虽然完成了信号的收发,但是不能携带数据,这就出现了信号高级版,
信号高级版可以携带一些信息
sigaction函数原型
int sigaction(int signum,const struct sigaction *act, struct sigaction *oldact);
//参数一为操作的信号。
//参数二下面的结构体
//参数三为备份
struct sigaction {
void (*sa_handler)(int); //信号处理程序,不接受额外数据,SIG_IGN 为忽略,SIG_DFL 为默认动作
void (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,能够接受额外数据和sigqueue配合使用
sigset_t sa_mask;//阻塞关键字的信号集,可以再调用捕捉函数之前,把信号添加到信号阻塞字,信号捕捉函数返回之前恢复为原先的值。
int sa_flags;//影响信号的行为SA_SIGINFO表示能够接受数据
};
//注意!!!!sa_handler、sa_sigaction只能任选其一
5.示例代码
接受端。
#include<signal.h>
#include<stdio.h>
void handler(int signum,siginfo_t info*,context)
{
printf("get signum == %d\n",signum);
if(context != NULL)
{
printf("get data == %d\n",info->si_int);
printf("from == %d\n",info->si_pid);
printf("思源是GAY\n");
}
}
int main()
{
struct sigaction set;
set.sa_sigaction = handler;
set.sa_flags = SA_SIGINFO;
printf("pid == %d\n",getpid());//打印自己的pid号
sigaction(SIGINT,&set,NULL);
while(1);
return 0;
}
发送端
#include<signal.h>
#include<stdio.h>
int main(int argc,char **argv[])
{
int signum;
int pid;
union sigval value;
value.sival_int = 100;
signum = atoi(argv[1]);//将信号转换成整型。
pid = atoi(argv[2]);//将pid转换成整型。
sigqueue(pid,signum,value)
printf("done\n");
printf("思源是GAY\n");
printf("pid == %d\n",getpid());
return 0;
}
六.信号量
信号量其实就类似于钥匙,为了防止资源争夺混乱,可以对资源进行上锁,有钥匙的进程访问,没有钥匙则等待。
1.创建/访问信号量(semget)
它的作用是创建一个信号量或者打开一个信号量函数原型如下。
int semget(key_t key, int num_sems, int sem_flags);
参数一key 为ftok函数获取的键值。
参数二num_sems为信号数目,通常都是1.
参数三 为权限,可以使用IPC_CREAT 来创建信号量。
2.修改信号量(semop)
控制修改信号量里面的值
int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);
参数一为semget返回的标识符
参数二为一个结构体,如下。
struct sembuf{
short sem_num; // 除非使用一组信号量,否则它为0
short sem_op; // 信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,
// 一个是+1,即V(发送信号)操作。
short sem_flg; // 通常为SEM_UNDO,使操作系统跟踪信号,
// 并在进程没有释放该信号量而终止时,操作系统释放信号量
};
3.控制信号量(semctl)
控制信号量里面的信息,函数原型如下。
int semctl(int sem_id, int sem_num, int command, ...);
前两个参数与semget一样,第三个参数通常是,SETVAL,IPC_RMID,两个其中一个,使用SETVAL需要第四个参数,通常改变 val的值来设置信号量第一次使用时候的值。
union semun{
int val;
struct semid_ds *buf;
unsigned short *arry;
};
4.示例代码:信号量控制子进程先运行。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include<stdio.h>
struct sembuf{
short sem_num; // 除非使用一组信号量,否则它为0
short sem_op; // 信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,
// 一个是+1,即V(发送信号)操作。
short sem_flg; // 通常为SEM_UNDO,使操作系统跟踪信号,
// 并在进程没有释放该信号量而终止时,操作系统释放信号量
};
void P(int id)//拿出钥匙
{
struct sembuf value;
value.sem_num = 0;
value.sem_op = -1;
value.sem_flg = SEM_UNDO;
semop(id,&value,1);
printf("get key\n");
}
void V(int id)//放回钥匙
{
struct sembuf set;
set.sem_num = 0;
set.sem_op = 1;
set.sem_flg = SEM_UNDO;
semop(id,&set,1);
printf("put key\n");
}
int main()
{
int semid;
key_t key;
key =ftok(".",1);
semid = semget(key,1,IPC_CREAT|0666);//创建信号量
union semun initsem;
initsem.val = 0;//信号量第一次为0
semctl(semid,0,SETVAL,initsem);
int pid = fork()
if(pid > 0)
{
//父进程阻塞在这无法运行等待子进程将钥匙放进去。
P(semid);//父进程的到钥匙
printf("this is father\n");
V(semid);//放回钥匙
cenctl(semid,0,IPC_RMID);//销毁钥匙
}
else if(pid == 0)
{
printf("this is child\n")
V(semid);//子进程先放钥匙
}
else
{
printf("fork error\n");
}
return 0;
}
5.信号量与共享内存结合使用
信号量与共享内存写端
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include<stdio.h>
struct sembuf{
short sem_num; // 除非使用一组信号量,否则它为0
short sem_op; // 信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,
// 一个是+1,即V(发送信号)操作。
short sem_flg; // 通常为SEM_UNDO,使操作系统跟踪信号,
// 并在进程没有释放该信号量而终止时,操作系统释放信号量
};
void P(int id,int num)//拿出钥匙
{
struct sembuf value;
value.sem_num = num;
value.sem_op = -1;
value.sem_flg = SEM_UNDO;
semop(id,&value,1);
printf("get key\n");
}
void V(int id,int num)//放回钥匙
{
struct sembuf set;
set.sem_num = num;
set.sem_op = 1;
set.sem_flg = SEM_UNDO;
semop(id,&set,1);
printf("put key\n");
}
int main()
{
int semid;
int shmid;
char *shmaddr;
key_t key;
key_t key1;
key =ftok(".",1);//获取建值
key1 = ftok(".",2);
semid = semget(key,1,IPC_CREAT|0666);//创建信号量
shmid = shmget(key1,1024*4,IPC_CREAT|0666);//创建共享内存
union semun initsem;
initsem.val = 0;//信号量第一次为0
semctl(semid,0,SETVAL,initsem);
P(semid,0);
shmaddr = shmat(shmid,0,0);
strcpy(shmaddr,"si yuan is gay");
shmdt(shmaddr);