Linux_进程间的通讯方式

进程间通信(IPC)

1:管道(无名管道、有名管道),单向,数据先进先出。

2:信号,进程间异步通信,一般通知机制都用信号(一个进程满足某种条件该让另一个进程做某件事情)。

3:共享内存,数据不区分类型和先后顺序,只要将数据发送给其他进程就可以,是效率最高的进程间通信方式。但是共享内存本身没有同步机制,必须结合信号量来实现同步。

4:信号量,主要来实现进程间的同步,也可以实现互斥。

5:消息队列,当进程之间发送的数据有类型的区别,各个进程可以根据自己的情况来读取不同的类型。

6:套接字(socket)

 

一、管道

单向,单工通信,先进先出.

1、无名管道(只能用于具有亲缘关系的通信)

ls -al | grep "xxx"  寻找关于“xxx”的所有文件,命令中的“ | ”就是无名管道。

ls 取 grep 的数据然后显示到终端

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main(void)
{
	int fd[2];
	pid_t pid;
	char buf[20];

	pipe(fd);  //经过该语句,内核在内存中开辟一段区域,做为管道的存储空间,
               //该存储空间是一个虚拟文件,它会给我们两个文件描述符。

	pid = fork();

	if(0 == pid)
	{
		sleep(2);
		//关闭写端
		close(fd[1]);
		while(1)
		{
			read(fd[0], buf, 20);
			if(strncmp(buf,"exit",4) == 0)
			{
				break;
			}
			printf("从父进程读取数据%s", buf);
		}
		exit(0);
	}

	//以下是父进程
	//关闭读端
	close(fd[0]);
	while(1)
	{
		fgets(buf,1024,stdin);
		write(fd[1], buf, strlen(buf));
		if(strncmp(buf,"exit",4) == 0)
		{
			break;
		}
	}
	wait(NULL);

	return 0;
}

2.有名管道,读端和写端必须同时打开。

流程:a.使用mkfifo创建管道

            b.使用open打开管道

            c.使用read/write读写管道

            d.使用close关闭管道

读端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

//有名管道用于非亲缘关系通讯(既可以亲缘,也可以非亲缘,此处列举非亲缘)
int main(void)
{
	int ret;
	int fd;
	char buf[20];

	fd = open("myfifo", O_RDONLY);
	if(-1 == fd)
	{
		perror("open");
		exit(1);
	}

	//对管道进行操作
	while(1)
	{
		ret = read(fd, buf, 20);
		buf[ret] = '\0';
		if(strncmp(buf, "exit", 4) == 0)
		{
			break;
		}
		printf("从对方读取%s",buf);
		sleep(2);
	}

	close(fd);
	return 0;
}

写端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

//有名管道用于非亲缘关系通讯(既可以亲缘,也可以非亲缘,此处列举非亲缘)
int main(void)
{
	int ret;
	int fd;
	char buf[20];

	//判断文件是否存在
	ret = access("myfifo",F_OK);
	if(ret != 0)
	{
		//创建管道
		ret = mkfifo("myfifo", 0666); 
		if(-1 == ret)
		{
			perror("mkfifo");
			exit(1);
		}
	}
	//打开管道
	fd = open("myfifo", O_WRONLY);
	if(-1 == fd)
	{
		perror("open");
		exit(1);
	}

	//对管道进行操作
	while(1)
	{
		fgets(buf, 1024, stdin);
		write(fd, buf, 20);
		if(strncmp(buf,"exit",4) == 0)
			break;
	}

	close(fd);
	return 0;
}

 

二、信号

信号是进程间唯一的异步通信

查看信号:kill -l

信号的来源:

1.由硬件检测产生(SIGSEGV:段错误)

2.由终端按键产生(SIGINT:ctrl + c)

3.由软件本身产生(SIGPIPE:管道)  读端关闭,写端写入

4.由内核发送产生(SIGIO、SIGURG)

5.由其他进程发送(kill)

收到一个信号怎么处理:

1.忽略该信号

2.按照默认方式处理

3.由用户自定义处理

常用的信号:

#define SIGHUP 1            //父进程给子进程发的

#define SIGCHLD 17       //子进程给父进程发的

 1) SIGHUP     2) SIGINT     3) SIGQUIT     4) SIGILL     5) SIGTRAP
 6) SIGABRT     7) SIGBUS     8) SIGFPE     9) SIGKILL    10) SIGUSR1
