进程间通信:IPC(Inter_Process Communication)
进程通信:在用户空间无法实现进程通信,需通过Linux内核空间的对象来完成进程通信。
线程间通信:可以直接在用户空间实现,可以通过全局变量通信。
通信方式:
1、管道通信:
无名:在文件系统中无文件名
函数形式:int pipe(int fd[2])
参数:两个文件描述符:fd[0]、fd[1],管道读端fd[0]、写端fd[1]
返回值:成功0,失败-1
注意:1、读一次,管道自动清空,二次读取则会阻塞
2、无名管道只能实现父子进程或有亲缘关系的进程之间的通信
代码1:无名管道的创建与简单使用
#include<stdio.h>
#include<unistd.h>
int main()
{
int fd[2];
int ret;
char write_buf[]="Hello I love you, mei";
char read_buf[128]={0};
ret = pipe(fd);
if(ret<0)
{
printf("fail\n");
return -1;
}
printf("Creat pipe success fd[0] = %d fd[1] =%d\n",fd[0],fd[1]);
ret = write(fd[1],write_buf,sizeof(write_buf));
//read
read(fd[0],read_buf,ret);
printf("read_buf=%s\n",read_buf);
return 0;
}
结果:
binge@ubuntu14:~/user/my_share/process$ gcc -o pipe1 pipe_1.c
binge@ubuntu14:~/user/my_share/process$ ./pipe1
Creat pipe success fd[0] = 3 fd[1] =4
read_buf=Hello I love you, mei
代码2:无名管道实现父子进程间通信
#include<stdio.h>
#include<unistd.h>
int main()
{
pid_t pid;
int fd[2];
int ret;
int process_inter =0;
ret = pipe(fd);
if(ret<0)
{
printf("Creat pipe fail\n");
return -1;
}
printf("Creat pipe success\n");
pid = fork();
if(pid==0) //child process
{
int i=0;
read(fd[0],&process_inter,1);
while(process_inter ==0);
for(;i<6;i++)
{
printf("This is children process i =%d\n",i);
sleep(1);
}
}
if(pid>0)
{
int i =0;
for(;i<6;i++)
{
printf("this is father process! i=%d\n",i);
sleep(1);
}
process_inter = 1;
write(fd[1],&process_inter,1);
}
while(1);
return 0;
}
运行结果:
binge@ubuntu14:~/user/my_share/process$ gcc -o fork fork.c
binge@ubuntu14:~/user/my_share/process$ ./fork
Creat pipe success
this is father process! i=0
this is father process! i=1
this is father process! i=2
this is father process! i=3
this is father process! i=4
this is father process! i=5
This is children process i =0
This is children process i =1
This is children process i =2
This is children process i =3
This is children process i =4
This is children process i =5
^C
有名管道: 文件系统中存在一个文件节点
作用:弥补无名管道只能在有亲缘关系的进程间实现通信的不足。
文件类型:管道文件 标识:p
文件类型 | 文件标识 | 创建方法 | 是否占磁盘块空间 | |
普通文件 | _ | open | 是 | |
目录文件 | d | mkdir | 是 | |
链接文件(软链接) | l | ln -s | 是 | |
管道文件(有名) | p | mkfifo | 否 | |
字符设备 | c | 否 | ||
块设备 | b | 否 | ||
套接字 | s | 否 |
函数形式:int mkfifo(const char *filename,mode_t mode);
返回值:成功:0,失败:-1
注:mkfifo并没有在内核中创建管道。只有当使用open函数打开节点时才会在内核中生成管道
例子:通过管道实现无亲缘关系进程间通信。
//first.c
#include<stdio.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int i;
int fd;
int process_inter = 0;
fd = open("./fifo",O_RDONLY);
if(fd<0)
{
printf("fail to open fifo\n");
return -1;
}
printf("open fifo\n");
read(fd,&process_inter,1);
while(process_inter =='0');
for(i = 0 ;i<5 ;i++ )
{
printf("this is the No.1 process i = %d\n",i);
sleep(1);
}
while(1);
return 0;
}
//second.c
#include<stdio.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int i;
int fd;
int process_inter = 0;
fd = open("./fifo",O_WRONLY);
if(fd<0)
{
printf("fail to open fifo\n");
return -1;
}
printf("success to open fifo\n");
for(i = 0 ;i<5 ;i++ )
{
printf("this is the No.2 process i = %d\n",i);
sleep(1);
}
process_inter = 1;
sleep(5);
write(fd,&process_inter,1);
while(1);
return 0;
}
运行结果:
binge@ubuntu14:~/user/my_share/process$ gcc -o first first.c
binge@ubuntu14:~/user/my_share/process$ ./first
open fifo
this is the No.1 process i = 0
this is the No.1 process i = 1
this is the No.1 process i = 2
this is the No.1 process i = 3
this is the No.1 process i = 4
binge@ubuntu14:~/user/my_share/process$ gcc -o second second.c
binge@ubuntu14:~/user/my_share/process$ ./second
success to open fifo
this is the No.2 process i = 0
this is the No.2 process i = 1
this is the No.2 process i = 2
this is the No.2 process i = 3
this is the No.2 process i = 4
2、信号通信:信号在内核中已经存在
信号通信框架:
- 信号发送:kill raise(发信号给自己) alarm
- 信号的接收:pause() sleep while(1)
- 信号的处理:signal
kill:
进程A欲与B通信,则A告诉内核想要给B发什么信号,内核将此信号发给B。反之则反。
两个参数:发什么信号,发给谁
#include <sys/types.h>
#include <signal.h>
#include<stdio.h>
#include<stdlib.h>
int main(int argc,char *argv[])
{
int sig;
int pid;
if(argc < 3)
{
printf("please input param\n");
return -1;
}
sig = atoi(argv[1]);//convert from string to int
pid = atoi(argv[2]);
printf("sig = %d,pid =%d\n",sig,pid);
kill(pid,sig);
return 0;
}
raise: int raise(int sig);
#include<stdio.h>
#include<signal.h>
int main(int argc,char *argv[])
{
printf("raise before\n");
raise(9);
printf("raise after\n");
return 0;
}
运行结果:
binge@binge-HP-Compaq:~/my_share/process$ gcc -o raise raise.c
binge@binge-HP-Compaq:~/my_share/process$ ./raise
raise before
已杀死
include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main(int argc,char *argv[])
{
pid_t pid;
pid = fork();
if(pid>0)
{
//father process
sleep(8);
if(waitpid(pid,NULL,WNOHANG)==0)
{
kill(pid,9);
}
wait(NULL);
while(1);
}
if(pid == 0)
{
//children process
printf("raise before\n");
raise(SIGTSTP);
printf("raise after\n");
return 0;
}
#include <unistd.h>
pid_t fork(void);
return:On success, the PID of the child process is returned in the parent, and
0 is returned in the child. On failure, -1 is returned in the parent,
no child process is created, and errno is set appropriately.
返回值:创建成功,则在父进程中返回值是子进程的PID,在子进程中返回值是0。
创建失败,父进程返回值是-1,不创建子进程,而出错信息被写入errno。
alarm:当定时时间到了,才发信号
函数原型:unsigned int alarm(unsigned int seconds)
返回值:成功则若之前已经设过闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。出错返回1。
信号名:SIGALARM
3、IPC:又分为共享内存、消息队列和信号灯 三种方式
共享内存:
//Client.c
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>
#include<unistd.h>
struct mybuf
{
int pid;
char buf[124];
};
void myfun(int signum)
{
return;
}
int main()
{
int shmid,key;
int pid;
struct mybuf *P;
key = ftok("a.tt",'a');
shmid = shmget(key,128,IPC_CREAT|0777);//创建共享内存
if(shmid < 0)
{
printf("Creat share memory fail!\n");
return -1;
}
printf("Creat share memory success!shmid=%d\n",shmid);
signal(SIGUSR2,myfun);
P = (struct mybuf *)shmat(shmid,NULL,0);//启动对共享内存的访问
if(P==NULL)
{
printf("creat shadow memory error!\n");
return -2;
}
pid = P->pid;//获取server的进程号
if(pid==0)
{
printf("fail get server pid\n");
return -1;
}
printf("success get server pid=%d\n",pid);
P->pid =getpid();//获取client的进程号并存入共享内存P
kill(pid,SIGUSR1);
while(1)
{
pause();
printf("client process recev:");
fputs(P->buf,stdout);
kill(pid,SIGUSR1);
}
shmctl(shmid,IPC_RMID,NULL);//删除共享内存
system("ipcs -m");
return 0;
}
//server.c
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>
#include<unistd.h>
struct mybuf
{
int pid;
char buf[124];
};
void myfun(int signum)
{
return;
}
int main()
{
int shmid,key,pid;
struct mybuf *P;
key = ftok("a.tt",'a');
shmid = shmget(key,128,IPC_CREAT|0777);
if(shmid < 0)
{
printf("Creat share memory fail!\n");
return -1;
}
printf("Creat share memory success!shmid=%d\n",shmid);
signal(SIGUSR1,myfun);
P = (struct mybuf *)shmat(shmid,NULL,0);
if(P==NULL)
{
printf("creat shadow memory error!\n");
return -2;
}
//get client pid
P->pid = getpid();
pause();
pid = P->pid;
printf("success get client pid = %d\n",pid);
while(1)
{
printf("server process send:");
fgets(P->buf,128,stdin);
kill(pid,SIGUSR2);
pause();
}
shmctl(shmid,IPC_RMID,NULL);
system("ipcs -m");
return 0;
}
消息队列:
创建消息队列:msgget();
写消息到消息队列:msgsnd();
从消息队列读取消息:msgrcv();
程序功能:实现无亲缘关系进程间通信(单向通信)
//read.c
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<stdlib.h>
#include<string.h>
struct msgbuf
{
long type;
char msg[124];
char ID[4];
};
int main( )
{
int msqid,key;
struct msgbuf rcvbuf;
key = ftok("./a.c",'1');
if(key<0)
{
printf("Creat key fail\n");
return -1;
}
msqid = msgget(key,IPC_CREAT|0666);
if(msqid<0)
{
printf("Creat message queue:%d fail\n",msqid);
return -1;
}
printf("Creat message queue:%d success\n",msqid);
system("ipcs -q");
while(1)
{
memset(rcvbuf.msg,0,124);
//read msg from msg queue
msgrcv(msqid,(void *)&rcvbuf,124,200,0);
printf("read process:%s",rcvbuf.msg);
}
msgctl(msqid,IPC_RMID,NULL);
system("ipcs -q");
return 0;
}
//write.c
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<stdlib.h>
#include<string.h>
struct msgbuf
{
long type;
char msg[124];
char ID[4];
};
int main( )
{
int msqid,readret,key;
struct msgbuf sendbuf;
key = ftok("./a.c",'1');
if(key<0)
{
printf("Creat key fail\n");
return -1;
}
msqid = msgget(key,IPC_CREAT|0666);
if(msqid<0)
{
printf("Creat message queue:%d fail\n",msqid);
return -1;
}
printf("Creat message queue:%d success\n",msqid);
system("ipcs -q");
sendbuf.type = 200;
while(1)
{
memset(sendbuf.msg,0,124);
printf("write process:");
fgets(sendbuf.msg,124,stdin);
//write msg to msg queue
msgsnd(msqid,(void *)&sendbuf,strlen(sendbuf.msg),0);
}
msgctl(msqid,IPC_RMID,NULL);
system("ipcs -q");
return 0;
}
信号灯:
创建信号灯:semget(key_t key, int nsem,int semflg)
返回值:成功,信号灯集ID;错误,-1
删除信号灯:int semctl(int semid , int semnum, int cmd , ...union semnu arg);
返回值:成功,0;失败,-1;
信号量:
定义信号量sem_t sem;
初始化:sem_init;
4、Socket通信(一个网络中两个进程间的通信):两个Linux内核