一.为什么需要进程通信
1)数据传输
一个进程需要把它的数据发送给另一个进程。
2)资源共享
多个进程之间共享同样的资源。
3)通知事件
一个进程向另外一个进程发送消息,通知它发生了某事件。
4)进程控制
控制运行、停止等。
二.IPC的由来
1)Unix进程通信
2)SystemV进程通信
3)POSIX(Portable Operating System Interface)进程通信
三.进程通信方式分类
1.管道通信(有名、无名管道)
含义:单向,先进先出的。
分类:无名(父子进程)、有名(任意进程)。
int pipe(int filedis[2]);
filedis[0]读管道;
filedis[1]写管道;
close() 关闭文件描述符。
2)//创建方式举例<以下所有示例linux测试ok>:
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int pipe_fd[2];
if(pipe(pipe_fd)<0)
{
printf("pipe create error\n");
return -1;
}
else
printf("pipe create success\n");
close(pipe_fd[0]);
close(pipe_fd[1]);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
3)//父子进程之间通信举例
注意:必须fork()前调用pipe(),否则子进程无法继承文件描述符。
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int pipe_fd[2];
pid_t pid;
char buf_r[100];
char* p_wbuf;
int r_num;
memset(buf_r,0,sizeof(buf_r));
/*创建管道*/
if(pipe(pipe_fd)<0)
{
printf("pipe create error\n");
return -1;
}
/*创建子进程*/
if((pid=fork())==0) //子进程 OR 父进程?
{
printf("\n");
close(pipe_fd[1]);
sleep(2); /*为什么要睡眠*/
if((r_num=read(pipe_fd[0],buf_r,100))>0)
{
printf( "%d numbers read from the pipe is %s\n",r_num,buf_r);
}
close(pipe_fd[0]);
exit(0);
}
else if(pid>0)
{
close(pipe_fd[0]);
if(write(pipe_fd[1],"Hello",5)!=-1)
printf("parent write1 Hello!\n");
if(write(pipe_fd[1]," Pipe",5)!=-1)
printf("parent write2 Pipe!\n");
close(pipe_fd[1]);
sleep(3);
waitpid(pid,NULL,0); /*等待子进程结束*/
exit(0);
}
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//3)有名管道
int mkfifo(const char* pathname, mode_t mode)
//读管道数据
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FIFO "/tmp/myfifo"
main(int argc,char** argv)
{
char buf_r[100];
int fd;
int nread;
/* 创建管道 */
if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))
printf("cannot create fifoserver\n");
printf("Preparing for reading bytes...\n");
memset(buf_r,0,sizeof(buf_r));
/* 打开管道 */
fd=open(FIFO,O_RDONLY|O_NONBLOCK,0);
if(fd==-1)
{
perror("open");
exit(1);
}
while(1)
{
memset(buf_r,0,sizeof(buf_r));
if((nread=read(fd,buf_r,100))==-1)
{
if(errno==EAGAIN)
printf("no data yet\n");
}
printf("read %s from FIFO\n",buf_r);
sleep(1);
}
pause(); /*暂停,等待信号*/
unlink(FIFO); //删除文件
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//写管道数据
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FIFO_SERVER "/tmp/myfifo"
main(int argc,char** argv)
{
int fd;
char w_buf[100];
int nwrite;
/*打开管道*/
fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);
if(argc==1)
{
printf("Please send something\n");
exit(-1);
}
strcpy(w_buf,argv[1]);
/* 向管道写入数据 */
if((nwrite=write(fd,w_buf,100))==-1)
{
if(errno==EAGAIN)
printf("The FIFO has not been read yet.Please try later\n");
}
else
printf("write %s to the FIFO\n",w_buf);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2.信号通信(signal)
//2.1信号介绍
1)产生信号:用户按键,除数为0的异常等。
2)kill函数将信号发送给进程。
3)kill命令将信号发送给进程。
格式如下:kill -s SIGQUIT 进程ID
ctrl +z == SIGSTOP
//2.2信号处理
1)忽略新信号,但 SIGKILL和SIGSTOP不能被忽略。
2)执行用户希望的动作。
3)执行系统默认动作。
2.3发送信号常见函数
1)kill
给自己或者其他进程。
2)raise
只能给自己发信号。
3)alarm
产生SIGALRM信号。
如果不捕获此信号,则默认动作是终止该进程。
4)Pause
调用进程挂起直至捕获到一个信号。
2.4信号的处理方式
1)signal
需要的头文件#include
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
void my_func(int sign_no)
{
if(sign_no==SIGINT)
printf("I have get SIGINT\n");
else if(sign_no==SIGQUIT)
printf("I have get SIGQUIT\n");
}
int main()
{
printf("Waiting for signal SIGINT or SIGQUIT \n ");
/*注册信号处理函数*/
signal(SIGINT, my_func);
signal(SIGQUIT, my_func);
pause();
exit(0);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@localhost pipe]# ./mysignal
Waiting for signal SIGINT or SIGQUIT
I have get SIGQUIT
3.共享内存
共享内存访问快,是被多个进程共享的一块物理内存。
一个进程向里写入数据,共享内存的其他进程都能收到数据。
3.2实现步骤
1)创建共享内存
int shmget(key_t key, …)
2)映射
int shmat(int shmid, char…)
3)解除映射
int shmdt(char* shmaddr)
3.3举例:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define PERM S_IRUSR|S_IWUSR
/* 共享内存 */
int main(int argc,char **argv)
{
int shmid;
char *p_addr,*c_addr;
if(argc!=2)
{
fprintf(stderr,"Usage:%s\n\a",argv[0]);
exit(1);
}
/* 创建共享内存 */
if((shmid=shmget(IPC_PRIVATE,1024,PERM))==-1)
{
fprintf(stderr,"Create Share Memory Error:%s\n\a",strerror(errno));
exit(1);
}
/* 创建子进程 */
if(fork()) // 父进程写
{
p_addr=shmat(shmid,0,0);
memset(p_addr,'\0',1024);
strncpy(p_addr,argv[1],1024);
wait(NULL); // 释放资源,不关心终止状态
exit(0);
}
else // 子进程读
{
sleep(1); // 暂停1秒
c_addr=shmat(shmid,0,0);
printf("Client get %s\n",c_addr);
exit(0);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
4.消息队列
4.1本质
链表,
4.2常用消息队列
1)POSIX消息队列
2)系统V消息队列
随内核持续的,只有内核重启或者删除,该队列才会删除。
4.3常用接口
键值:每个消息队列都有一个键值。
key_t ftok(char* pathname, char proj)
1)创建&打开
msgget(key_t key, int msgflag)
//IPC_CREATE
//IPC_EXEC
//IPC_NOWAIT 不阻塞
2)发送消息
int msgsend(int msqid, struct msgbuf* msgp, int msgsz, int msgflg)
//向消息队列发送一条消息
struct msgbuf
{
long mtype; //消息类型
char mtext[1]; //消息数据的首地址
};
3)接收消息
int msgrcv(int msqid, struct msgbuf* msgp, int msgsz, long msgtype, int msgflg)
举例:`这里写代struct msg_buf
{
int mtype;
char data[255];
};
int main()
{
key_t key;
int msgid;
int ret;
struct msg_buf msgbuf;
key=ftok("/tmp/2",'a');
printf("key =[%x]\n",key);
msgid=msgget(key,IPC_CREAT|0666); /*通过文件对应*/
if(msgid==-1)
{
printf("create error\n");
return -1;
}
//设置消息类型和数据(编号为pid)
msgbuf.mtype = getpid();
strcpy(msgbuf.data,"test haha");
ret=msgsnd(msgid,&msgbuf,sizeof(msgbuf.data),IPC_NOWAIT);
if(ret==-1)
{
printf("send message err\n");
return -1;
}
memset(&msgbuf,0,sizeof(msgbuf));
ret=msgrcv(msgid,&msgbuf,sizeof(msgbuf.data),getpid(),IPC_NOWAIT);
if(ret==-1)
{
printf("recv message err\n");
return -1;
}
printf("recv msg =[%s]\n",msgbuf.data);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
}
5.信号量
5.1主要用途
进程间的互斥和同步,保护临界资源。
5.2分类
1)二值信号量,最大值为1(最多允许一个访问)
2)计数信号量,信号灯的值可以取任意非负值。
5.3操作
1)打开和创建
int semget(key_t key, int nsems, int semflg)
2)操作
int semop(int semid, struct sembuf *sops, unsigned nsops)
6.套接字(socket)
小结:
以上知识点目前没有在Linux项目中使用的较少,但必须要有知识储备,以备不时之需。