11) SIGSEGV    12) SIGUSR2    13) SIGPIPE    14) SIGALRM    15) SIGTERM
16) SIGSTKFLT    17) SIGCHLD    18) SIGCONT    19) SIGSTOP    20) SIGTSTP
21) SIGTTIN    22) SIGTTOU    23) SIGURG    24) SIGXCPU    25) SIGXFSZ
26) SIGVTALRM    27) SIGPROF    28) SIGWINCH    29) SIGIO    30) SIGPWR
 

简单实例,杀死一个信号:

while.c

#include <stdio.h>

int main(void)
{	
	printf("pid = %d\n", getpid());

	while(1)
	{
		printf("信号测试\n");
		sleep(1);
	}

	return 0;
}

kill.c

#include <stdio.h>
#include <signal.h>

int main(int argc, char *argv[])
{
	kill(atoi(argv[1]), SIGKILL);

	return 0;
}

运行 ./while,然后./kill pid

 

函数原型:void (*signal(int signum, void (*handler)(int)))(int)

void func(int arg)

{

}

void (*p)(int)

p = signal(SIGUSER1,func)  //当这个函数执行时告诉内核收到SIGUSER1时,执行func函数

int pause(void); //阻塞当前进程,直到收到一个信号为止

 

三、共享内存

共享内存:开辟一段物理内存使多个进程共享

查看系统共享内存:ipcs

一个终端同时打开两个文件:vsp xxx.c

共享内存必须结合其他方式来实现进程间的同步。

编程步骤:

1.开辟一块共享内存

2.将共享内存映射到进程的地址空间(映射的本质就是得到一个虚拟地址,通过该虚拟地址可以访问到共享内存)

3.对共享内存进行读写

4.撤销映射

5.删除共享内存

shm_read.c

#include <stdio.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <signal.h>

void sig_func(int arg)
{

}

int main(void)
{
	int shmid;
	char *p;
	char buf[20];

	pid_t pid = getpid();

	signal(SIGUSR1, sig_func);


	//获得键值
	key_t key = ftok(".", 'a');
	
	//开辟一段共享内存
	shmid = shmget(key, 1024, IPC_CREAT|0666);

	//将共享内存映射到进程的地址空间
	p = shmat(shmid, NULL, 0);   //既可读又可写
	*((pid_t *)p) = pid;

	//对共享内存进行读写
	while(1)
	{
		pause();
		printf("读取数据%s", p);
	}

	//撤销映射
	shmdt(p);

	//删除共享内存
	shmctl(shmid, IPC_RMID, NULL);

	return 0;
}

shm_write.c

#include <stdio.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <string.h>
#include <signal.h>

int main(void)
{
	int shmid;
	char *p;
	char buf[20];

	pid_t pid;

	//获得键值
	key_t key = ftok(".", 'a');
	
	//开辟一段共享内存
	shmid = shmget(key, 1024, IPC_CREAT|0666);

	//将共享内存映射到进程的地址空间
	p = shmat(shmid, NULL, 0);   //既可读又可写
	pid = *(pid_t *)p;

	//对共享内存进行读写
	while(1)
	{
		fgets(buf, 1024, stdin);
		strcpy(p, buf);
		kill(pid, SIGUSR1);  //发送信号
	}

	//撤销映射
	shmdt(p);

	//删除共享内存
	//shmctl(shmid, IPC_RMID, NULL);

	return 0;
}

 

四、信号量

信号量:主要来实现进程间或线程间同步,也可以实现互斥。

信号量的值:表示资源的可用量

信号量的操作流程:

1.创建一个信号量集合

        param2:信号量集合中信号量的个数

        int semget(key_t key, int nsems, int semflg)

2.对信号量进行初始化

        param2:要操作的信号量在集合中的下标

        param3:要进行什么操作,详细见man手册

        int semctl(int semid, int semnum, int cmd,...)

3.实现P、V函数

4.在适当地方进行P、V操作

5.删除信号量集合

P操作:信号量的值减1,获得资源

V操作:信号量的值加1,释放资源

代码详述,只创建1个信号量:

sem.h

#include <stdio.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>

union semun{
	int 			val;
	struct semid_ds *buf;
	unsigned short  *array;
	struct seminfo  *__buf;
};

//实现信号量初始化
void sem_ini(int semid, int index, int val);
//实现P操作
void sem_p(int semid, int index);
//实现V操作
void sem_v(int semid, int index);
//集合删除信号量
void sem_del(int semid);

sem.c

#include "sem.h"

