前段时间学习了进程的基本操作,今天来学习一下Linux中进程间的通信。
一。进程间通信也叫IPC:是为了实现不同进程之间的数据交互。
1.进程间的通信有管道、消息队列、共享内存以及信号量。
二。管道:
1.管道是Unix中最古老的进程间通信的形式。总结地说:管道就是从一个进程连接到另一个进程的一个数据流。
2.普通常用的管道:如下操作:
3.匿名管道:用pipe实现创建一个无名管道。
1.
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4
5 int main(void)
6 {
7 int fds[2];
8 char buf[100]={};
9 if(pipe(fds)==-1)perror("pipe"),exit(1);
10 if(fork()==0){
11 close(fds[0]);
12 sleep(2);
13 write(fds[1],"abc\n",4);
14 close(fds[1]);
15 exit(0);
16 }
17 else{
18 close(fds[1]);
19 printf("before read\n");
20 read(fds[0],buf,100);
21 printf("after read\n");
22 close(fds[1]);
23 printf(":[%s]\n",buf);
24 exit(0);
25
26 }
27 }
它的结果为:
[ymk@localhost d3]$ ./1
before read
after read
:[abc
]
2.
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4
5 int main(void)
6 {
7 int fds[2];
8 pipe(fds);
9 if(fork()>0){
10 close(fds[1]);
11 close(0);
12 dup(fds[0]);
13 close(fds[0]);
14 execlp("wc","wc","-l",NULL);
15 }
16 else{
17 close(fds[0]);
18 close(1);
19 dup(fds[1]);
20 close(fds[1]);
21 execlp("ls","ls","-l",NULL);
22
23 }
24 }
它的结果是:
[ymk@localhost d3]$ ./2
17
4.管道的特点:
(1)只能用于具有共同祖先的进程之间进行通信。
(2)管道提供流式服务。
(3)进程退出,管道释放,所以管道的生命周期随进程。
(4)内核会对管道操作进行同步与互斥。
(5)管道是半双工的,数据只能向一个方向流动,需要双方通信时,需要建立起两个管道。
5.命名管道:mkfifo。
应用:
#include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<fcntl.h>
5 int main(void)
6 {
7 int fd=open("my.p",O_RDONLY);
8 char buf[100]={};
9 if(fd==-1)perror("open"),exit(1);
10 printf("before read\n");
11 read(fd,buf,100);
12 printf("buf=[%s]\n",buf);
13 }
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<fcntl.h>
5 int main(void)
6 {
7 int fd=open("my.p",O_WRONLY);
8 char buf[100]={};
9 if(fd==-1)perror("open"),exit(1);
10 write(fd,"abc",3);
11 close(fd);
12 }
运行时,如果先执行读,则没反应,它会一直等待写的执行。
三。消息队列。
1.它提供了一个从一个进程向另一个进程发送一块数据的方法。他所用到的头文件是sys/ipc.h 和 sys/msg.h.
2.首先创建队列:查看消息队列:ipcs -q
int msgget(key_t key, //文件名。
int flag, //创建 IPC_CREAT | 0644. 打开:0.
返回值:是一个id,相当于一个文件描述符。
创建消息队列代码:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<sys/ipc.h>
4 #include<sys/msg.h>
5 #include<unistd.h>
6
7 int main(void)
8 {
9 int id=msgget(1234,IPC_CREAT|0644);
10 if(id==-1)perror("msgget"),exit(1);
11 printf("Create ok\n");
12
13 }
执行结果:
[ymk@localhost d5]$ gcc 1.c -o 1
[ymk@localhost d5]$ ./1
Create ok
[ymk@localhost d5]$ ipcs -a
------ Message Queues --------
key msqid owner perms used-bytes messages
0x000004d2 163840 ymk 644 0 0
3.放入队列:
int msgsnd(int id,
const void *msgp,//要发送数据的起始地址。
int len, //要发送数据的大小,不包括数据的类型。
int flag); //0.
struct msgbuf{
long mtype,//指的是类型,必须大于0.
char mtext[100];};
1 #include<stdio.h>
2 #include<sys/ipc.h>
3 #include<stdlib.h>
4 #include<sys/msg.h>
5 #include<string.h>
6
7 struct msgbuf{
8 long mtype;
9 char mtext[100];
10 };
11 int main(void)
12 {
13 int id=msgget(1234,0);
14 if(id==-1)perror("msgget"),exit(1);
15 struct msgbuf mb;
16 memset(&mb,0x00,sizeof(mb));
17 printf("type:");
18 scanf("%ld",&mb.mtype);
19 printf("context:");
20 scanf("%s",mb.mtext);
21 msgsnd(id,&mb,strlen(mb.mtext),0);
22 }
[ymk@localhost d3]$ vim msgsnd.c
[ymk@localhost d3]$ ./s
type:1
context:qwe
[ymk@localhost d3]$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x000004d2 163840 ymk 644 3 1
4.读取队列:
msgrcv(int id,
void *msgp, //获得的消息放在哪里
int len, //装消息的地方的大小,不包括类型。
int type, //类型号。
int flag);//0.
1 #include<stdio.h>
2 #include<sys/ipc.h>
3 #include<stdlib.h>
4 #include<sys/msg.h>
5 #include<string.h>
6
7 struct msgbuf{
8 long mtype;
9 char mtext[100];
10 };
11 int main(void)
12 {
13 int id=msgget(1234,0);
14 if(id==-1)perror("msgget"),exit(1);
15 long type;
16 struct msgbuf mb;
17 memset(&mb,0x00,sizeof(mb));
18 printf("type:");
19 scanf("%ld",&type);
20 if(msgrcv(id,&mb,100,type,0)==-1)
21 perror("msgrcv"),exit(1);
22 printf(":::::[%s]\n",mb.mtext);
23 }
[ymk@localhost d3]$ ./rcv
type:1
:::::[qwe]
5.删除队列:
(1)ipcrm -Q
(2)int msgctl(int id,int cmd,NULL);IPC_RMID:删除命令,第二个参数。
四。共享内存
1.它是最快的IPC。
2.创建:
shmget(key_t key,
int size,//指定共享内存的大小。
int flag);//创建用IPC_CREAT|0600 打开用0.
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<sys/ipc.h>
5 #include<sys/sem.h>
6 #include<string.h>
7
8 int main(void)
9 {
10 int id=semget(1234,1,IPC_CREAT|0600);
11 if(id==-1)perror("semget"),exit(1);
12
13 printf("semget ok\n");
14 }
结果:
[ymk@localhost d4]$ ./s
create ok
[ymk@localhost d4]$ ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 262144 ymk 600 524288 2 dest
0x00000000 1015809 ymk 600 4194304 2 dest
0x61033c8c 1081346 ymk 600 16 0
0x00000000 1048579 ymk 600 524288 2 dest
3.
//卸载,只断掉关系,不释放共享内存空间。
int shmdt(const void *shmaddr);
//将共享内存挂载到自己的虚拟地址空间
void *shmat(int shmid,
const void *shmaddr,//放到虚拟地址空间哪里 一般填NULL。
int shmflag);// 0.
返回值:挂载到的虚拟地址空间的起始地址。
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<sys/ipc.h>
5 #include<sys/shm.h>
6 #include<string.h>
7 struct stu{
8 int id;
9 char name[10];
10 };
11 int main(void)
12 {
13 key_t key=ftok(".",'a');
14 int id=shmget(key,0,0);
15 if(id==-1)perror("shmget"),exit(1);
16 struct stu *p=(struct stu*)shmat(id,NULL,0);
17 printf("id=%d,name=%s\n",p->id,p->name);
18 shmdt(p);
19 shmctl(id,IPC_RMID,0);
20 }
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<sys/ipc.h>
5 #include<sys/shm.h>
6 #include<string.h>
7 struct stu{
8 int id;
9 char name[10];
10 };
11 int main(void)
12 {
13 key_t key=ftok(".",'a');
14 int id=shmget(key,0,0);
15 if(id==-1)perror("shmget"),exit(1);
16 struct stu *p=(struct stu*)shmat(id,NULL,0);
17 p->id=10;
18 strcpy(p->name,"lili");
19 sleep(20);
20 shmdt(p);
21 }
它的结果是:当没有执行写时,读出来的是错的。而且会直接删掉共享内存。只有当执行写时,才能读出正确结果。
4.删除共享内存:
(1)ipcrm -M key
(2)shmctl(id,IPC_RMID,NULL);
五。信号量:
1.它本质上就是一个计数器。
2.创建信号量:
int semget(key_t key,
int nums, //信号量集中信号量的个数。,一般是1.
int flag); //创建IPC_CREAT|0600 打开:0.
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<sys/ipc.h>
5 #include<sys/shm.h>
6
7 struct stu{
8 int id;
9 char name[10];
10 };
11 int main(void)
12 {
13 key_t key=ftok(".",'a');
14 int id=shmget(key,sizeof(struct stu),IPC_CREAT|0600);
15 if(id==-1)perror("shmget"),exit(1);
16 printf("create ok\n");
17 }
结果:
[ymk@localhost d4]$ vim shm.c
[ymk@localhost d4]$ ./m
semget ok
[ymk@localhost d4]$ ipcs -s
------ Semaphore Arrays --------
key semid owner perms nsems
0x000004d2 32768 ymk 600 1
3.设置初值:
union semun{
int val;
};
semctl(int id, //semget返回的id.
int num, //集合中第几个信号量。从0开始。
int cmd, //设置值用SETVAL.
... //union semun联合体给出的具体初值。
};
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<sys/ipc.h>
5 #include<sys/sem.h>
6 #include<string.h>
7
8 union semun{
9 int val;
10 };
11 int main(void)
12 {
13 union semun su;
14 int id=semget(1234,0,0);
15 if(id==-1)perror("semget"),exit(1);
16 su.val=5;
17 semctl(id,0,SETVAL,su);
18
19 }
4.查看值以及p v操作:
int semctl(int id,
int num, // 集合中的第几个信号量,从0开始。
int cmd //获取值用GETVAL。
};
返回值:id信号量集中第num个信号量的值。
p v操作:
struct sembuf {
short sem_num; //信号量集和中的第几个信号量。
short sem_op; //负数:+1. 正数:-1.
short sem_flg; //
int semop(int id,
struct sembuf sbuf[],
int len);
getval.c
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<sys/ipc.h>
5 #include<sys/sem.h>
6 #include<string.h>
7
8 int main(void)
9 {
10 int num;
11 int id=semget(1234,0,0);
12 if(id==-1)perror("semget"),exit(1);
13
14 num=semctl(id,0,GETVAL);
15 printf("num=%d\n",num);
16
17 }
p.c
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<sys/ipc.h>
5 #include<sys/sem.h>
6 #include<string.h>
7
8 int main(void)
9 {
10 int id=semget(1234,0,0);
11 if(id==-1)perror("semget"),exit(1);
12
13 struct sembuf sb[1];
14 sb[0].sem_num=0;
15 sb[0].sem_op=-1;
16 sb[0].sem_flg=0;
17 semop(id,sb,1);
18 }
v.c
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<sys/ipc.h>
5 #include<sys/sem.h>
6 #include<string.h>
7
8 int main(void)
9 {
10 int id=semget(1234,0,0);
11 if(id==-1)perror("semget"),exit(1);
12
13 struct sembuf sb[1];
14 sb[0].sem_num=0;
15 sb[0].sem_op=1;
16 sb[0].sem_flg=0;
17 semop(id,sb,1);
18 }
结果是:当设置初值后,用getval可以查看初值,然后p操作会将值减一, v会加一。当为0时,p不能运行,等待值大于0,相反v也是。
以上就是进程间的通信,这次学习的函数很多,也很难记,所以希望大家能多多练习,这样才能记得牢固。