linux初学笔记-进程间通信

进程通信与线程通信

管道通信

ps-axj
1.进程通信:在用户空间实现进程通信是不可能的,通过linux内核通信
2.线程间通信:可以在用户空间实现,可以通过全局变量通信。

进程间通信
管道通信;无名管道,有名管道
信号通信:信号的发送,接收和处理
IPC通信:共享内存,消息队列,信号灯

soket通信:存在于一个网络中两个进程之间的通信(两个linux内核)

无名管道4linux初学笔记-进程间通信_第1张图片
管道文件是一个特殊的文件,是由队列来实现的。单向队列。
int pipe(int fd[2]);
创建管道,为系统调用:unistd.h

  • 参数:两个文件的描述符 fd[0](读端,出队) 和fd[1](写端,入队)
  • 返回值:成功是0 出错是-1.

例:

#include "unistd.h"
#include "stdio.h"
#include "std;ib.h"
int main()
{
	int fd[2];
	int ret ;
	char wretebuf[]="hello linux";
	char readbuf[128]={0};
	ret=pipe(fd);
	if(ret<0)
	{	
	  printf("creat pipe failure\n");
	  return -1;
	}
	printf("creat success fd[0]=%d,fd[1]=%d\n",fd[0],fd[1]);
	wwrite(fd[1],writebuf,sizeof(writebuf));
	read(fd[0],read,128);
	printf("readbuf=%s\n",readbuf);
	close(fd[0]);
	close(fd[1]);
	return 0;
}

linux初学笔记-进程间通信_第2张图片
内核已创建了0 ,1 ,2

注意:
1.管道是创建在内存中的,进程结束,空间释放,管道就不存在了。
2.管道中的内容,读完就删除了;
3.如果管道中没有东西可以读,则会读阻塞;