//val:创建信号量个数,此处针对一个信号量
void sem_init(int semid, int index, int val)
{
	union semun sem_p;
	int ret;

	sem_p.val = val; 
    ret = semctl(semid, index, SETVAL, sem_p);
	if (-1 == ret)
	{
		perror("semctl_init");
		exit(1);
	}
}

void sem_p(int semid, int index)
{
	int ret;
	struct sembuf buf;

	buf.sem_num = index;
	buf.sem_op = -1;
	buf.sem_flg = 0;
	ret = semop(semid, &buf, 1);
	if (-1 == ret)
	{
		perror("semop_p");
		exit(1);
	}
}

void sem_v(int semid, int index)
{
	int ret;
	struct sembuf buf;

	buf.sem_num = index;
	buf.sem_op = 1;
	buf.sem_flg = 0;
	ret = semop(semid, &buf, 1);
	if (-1 == ret)
	{
		perror("semop_v");
		exit(1);
	}
}

void sem_del(int semid)
{
	int ret;
	ret = semctl(semid, 0, IPC_RMID);
	if(-1 == ret)
	{
		perror("semctl_del");
		exit(1);
	}
}

sem_write.c

#include "sem.h"

int main(void)
{
	int semid;
	int shmid;
	char *p;
	char buf[20];
	pid_t pid;
	
	//获取键值
	key_t key_shm = ftok(".",'a');
	key_t key_sem = ftok(".",'b');

	//开辟一段共享内存
	shmid = shmget(key_shm, 1024, IPC_CREAT|0666);
	semid = semget(key_sem, 1, IPC_CREAT|0666);

	//对信号量进行初始化,第param2的下标初始化为param3的值
	sem_init(semid, 0, 0);

	//将共享内存映射到进程的地址空间
	p = shmat(shmid, NULL, 0);

	//对共享内存进行读写
	while(1)
	{
		fgets(buf, 1024, stdin);
		strcpy(p, buf);
		//对信号量进行V操作
		sem_v(semid, 0);
	}

	//撤销映射
	shmdt(p);

	return 0;
}

sem_read.c

#include "sem.h"

int main(void)
{
	int shmid;
	int semid;
	char *p;

	key_t key_shm = ftok(".",'a');
	key_t key_sem = ftok(".",'b');

	shmid = shmget(key_shm, 1024, IPC_CREAT|0666);
	semid = semget(key_sem, 1, IPC_CREAT|0666);

	p = shmat(shmid, NULL, 0);

	while(1)
	{
		//阻塞
		sem_p(semid,0);
		printf("读取数据%s", p);
	}

	shmdt(p);
	shmctl(shmid, IPC_RMID, NULL);
	sem_del(semid);

	return 0;
}

 

五、消息队列

双向的

1.创建消息队列

2.向消息队列发送数据

3.删除消息队列

msg_send.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/msg.h>
#include <sys/ipc.h>

struct msgbuf{
	long mtype;
	char mtext[20];
};


int main(void)
{
	int msgid;
	struct msgbuf info;
	int ret;
	info.mtype = 1;
	//获取键值
	key_t key = ftok(".",'a');

	//创建消息队列
	msgid = msgget(key, IPC_CREAT|0666);
	if(-1 == msgid)
	{
		perror("msgget");
		exit(1);
	}

	//向消息队列发送消息
	while(1)
	{
		fgets(info.mtext, 1024, stdin);
		ret = msgsnd(msgid, (void*)&info, sizeof(info), 0);
		if(-1 == ret)
		{
			perror("msgsnd");
			exit(1);
		}
	}

	return 0;
}

msg_recv.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/msg.h>
#include <sys/ipc.h>

struct msgbuf{
	long mtype;
	char mtext[20];
};


int main(void)
{
	int msgid;
	struct msgbuf info;
	int ret;

	info.mtype = 1;
	//获取键值
	key_t key = ftok(".",'a');

	//创建消息队列
	msgid = msgget(key, IPC_CREAT|0666);
	if(-1 == msgid)
	{
		perror("msgget");
		exit(1);
	}

	//向消息队列发送消息
	while(1)
	{
		//param4:接收到第1个消息队列的消息,若改成4,则接收到第4个消息队列的消息
		ret = msgrcv(msgid, &info, sizeof(info),1,0);
		if(-1 == ret)
		{
			perror("msgrcv");
			exit(1);
		}
		printf("接收数据 %s", info.mtext);
	}

	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值