LinuxC 进程之间通信

进程中通信即IPC        Inter Process Communication

进程中通信常见的几种方式                                        本文只讲管道、消息队列、共享内存

        1、管道通信:无名管道、有名管道

        2、信号--系统开销小

        3、消息队列--内核的链表

        4、信号量--计数器

        5、共享内存

        6、内存映射

        7、套接字

无名管道:

  1. 管道的概念

LinuxC 进程之间通信_第1张图片本质

        内核缓冲区

        伪文件-不占用磁盘空间

 

特点:

        两部分:

        读端,写端,对应两个文件描述符

        数据写端流入,读端流出

        操作管理的进程被销毁之后,管道自动被释放

        管道默认是阻塞的

        

管道的原理

        内部实现方式:队列

        环形队列 内存中不是   环形队列假想的

        特点:先进先出

        缓冲区大小

                默认4K

                大小会根据实际情况做出适当调整

管道的局限性

        队列:

                数据只能读取一次,不能重复读取

        单工:遥控器

        半双工:对讲机

        数据传输方向是单向的

        双工:电话

        匿名管道

        适用于有血缘关系的进程

创建匿名管道

        Int pipe(int fd[2])

        fd-传出参数

        fd[0]-读端

        fd[1]-写端

返回值:

        0:成功

        -1:创建失败

#include
#include
#include


int main()
{
	int fd[2];
	int ret;
	ret=pipe(fd);
	if(-1==ret)
	{
		perror("pipe failed");
		exit(1);
	}

	printf("read fd[0] is %d\n ",fd[0]);
	printf("write fd[1] is %d\n",fd[1]);

	close(fd[0]);
	close(fd[1]);

	return 0;
}

父子进程使用管道通信

        实现ps aux |grep  “bash”

        数据重定向:dup2

#include
#include
#include
#include

int main()
{
	int fd[2];
	int ret;
	ret=pipe(fd);
	if(-1==ret)
	{
		perror("pipe failed");
		exit(1);
	}
	
	pid_t pid=fork();
	if(pid==-1)
	{
		perror("fork falied");
		exit(1);
	}
	else if(pid==0)
	{
		close(fd[1]);
		dup2(fd[0],STDIN_FILENO);
		execlp("grep","grep","bash","--color=auto",NULL);
	}
	else if(pid>0)
	{
		close(fd[0]);
		dup2(fd[1],STDOUT_FILENO);
		execlp("ps","ps","aux",NULL);
		perror("execlp");
		exit(0);

	}
	printf("read fd[0] is %d\n ",fd[0]);
	printf("write fd[1] is %d\n",fd[1]);

	close(fd[0]);
	close(fd[1]);

	return 0;
}

查看管道缓冲区大小

        命令

                ulimit -a

                fpathconf

#include
#include
#include

int main()
{
	int fd[2];
	int ret=pipe(fd);
	if(ret==-1)
	{
		printf("pipe create failed\n");
		exit(0);
	}

	long size=fpathconf(fd[0],_PC_PIPE_BUF);
	printf("size is %ld\n",size);
	printf("pipe[0] is %d\n",fd[0]);
	printf("pipe[1] is %d\n",fd[1]);


	close(fd[0]);
	close(fd[1]);
	return 0;
}

有名管道

        函数形式:

        int mkfifo(const char *filename,mode_t mode)

        功能:创建管道文件

        参数:管道文件文件名,权限,创建的文件权限仍然和umask有关系

        返回值:创建成功返回0,创建失败返回-1

特点:

        有名管道

        在磁盘上有这样一个文件ls -l ->p

        也是一个伪文件,在磁盘大小永久为0

        数据存在内核中有一个对应的缓冲区

        半双工通信方式

使用场景

        没有血缘关系的进程间通信

使用mkfifo之后,只是在用户态创建了一个管道结点,内核中并没有管道,open之后才会到内核中建立一个管道,并且用户态的结点指向内核态的管道。相当于是一个缓冲区。

#include
#include
#include
#include

int main()
{
	int ret;
	ret=mkfifo("./myfifo",0766);
	if(ret==-1)
	{
		printf("mkfifo create failed\n");
		exit(1);
	}
	printf("mkfifo create success\n");

	return 0;
}