#include "unistd.h"
#include "stdio.h"
#include "sys/type.h"
#include "stdlib.h"
int main()
{
	pid_t pid;
	int fd[2];
	int ret;
	int process_inter=0;
	ret=pipe(fd);
	if(ret<0){printf("creat pipe failure\n"); return -1;}
	printf(creat pipe success\n);
	
	pid = fork();
	if(pid==0)
	{
		int i=0;
		read(fd[0],&process_inter,1);//if pipe empty child sleep
		while(process_inter=0); //if father make process_inter as 1,break.
		 for(i=0;i<5;i++)
		 {
		 	printf("this is child process i=%d\n",i);
		 	usleep(100);
		 }
	  if(pid>0)
	  {
	  	int i=0;
		 for(i=0;i<5;i++)
		 {
		 	printf("this is father process i=%d\n",i);
		 	usleep(100);
		 }
		 process_inter=1;
		 write(fd[1],&process_inter,1);
	}
	return 0;
}

linux初学笔记-进程间通信_第3张图片
无名管道的缺陷:不能实现非父子进程间的通信

有名管道

管道文件只有inode号,不占磁盘块空间,和套接字,字符设备文件,块设备文件一样。

int mkfifo(const char *filename,mode_t mode);
创建管道文件节点(未在内核创建管道文件,open此节点之后才创建管道),实现无亲缘关系进程间通信。

  • 参数:管道文件文件名,权限(与umask有关)
  • 返回值:创建成功返回0,创建失败为-1.

创建管道节点

#include "stdio.h"
#include "unistd.h"
#include "stdlib.h"
int main()
{
	int ret;
	ret=mkfifo("./myfifo",0777);
	if(ret<0){printf("error");return -1}
	printf("success")
	return 0;

}

第一个进程

  2 #include "unistd.h"
  3 #include "stdlib.h"
  4 #include "sys/types.h"
  5 #include "fcntl.h"
  6 int main()
  7 {    
  8     int fd;
  9     int i;
 10     int  process_inter=0;
 11     fd=open("./myfifo",O_WRONLY);
 12     if(fd<0){printf("open failure\n");return -1;}
 13     printf("open myfifo success\n");
 14     for(i=0;i<5;i++)
 15      {
 16         printf("this is first process i=%d\n",i);
 17         usleep(100);
 18      }  
 19      process_inter=1;
 20      sleep(5);
 21      write(fd,&process_inter,1);
 22      while(1);
 23      return 0;
 24 }              

第二个进程

#include "stdio.h"
#include "unistd.h"
#include "stdlib.h"
#include "sys/types.h"
#include "fcntl.h"
int main()
{    
    int fd,i;
     int process_inter=0;
     fd=open("./myfifo",O_RDONLY);
     if(fd<0){printf("open failure\n");return -1;}
     printf("open myfifo success\n");
     read(fd,&process_inter,1);
     while(process_inter==0);
     for(i=0;i<5;i++)
     {
     printf("this is second process i=%d\n",i);
     usleep(100);
     }
     while(1);
     return 0;
}
gec

运行first后无输出,再运行second 才都能输出。

信号通信

信号的发送 kill raise alarm

kill
内核里面已经存在很多信号。
kill -l 查看
信号的发送
linux初学笔记-进程间通信_第4张图片

#include "stdio.h"
#include "unistd.h"
#include "stdlib.h"
#include "sys/types.h"
#include "signal.h"
int main(int argc,char *argv[])
{
	int sig;
	int pid;
	if(argc<3){printf("error\m");return -1;}
	sig=atoi(argv[1]); //字符转化为整型
	pid=atoi(argc[2]);
	printf("sig=%d,pid=%d\n",sig,pid);
	kill(pid,sig);
	
}

int raise(int sig)
发信号给自己 =kill(getpid(),sig)

父进程杀死子进程后没有回收(wait(NULL);)的话,子进程变成Z状态,僵司进程。

alarm
linux初学笔记-进程间通信_第5张图片

信号的接收 pause


linux初学笔记-进程间通信_第6张图片
使进程处于睡眠状态S

信号的处理 signal

linux初学笔记-进程间通信_第7张图片
参数:信号名称,处理函数

#include "stdio.h"
#include "unistd.h"
#include "stdlib.h"
#include "sys/types.h"
#include "signal.h"
void myfun(int signum)
{
	int i;
	i=0
	while(i<5)
	{
	printf("receive signum=%d",signum)
	sleep(1);
	i++;
	}
    return;
}
void myfun1(int signum)
{
    printf("receive signum=%d\n",signum);
    wait(NULL);
}
int main()
{
 pid_t pid;
 pif=fork();
 if(pid>0)
 {
 	int i;
 	i=0; 
 	signal(10,myfun);
 	signal(17,myfun1);
 	while(1)
 	{
 		printf("parent process things,i=%d\n",i);
 		sleep(1);
 		i++;
 	}
 }
 if(pid==0)
 {
 	sleep(10)
 	kill(getppid(),10);//发送给父进程信号10
 	sleep(10);
 	exit(0);//kill(getppid() ,17)
 	 }
}

IPC通信

ipc通信包含共享内存,消息队列,信号灯方式。
linux初学笔记-进程间通信_第8张图片

共享内存

共享内存创建之后一直存在于内核中,直到被删除或系统关闭。
共享内存和管道不一样,读取后,内容仍在其共享内存中。

创建共享内存
shmget
linux初学笔记-进程间通信_第9张图片
创建后查看IPC对象 ipcs -m
删除IPC对象 ipcrm -m id
两种key的获得
1.用宏创建时key为0,类似无名管道
2.:ftok获取key,类似有名管道,实现无亲缘关系的通信
*char ftok(const char path,char key)
参数:文件路径和文件名
一个字符
返回值:正确返回一个key值,出错返回-1

*void shmat(int shmid,const void shmaddr ,int shmflg)
当内核空间和用户空间存在大量数据交互时, 共享内存映射就成了这种情况下的不二选择; 它能够最大限度的降低内核空间和用户空间之间的数据拷贝, 从而大大提高系统的性能.
将共享内存映射到用户空间的地址中
参数:ID号
映射到的地址,NULL时为系统自动完成的映射
shmflg:SHM_RDONLY共享内存只读默认是0.
返回值:成功返回映射后的地址,失败返回NULL

int shmdt(const void shmaddr);
讲进程里的地址映射删除 ,内核的还在

  • 参数:shmaddr共享内存映射后的地址
  • 返回值:成功为0 出错为-1

int shmctl(int shmid, int cmd ,struct shmid_ds buf);
删除内核中共享内存对象。

  • 参数:shmid :要操作的共享内存标识符。
    cmd :IPC_STAT(获取对象属性)—实现了命令ipcs -m
    IPC_SET (设置对象属性)
    IPC_RMID (删除对象)—实现了命令ipcrm -m
    buf:指定IPC_STAT/IPC_SET时用以保存/设置属性,删除设置时为NULL。
  • 函数返回值 :成功 0 ,出错 -1
#include "stdio.h"
#include "unistd.h"
#include "stdlib.h"
#include "sys/types.h"
#include "signal.h"
#include "sys/shm.h"
void myfun(int signum)
{
	return;
}
int main()
{
	int shmid;
	int key;
	char *p;
	int pid;

	shmid=shmget(key,128,IPC_CREAT | 0777); //创建共享内存
	if(shmid <0)
	{  
	  	
		printf("creat share memory failure\n");
		return -1;
	}
	printf("creat share memory success shmid=%d\n",shmid);
	pid=fork();
	if(pid>0)
	{   
	    signal(SIGUSR2,myfun);
	    p=(char *) shmat(shmid,NULL,0);//共享内存映射
	    if(p==NULL){printf("parent shmat function failure\n");return -3;}
	    while(1)
	    {
	    	//write share memory
	    	printf("parent process start share memory");
	    	fget(p,128,stdin);
	    	kill(pid,SIGUSR1);//通知子进程读
	    	pause();//等待子进程读
	    }
	}
	if(pid==0)
	{   
	    signal(SIGUSR1,myfun);
	     p=(char *) shmat(shmid,NULL,0);//共享内存映射
	     if(p==NULL){printf("parent shmat function failure\n");return -3;}
	    while(1)
	    {
	    pause();//等待父进程写,pause()函数表示让进程或者线程进入休眠状态,直到进程或线程被杀死或者**调用信号捕捉函数**
	    
		//start read share memory
		printf("share memory data:%s",p);
		kill(getppid(),SIGUSR2);
		}
	}
	//system("ipcs -m");//查看共享内存
   shmdt(p);//删除映射内存
   shmctl(shmid,IPC_RMID,NULL);//删除共享内存
   system("ipcs -m");
  
   return 0;

}

SIGUSR1 用户自定义信号 默认处理:进程终止
SIGUSR2 用户自定义信号 默认处理:进程终止

消息队列

linux初学笔记-进程间通信_第10张图片
msqid_ds :内核维护消息队列的结构体,队列的第一个消息指针msg_first,最后一个消息指针msg_last
Data:数据
Length:数据的长度
type:消息类型

头文件:
sys/types/h sys/ipc.h sys/msg.h

int msgget(key_t,int flag)
创建消息队列

  • 参数:key 和消息队列关联的key值
    flag 消息队列的访问权限
  • 返回值:成功: 消息队列ID , 出错:-1
    查看队列 ipcs -q

*int msgctl(int msgqid, int cmd, struct msqid_ds buf);

  • 参数:msqid 消息队列的队列ID。
    cmd :IPC_STAT 读取消息队列的属性,并将其保存在buf指向的缓冲区中。ipcs -q。
    IPC_SET:设置消息队列的属性,这个值取自buf参数。
    IPC_RMID 从系统中删除消息队列
    buf:消息队列缓冲区。
  • 返回值:成功 0 ,出错 -1.


linux初学笔记-进程间通信_第11张图片

linux初学笔记-进程间通信_第12张图片
注意 :消息队列的写实际上是插入,读实际上是删除

sever.c 父进程写type100 子进程读type200

#include "sys/msg.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
struct msgbuf
{
	long type;
	char voltage[124];
	char ID[4];
};
int main()
{
	int msgid;
	int readret;
	int key;
	struct msgbuf sendbuf ,recvbuf;
	int pid;
	key=ftok("./a.c",'a');
	if(key<0)
	{
		printf("creat key failure\n");
		return -2;
	}
	msgid=msgget(key,IPC_CREAT|0777);//建立消息队列
	if(msgid<0)
	{
		printf("creat message queue failure\n");
		return -1;
	}
	printf("creat message queue success msgid=%d\n",msgid);
	system("ipcs -q");
	pid=fork();//创建子进程
	if(pid>0)//父进程写 type100
	{
		sendbuf.type=100;
		//写
		while(1)
		{
			memset(sendbuf.voltage,0,124);
			printf("please input message:\n");
			fgets(sendbuf.voltage,124,stdin);
			msgsnd(msgid,(void *)&sendbuf,strlen(sendbuf.voltage),0);
		}
		
	}
	if(pid==0)//子进程  读type200
	{   
	   while(1){
		memset(recvbuf.voltage,0,124);
		msgrcv(msgid,(void *)&recvbuf,124,200,0);
		printf("receive message from message queue:%s",recvbuf.voltage);
       		 }
	}
	msgctl(msgid,IPC_RMID,NULL);
	system("ipcs -q");
	return 0;
	
}
		

client.c 父进程读type200 子进程写type100

#include "sys/msg.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
struct msgbuf
{
	long type;
	char voltage[124];
	char ID[4];
};
int main()
{
	int msgid;
	int readret;
	int key;
	struct msgbuf sendbuf ,recvbuf;
	int pid;
	key=ftok("./a.c",'a');
	if(key<0)
	{
		printf("creat key failure\n");
		return -2;
	}
	msgid=msgget(key,IPC_CREAT|0777);//建立消息队列
	if(msgid<0)
	{
		printf("creat message queue failure\n");
		return -1;
	}
	printf("creat message queue success msgid=%d\n",msgid);
	system("ipcs -q");
	pid=fork();//创建子进程
	if(pid==0)//zijindxie
	{
		sendbuf.type=200;
		//写如队列
		while(1)
		{
			memset(sendbuf.voltage,0,124);
			printf("please input message:\n");
			fgets(sendbuf.voltage,124,stdin);
			msgsnd(msgid,(void *)&sendbuf,strlen(sendbuf.voltage),0);
		}
		
	}
	if(pid>0)//fu进程 
	{   
	   while(1){
		memset(recvbuf.voltage,0,124);
		msgrcv(msgid,(void *)&recvbuf,124,100,0);
		printf("receive message from message queue:%s",recvbuf.voltage);
       		 }
	}
	msgctl(msgid,IPC_RMID,NULL);
	system("ipcs -q");
	return 0;
	
}
	

实现了服务端和客户端双向通信

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200721103124102.png
在这里插入图片描述

信号灯

信号灯(semaphore)它是不同进程间或一个给定进程内部不同线程间同步的机制System V的信号灯是一个或者多个信号灯的一个集合(允许对集合中的多个计数信号灯进行同时操作)。其中的每一个都是单独的计数信号灯。而Posix信号灯指的是单个计数信号灯。
linux初学笔记-进程间通信_第13张图片
创建后用 ipcs -s 查看

linux初学笔记-进程间通信_第14张图片
int semop(int semid,struct senbuf opsptr,size nops);
semid:信号灯集ID
struct sembuf
{
short sen num;//要操作的信号灯编号
short sem_op //0:等待,直到信号灯的值变成0,1:释放资源,V操作.-1:分配资源,p操作
short sem_flg ;//0,IPC_NOWAIT,SEM_UNDO
}
nops:要操作的信号灯个数

你可能感兴趣的:(linux)