Linux之进程间的通信。

前段时间学习了进程的基本操作,今天来学习一下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也是。

以上就是进程间的通信,这次学习的函数很多,也很难记,所以希望大家能多多练习,这样才能记得牢固。

猜你喜欢

转载自blog.csdn.net/ymk1507050118/article/details/81223374