有名管道:实现进程间通信

        读端:

#include
#include
#include
#include
#include
#include
#include

int main()
{
	int ret;
	int fd;
	char readBuff[128];
	memset(readBuff,0,sizeof(readBuff));
	ret=mkfifo("./myfifo",0766);
	if(ret==-1)
	{
		printf("mkfifo create failed\n");
		exit(1);
	}
	printf("mkfifo create success\n");
	
	fd=open("./myfifo",O_RDONLY);
	if(-1==fd)
	{
		perror("open failed");
		exit(1);
	}

	ret=read(fd,readBuff,sizeof(readBuff));

	printf("ret is %d readBuff is %s\n",ret,readBuff);
	close(fd);

	return 0;
}

写端:

#include
#include
#include
#include
#include
#include
#include

int main()
{
	int fd;
	int ret;
	char *writeBuff="hello world";
	fd=open("./myfifo",O_WRONLY);
	if(-1==fd)
	{
		perror("open fifo failed");
		return 1;
	}

	ret=write(fd,writeBuff,strlen(writeBuff));
	printf("write is success %d ,%ld\n",ret,strlen(writeBuff));
	close(fd);
	return 0;
}

 

 

消息队列

消息队列,是消息的链表,存放在内核中,一个消息队列由一个标识符(队列ID)来标识

LinuxC 进程之间通信_第2张图片

 

特点:

消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级

消息队列独立于发送和接收进程,进程终止时,消息队列及其内容仍存在

消息队列可以实现消息的随机查询,消息不一定要先进先出的次序读取,也可以按消息的类型读取。

相关函数

        Int msgget(key_t key,int msgfla);

        创建或打开消息队列

参数:

        Key:和消息队列关联的key值

        Msgflg:是一个权限标志,表示消息队列的访问权限,它与文件的访问权限一样。Msgflg可以与IPC_CREAT做或操作,表示当key所命名的消息队列不存在时创建一个消息队列,如果key所命名的消息队列存在时,IPC_CREAT标志会被忽略,而返回一个标识符。

        返回值:成功返回队列ID,失败则返回-1。

#include
#include 
#include 
#include 
#include

int main()
{
        int msgid;
        //msgid=msgget(IPC_PRIVATE,0755);
        if(msgid==-1)
        {
                printf("create message queue failed\n");
                return 1;
        }
        printf("create message queue success msgid=%d\n",msgid);
        system("ipcs -q");

        msgctl(0,IPC_RMID,NULL);
        msgctl(1,IPC_RMID,NULL);
        system("ipcs -q");
        return 0;
}

Int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg)

//读取消息,成功返回消息数据的长度,失败返回-1;

参数:

        Msgid:消息队列的ID

        Msgp:指向消息的指针,常用结构体msgbuf如下:

        Struct msgbuf

        {

                Long mtype;消息类型

                Char mtext[N];消息正文

        }

        Size:发送的消息正文的字节数

        Flag:

                IPC_NOWAIT:消息没有发送完成函数也会立即返回

                0:知道发送完成函数才返回

返回值:

        成功:0

        失败:-1

#include
#include 
#include 
#include 
#include
#include



struct msgbuf
{
        long mtype;
        char mtest[128];
        char ID[4];
};

int main()
{
        struct msgbuf sendbuf;
        int msgid;
        msgid=msgget(IPC_PRIVATE,0755);
        if(msgid==-1)
        {
                printf("create message queue failed\n");
                return -1;
        }

        system("ipcs -q");

        printf("create message queue success msgid = %d\n",msgid);

        sendbuf.mtype=100;
        printf("please input to message queue\n");
        fgets(sendbuf.mtest,128,stdin);
        msgsnd(msgid,(void *)&sendbuf,strlen(sendbuf.mtest),0);

        while(1);

        return 0;
}

Size_t msgrcv(int msgid,void *msgp,size_t msgsz,long msgtyp,int msgflg);

//从一个消息队列中获取消息

