1.进程间的通信IPC是指在不同进程间的传播和交换信息。
2.IPC的方式通常有管道(包括无名管道和命名管道),消息队列,信号量,共享存储,Socket,Streams等,其中Socket和Streams支持不同主机上的两个进程IPC。
3.管道:
通常指无名管道,特点是:1.他是半双工(数据只能在一个方向上流动)具有固定的读fd[0]写fd[1]端。2.只能用于亲缘关系的进程之间(如父子兄弟进程之间)。3.他是一种特殊文件,不 属于任何文件系统,并且只存在于内存中。
原型:int pipe(int pipefd[2]);
int main()
{
int fd[2]; //定义一个数组fd[0]为只读;fd[1]为只写;
int pid; //fork返回值;
char buf[128];
if(pipe(fd) == -1){ //判断创建无名管道是否成功;
printf("creat pipe falied\n");
}
pid=fork();
if(pid<0){ // 判断子进程创建;
printf("creat child falied\n");
}else if(pid>0){ //进入父进程;
sleep(2);
printf("this is father\n");
close(fd[0]);//关闭只读口准备写
int nwrite= write(fd[1],"qwedrft",strlen("qwedrft"));
wait();// 防止成为僵尸进程;
}else{
printf("this is child\n");
close(fd[1]); //关闭只写;
read(fd[0],buf,128);//从fd[0]将内容读到buf里;
printf("read from father:%s\n",buf);
exit(0);
}
return 0;
}
4.FIFO,也叫命名管道,是一种文件类型,特点:(1)可在无关进程之间交换数据,与无名管道不同,(2)FIFO有路径名与之相关联以一种特殊设备文件形式存在于文件系统中。
fifo具有管道特性,数据传输完成后管道中的数据全部清空
原型:int mkfifo(const char *pathname, mode_t mode); pathname为带路径的文件名;mode和open中的mode一样。若成功返回0;出错返回-1.
#include <errno.h>
#include <fcntl.h>//判断是否有原文件的头文件。
接收端:
int main()
{
char buf[128];// 定义一个字符数组接收信息;
int fd;
if((mkfifo("./file",0600) == -1) && errno != EEXIST){ //判断是否成功若file文件开始就存在也不会报错。
printf("mkfifo failuer\n");
perror("why:\n");
}else{
fd=open("./file",O_RDONLY);//打开文件且权限为只读;若不强调则默认阻塞,若强调O_NONBLOCK则不阻塞,分情况用。
printf("open success\n");
}
while(1){
int nread=read(fd,buf,128);
printf("nread=%d;from file:%s\n",nread,buf);
}
close(fd);
return 0;
}
发送端:
int main()
{
int fd;
char *str="qwdegtj";
int cnt=0;
fd=open("./file",O_WRONLY); //打开mkfifo创建的文件,并设置只写权限;
printf("open success\n");
while(1){
write(fd,str,strlen(str));//将str中的内容写入fd中
sleep(1);
cnt++;//计数标记
if(cnt == 5){
break;
}
}
close(fd);
return 0;
}
5.消息队列:是消息的链接表,存放在内核中,一个消息队列有一个标识符(即队列ID)来标识,可以进行非亲属进程之间的消息传递。
特点:(1)消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
(2)消息队列独立于发送与接受进程,进程结束时,消息队列及其内容不会被删除。
(3)消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
(4)要使两个不同进程通过消息队列传递信息,则fotk的参数必须一样,得到的key值才能一样,且信息类型也要相同。
typedef struct{ //消息队列的消息格式:
long mtype; //消息类型,必须是长整形,必须放在结构体的第一个,可以自己定值;
char mtext[128];//消息内容,可以有多个成员,可以是多种类型的数据;
}msg;
int main()
{
msg snd_buf={988,"thank you for reach"};//定义一个结构体变量,并输入内容,消息类型为888,内容为一串字符
msg rcv_buf; //接收端消息内容结构体变量;
int msg_id; //消息队列标识符;由msgget函数返回;
//key_t ftok(const char *pathname, int proj_id);
//pathname为路径名,"."代表当前路径;proj_id为项目ID只有低8位有效,可自己定值;
key_t key;
key=ftok(".",'5'); //若成功返回key值,失败返回-1;key值相当于open里的fd
printf("the key=%x\n",key);
// int msgget(key_t key, int msgflg); //msgflg为标识函数行为,key为IPC键值;
msg_id=msgget(key,IPC_CREAT|0777);//若没有队列则创建权限为0777的队列;(IPC_EXCL:若原有则报错返回失败-1)返回值为队列ID
if(msg_id == -1){
printf("msg_id get error!!");
}
// int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
发送消息:msg_id:队列标识符;*msgp:待发消息结构体的地址;msgsz:消息正文字节数;msgflg:函数控制属性(若为0则阻塞;若IPC_NOWAIT则不阻塞)
msgsnd(msg_id,&snd_buf,strlen(snd_buf.mtext),0);
printf("snd oxer!!");
//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
接收消息:msgid:队列ID;*msgp:存放消息结构体地址;msgsz:消息正文字节数;msgtyp接收的消息的类型;msgflg(0则阻塞;IPC_NOWAIT不阻塞)
msgrcv(msg_id,&rcv_buf,sizeof(rcv_buf.mtext),988,0);
printf("from snd:%s\n",rcv_buf.mtext);
//int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msqid为要操作的队列id;cmd有三个指令如下:
//buf用来存放或更改队列的各种信息和属性NULL就是不需要存放
msgctl(msg_id,IPC_RMID,NULL);
return 0;
}
6.共享内存:
特点:(1)允许两个不相关的进程访问同一个内存;
(2)共享内存是两个正在运行的进程之间传递数据的最有效的方式;
(3)不同进程之间的共享内存通常安排为同一段物理内存;
(4)共享内存不提供任何互斥和同步机制,一般用信号量对临界资源进行保护;
(5)接口简单;
创建端:
int main()
{
key_t key;
//int shmget(key_t key, size_t size, int shmflg); //size只能为1024的整数倍;shmflg:权限标志(IPC_CREAT:创建内存并设置权限)
int shm_id; char *shmaddr; //联接共享内存的指针。
key=ftok(".",'1'); //获得key值
printf("key:%x\n",key);
shm_id=shmget(key,1024*4,IPC_CREAT|0600);//创建4M的共享内存;得到shm_id的值;
printf("ID:%d\n",shm_id);
if(shm_id == -1){ //判断是否创建成功;
printf("shm get error!!\n");
exit(-1);
}
//void *shmat(int shmid, const void *shmaddr, int shmflg);
//shmid:由shmget返回值得到;shmaddr:用来指定共享内存映射到进程中的位置,若要成功flg必须为SHM_RDN标志。
shmaddr=shmat(shm_id,0,0);//第一个0表示让系统自动分配地址;减轻程序对硬件的依赖,第二个0表示内存可读可写。
strcpy(shmaddr,"qedweferh");//将内容写入联接地址;
sleep(5);//等5秒,等待接受端;
//int shmdt(const void *shmaddr);//解除映射;
shmdt(shmaddr);
//int shmctl(int shmid, int cmd, struct shmid_ds *buf); //cmd控制指令(IPC_RMID:删除这块共享内存);buf接受内存结构体中的信息,若删除就写0;
shmctl(shm_id,IPC_RMID,0);
printf("quit\n");
return 0;
}
接收端:
int main()
{
key_t key;
//int shmget(key_t key, size_t size, int shmflg);
int shm_id;
char *addr;
key=ftok(".",'1');//保证key值相等
以上都相同;
shm_id=shmget(key,1024*4,0);//直接引用内存不用在创建
printf("ID=%d\n",shm_id);
if(shm_id == -1){
printf("shm get error!!\n");
exit(-1);
}
//void *shmat(int shmid, const void *shmaddr, int shmflg);//映射到共享内存
addr=shmat(shm_id,0,0);//接收信息
printf("shmat ok!\n");
printf("from write:%s\n",addr);
//int shmdt(const void *shmaddr);
shmdt(addr);//解除映射
//int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmctl(shm_id,IPC_RMID,0);//销毁内存
printf("quit\n");
return 0; //共享内存可以互相发送和接受用信号量控制
}
7,信号
同一进程的信号发送:
//typedef void (*sighandler_t)(int); //信号执行函数
//sighandler_t signal(int signum, sighandler_t handler);
//signum:信号代号;handler:执行函数
void handler(int signum)
{
printf("get signum:%d\n",signum);
printf("never quit\n");
switch(signum){
case 2:
printf("SIGINT\n");
break;
case 9:
printf("SIGLILL\n");
break;
case 10:
printf("SIGUSR1\n");
break;
}
}
int main()
{
signal(SIGINT,handler);
signal(SIGKILL,handler);
signal(SIGUSR1,handler);
while(1);
return 0;
}
//int kill(pid_t pid, int sig);//调用kill函数和system函数在不同进程传递信号
信号处理的三种方式:1.接收信号调用处理函数;2.忽略; 3.系统默认处理
int main(int argc,char **argv)
{
int pid; //输入main函数的变量
int signum;
char cmd[128];
pid=atoi(argv[2]); //将字符型转化为整形
signum=atoi(argv[1]);
printf("pid:%d;signum:%d\n",pid,signum);
sprintf(cmd,"kill -%d %d",signum,pid);//将命令赋于cmd;(kill -9 40333)
system(cmd);//使用system函数
// kill(pid,signum);//调用kill函数;pid为进程号,signum为信号的代号
printf("kill send ok!!");
return 0;
}
8.通过信号携带信息,信号的高级使用,sigaction函数捕获信号:
/*struct sigaction{
void (*sa_sigaction)(int,siginfo_t*,void*);//信号处理函数
int sa_flags;//指定了对信号的某些特殊处理;
};*/
void handler(int signum,siginfo_t *info,void *context)//当需要处理信息时调用这个函数,signum为要捕获的信号;*info为一个结构体包里面包含信号携带的各种信息;context为NULL是说明信息没有传成功。
{
printf("get signum:%d\n",signum);
if(context != NULL){
printf("get data:%d\n",info->si_value.sival_int);//info中还包含一个联合体value
printf("det data:%d\n",info->si_int);//和上面那段代码作用一样
// printf("get data:%s\n",info->si_value.sival_ptr);//这是一块内存
printf("thr first pid:%d\n",info->si_pid);//打印发送进程的pid
}
}
int main()
{
//int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
signum;要捕获的信号;*act:对信号处理的结构体;*oldact:对原来处理信号信息的备份,若不用备份写NULL;
struct sigaction act;//定义一个信号处理的结构体函数;
printf("the pid:%d\n",getpid());//打印本进程的pid
act.sa_sigaction=handler;//定义信号信息处理函数;
act.sa_flags=SA_SIGINFO;//要想处理信息就必须是这个命令
sigaction(SIGUSR1,&act,NULL);//act中最重要的三个变量flags决定需不需要处理信息若需要用sa.siigaction函数若不需要用handler函数相当于signal()。
while(1);
return 0;
发送端:
int main(int argc,char **argv)
{
int signum;
int pid;
pid=atoi(argv[2]);
signum=atoi(argv[1]);
printf("pid:%d\n",pid);
union sigval value;//定义信息处理函数中携带信息的联合体value;
value.sival_int=100;//输入数据;
// value.sival_ptr={"meiyoumingtianle"};//不对
// int sigqueue(pid_t pid, int sig, const union sigval value);
sigqueue(pid,signum,value);//传递信号;pid接收进程的pid,signum传递的信号;value:信息联合体
printf("pid:%d\n",getpid());
printf("done\n");
return 0;
}
9.信号量
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) */
};
void p_get_key(int sem_id)//p操作得到钥匙
{
//int semop(int semid, struct sembuf *sops, unsigned nsops);
struct sembuf sops;
sops.sem_num=0;
sops.sem_op=-1; //1表示对信号量进行p操作,调用一次信号量数减一;拿钥匙的过程;
sops.sem_flg=SEM_UNDO;
semop(sem_id,&sops,1);
printf("get key ok\n");
}
void v_put_key(int sem_id)//v操作放回钥匙
{
//int semop(int semid, struct sembuf *sops, unsigned nsops);//访问信号量集的函数;semid为semget的返回值;*sops为存放对信号量操作的的结构体。nsops有几个信号量结构体就是几。
struct sembuf sops;
sops.sem_num=0;//信号量的下标,0表示第一个信号量;
sops.sem_op=1; //1表示对信号量进行v操作,调用一次信号量数加一;放钥匙的过程;
sops.sem_flg=SEM_UNDO;//只有两个值IPC_NOWAIT不阻塞无意义;SEM_UNDO阻塞
semop(sem_id,&sops,1);
printf("put key ok\n");
}
int main(int argc,char **argv)
{
int sem_id;
key_t key;
int pid;
key=ftok(".",1);
//int semget(key_t key, int nsems, int semflg);// 创建信号量;nsems为信号量集个数;flg和open函数一样
sem_id=semget(key,1,IPC_CREAT|0666);
union semun tmp;//这个联合体中只用到第一个参数即信号量的值,为0时挂起大于0时可执行,一般只有0和1两种。
tmp.val=0;//先设置其为挂起状态
//int semctl(int semid, int semnum, int cmd, ...);
semctl(sem_id,0,SETVAL,tmp);//控制信号量sen_id为semget的返回值为信号量的id;semnum为要创建的信号量集的个数,一般为1;cmd将要采取的动作;若为SETVAL则引入第四个变量联合体semun。
pid=fork();
if(pid>0){
p_get_key(sem_id);//拿钥匙p操作,因为信号量设置为0所以会等待
printf("this is father\n");
v_put_key(sem_id);//放钥匙v操作
semctl(sem_id,0,IPC_RMID);//销毁信号量集
}else if(pid == 0){
printf("this is child\n");
v_put_key(sem_id);//先运行子进程后v操作放回钥匙父进程才能正常拿到钥匙运行
}else{
printf("error\n");
}
return 0;
}