参数:

        Msgid:消息队列的ID

        Msgp:要接受消息的缓冲区

        Size:要接受的消息的字节数

        Msgtype:

                0:接收消息队列中第一个消息

                大于0:接收消息队列中第一个类型为msgtyp的消息

                小于0:接收消息队列中类型值不大于msgtyp的绝对值且类型值又最小的消息

        Flag:

                0:若无消息函数一直阻塞

                IPC_NOWAIT:若没有消息,进程会立即返回ENOMSG

        返回值:

                成功:接收到的消息i长度

                出错:-1

#include
#include
#include
#include
#include
#include

struct msgbuf
{
	long mtype;
	char mtest[128];
	char ID[4];
};

int main()
{
	struct msgbuf sendbuf,recvbuf;
	int msgid,ret;
	msgid=msgget(IPC_PRIVATE,0755);
	if(msgid==-1)
	{
		printf("msgid fail\n");
		exit(1);
	}
	printf("create msgid is %d\n",msgid);

	sendbuf.mtype=100;
	printf("please input\n");
	fgets(sendbuf.mtest,128,stdin);
	
	msgsnd(msgid,(void *)&sendbuf,strlen(sendbuf.mtest),0);

	ret=msgrcv(msgid,(void *)&recvbuf,128,100,0);

	printf("recvbuf %d is %s\n ",ret,recvbuf.mtest);

	return 0;
}

LinuxC 进程之间通信_第3张图片

 

Int msgctl(int msqid,int cmd,strcut msqid_ds *buf);

//控制消息队列,成功返回0,失败返回-1

参数:

        Msqid:消息队列的队列ID

        Cmd:

        IPC_STAT:把msgid_ds结构体中的数据设置为消息队列的当前关联值,即用消息队列的当前关联值覆盖msgid_ds的值

        IPC_SET:如果进程有足够的权限,就把消息队列的当前关联值设置为msgid_ds结构中给出的值

        IPC_RMID:删除消息队列

        Buf:是指向msgid_ds结构的指针,它指向消息队列模式和访问权限的结构

返回值:

        成功:0

        失败:-1

在以下两种情况下,msgget将创建一个新的消息队列:

        如果没有与键值key相对应的消息队列,并且flag中包含了IPC_CREAT标志

        Key参数为IPC——PRIVTE

函数msgrcv在读取消息队列时,type参数有下面几种情况

        Type==0,返回队列中的第一消息

        Type>0,返回队列中消息队列类型为type的第一个消息

        Type<0,返回队列中消息类型值小于或等于type绝对值的消息,如果有多个,则取类型值最小的消息。

        可以看出,type值非0时用于以非先进先出次序读取消息,也可以把type看成优先级的权值。

ftok函数

        Key_t ftok(char * fname,int id)

        //系统建立IPC通讯(如消息队列,共享内存时)必须指定一个ID值。通常情况下,该id值通过ftok函数得到。

参数:

        Fname就是你指定的文件名(该文件必须是存在而且可以访问的)

        Id是子序号,虽然为int,但是只有8个比特被使用(0-255)

返回值:

        当成功执行的时候,一个key_t值将会被返回,否则-1被返回

ipcs  -q 查看消息队列

消息队列发送数据后,数据大小会增加,接收数据后,数据大小会减少。但是消息队列结点还是会存在。

两个进程之间通过消息队列实现一个进程写,另一个进程读。半双工通信

#include
#include
#include
#include
#include
#include


struct msgbuf
{
	long mtype;
	char mtest[128];
	char ID[4];
};


int main()
{
	struct msgbuf sendbuf;
	key_t key;
	int msgid;

	key=ftok("a.c",1);
	msgid=msgget(key,IPC_CREAT|0755);
	if(msgid==-1)
	{
		printf("msgid fail\n");
		exit(1);
	}
	printf("msgid is %d\n",msgid);
	sendbuf.mtype=200;
	printf("please input\n");
	while(1)
	{	
		memset(sendbuf.mtest,0,128);
		fgets(sendbuf.mtest,128,stdin);
		msgsnd(msgid,(void *)&sendbuf,strlen(sendbuf.mtest),0);
	}

	return 0;
}

#include
#include
#include
#include
#include
#include


struct msgbuf
{
	long mtype;
	char mtest[128];
	char ID[4];
};

int main()
{
	struct msgbuf recvbuf;
	key_t key;
	int msgid,ret;
	key=ftok("a.c",1);
	msgid=msgget(key,IPC_CREAT|0755);
	if(msgid==-1)
	{
		printf("msgid failed\n");
		exit(1);
	}

	printf("msgid is %d\n",msgid);

	recvbuf.mtype=200;

	while(1)
	{
		memset(recvbuf.mtest,0,128);
		ret=msgrcv(msgid,(void *)&recvbuf,128,200,0);
		printf("recv is %s",recvbuf.mtest);
		printf("total is %d\n",ret);
	}

	return 0;

}

LinuxC 进程之间通信_第4张图片

 进程之间消息队列全双工通信:

服务器端:

#include
#include
#include
#include
#include
#include
#include

struct msgbuf
{
	long mtype;
	char mtest[128];
	char ID[4];
};


int main()
{
	struct msgbuf sendbuf,recvbuf;
	key_t key;
	int msgid,ret;
	pid_t pid;
	key=ftok("a.c",1);
	msgid=msgget(key,IPC_CREAT|0755);
	if(msgid==-1)
	{
		printf("msgid fail\n");
		exit(1);
	}
	printf("msgid is %d\n",msgid);
	sendbuf.mtype=200;
	recvbuf.mtype=300;
	pid=fork();
	if(pid==0)
	{
		while(1)
		{
			memset(recvbuf.mtest,0,128);
			ret=msgrcv(msgid,(void *)&recvbuf,128,300,0);
			printf("recvbuf is %s",recvbuf.mtest);
			printf("total ret is %d\n",ret);
		}
	}
	else if(pid>0)
	{	

		printf("please input\n");
		while(1)
		{
			memset(sendbuf.mtest,0,128);
			fgets(sendbuf.mtest,128,stdin);
			msgsnd(msgid,(void *)&sendbuf,strlen(sendbuf.mtest),0);	
		}

	}


	return 0;
}

客服端:

#include
#include
#include
#include
#include
#include
#include

struct msgbuf
{
	long mtype;
	char mtest[128];
	char ID[4];
};


int main()
{
	struct msgbuf sendbuf,recvbuf;
	key_t key;
	int msgid,ret;
	pid_t pid;
	key=ftok("a.c",1);
	msgid=msgget(key,IPC_CREAT|0755);
	if(msgid==-1)
	{
		printf("msgid fail\n");
		exit(1);
	}
	printf("msgid is %d\n",msgid);
	sendbuf.mtype=300;
	recvbuf.mtype=200;
	pid=fork();
	if(pid==0)
	{
		while(1)
		{
			memset(recvbuf.mtest,0,128);
			ret=msgrcv(msgid,(void *)&recvbuf,128,200,0);
			printf("recvbuf is %s",recvbuf.mtest);
			printf("total ret is %d\n",ret);
		}
	}
	else if(pid>0)
	{	

		printf("please input\n");
		while(1)
		{
			memset(sendbuf.mtest,0,128);
			fgets(sendbuf.mtest,128,stdin);
			msgsnd(msgid,(void *)&sendbuf,strlen(sendbuf.mtest),0);	
		}

	}


	return 0;
}

LinuxC 进程之间通信_第5张图片

 

共享内存

  1. 概念

共享内存(Shared Memory)就是允许多个进程访问同一个内存空间,是在多个进程之间共享和传递数据最高效的方式。操作系统将不同进程之间共享内存安排为同一段物理内存,进程可以将共享内存连接到它们自己的地址空间中,如果某个进程修改了共享内存中的数据,其它的进程读到的数据也将会改变。

  1. 相关函数

Int shmget(key_t key,size_t size,int shmflg);

//用来获取或创建共享内存

参数:

        Key:IPC_PRIVATE或ftok的返回值

        Size:共享内存区大小

        Shmflg:同open函数的权限位,也可以用8进制表示法

返回值:

        成功:共享内存段标识符-----ID----文件描述符

        出错:-1

#include
#include
#include
#include

int main()
{
	int shmid;
	shmid=shmget(IPC_PRIVATE,128,0777);
	if(shmid==-1)
	{
		perror("shmid failed");
		exit(1);
	}

	printf("create shmid is %d\n",shmid);
	system("ipcs -m");

	return 0;
}

LinuxC 进程之间通信_第6张图片

 

void *shmat(int shm_id,const void *shm_addr,int shmflg);

//把共享内存连接映射到当前进程的地址空间 从内核态映射到用户态中

参数

        Shm_id:ID号

        Shm_addr:映射到的地址,NULL为系统自动完成的映射

        Shmflg:

                SHM_RDONLY共享内存只读

                默认是0,表示共享内存可以读写

返回值:

        成功:映射后的地址

        失败:NULL

#include
#include
#include
#include
#include

int main()
{
	int shmid;
	key_t key;
	
	char *q;
	key=ftok("a.c",1);
	if(key== -1)
	{
		perror("key fail");
		exit(1);
	}
	printf("key is %d\n",key);
	shmid=shmget(key,128,IPC_CREAT|0755);
	if(shmid==-1)
	{
		perror("shmid fail");
		exit(1);
	}

	printf("shmid is %d\n",shmid);
	system("ipcs -m");

	q=(char *)shmat(shmid,NULL,0);
	if(q==NULL)
	{
		printf("shmat failed\n");
		exit(1);
	}

	fgets(q,128,stdin);

	printf("%s",q);
	return 0;
}

LinuxC 进程之间通信_第7张图片

 

Int shmdt(const void *shmaddr);

//将进程里的地址映射删除

参数:

Shmid:要操作的共享内存标识符

返回值:

        成功:0

        出错:-1

Int shmctl(int shm_id,int command,struct shmid_ds *buf)

//删除共享内存对象

参数:

        Shmid:要操作的共享内存标识符

        Cmd:

                IPC_STAT(获取对象属性)---实现了命令ipcs  -m

                IPC_SET(设置对象属性)

                IPC_RMID(删除对象)-----实现了命令ipcrm  -m

        Buf:指定IPC_STAT/IPC_SET时用以保存/设置属性

返回值:

        成功:0

        出错:-1

#include
#include
#include
#include
#include
#include
int main()
{
	int shmid;
	key_t key;
	
	char *q;
	key=ftok("a.c",1);
	if(key== -1)
	{
		perror("key fail");
		exit(1);
	}
	printf("key is %d\n",key);
	shmid=shmget(key,128,IPC_CREAT|0755);
	if(shmid==-1)
	{
		perror("shmid fail");
		exit(1);
	}

	printf("shmid is %d\n",shmid);
	system("ipcs -m");

	q=(char *)shmat(shmid,NULL,0);
	if(q==NULL)
	{
		printf("shmat failed\n");
		exit(1);
	}

	fgets(q,128,stdin);

	printf("%s",q);

	shmdt(q);
	//memcpy(q,"linux",5);
	shmctl(shmid,IPC_RMID,NULL);
	return 0;
}

出错:-1

特点:

共享内存创建后,一直存在于内核中,直到被删除或系统关闭。

共享内存和管道不一样,读取后,内容仍然在共享内存中。

Ipcrm -m +shmid  删除共享内存

Ipcs -m 查看共享内存

shmdt只能删除用户态地址映射,要想删除内核中的共享内存要用shmctl

实现ipcrm -m shmid

#include
#include
#include
#include
#include
#include


int main(int argc,char *argv[])
{
	int shmid;
	if(argc<4)
	{
		printf("argc fail\n");
		exit(1);
	}

	if(strcmp("ipcrm",argv[1])==0)
	{
		if(strcmp("-m",argv[2])==0)
		{
			shmid=atoi(argv[3]);
			printf("shmid is %d\n",shmid);
			shmctl(shmid,IPC_RMID,NULL);
			system("ipcs -m");
		}
		else
		{
			printf("argv[2] fail \n");
			exit(1);
		}
	}
	else
	{
		printf("argv[1] fail\n");
		exit(1);
	}

	return 0;
}

你可能感兴趣的:(服务器,linux,c语言)