Linux——进程间通信

目录

一、概述

二、管道通信原理

管道,通常指无名管道

1、特点:

2、pipe函数原型:

 FIFO,也称为命名管道,它是一种文件类型

1、特点:

2、函数原型

二、System V 进程间通信

1.IPC的键值

1.1 key_t键和ftok函数

1.2 IPC键值与IPC标识符

 1.3 ipc_perm结构说明

1.4 IPC对象的创建权限

1.5 创建或打开IPC对象流程图

2、消息队列

2.1 消息队列简要说明

 2.2 消息队列函数

2.3 消息队列使用程序范例

3、信号量

3.1 信号集内核图

3.2 信号量集内核结构定义

3.3 信号量函数

​编辑

4、共享内存

4.1 共享内存内核结构定义

 4.2 共享内存函数

 代码示例

三、信号

1、信号概述

1.1信号分类

1.2 信号源分类

1.3 不可靠信说明

1.4 常见信号说明 

1.5信号的三种操作方式 

1.6 信号的五种默动作

1.7 阻塞信号和忽略信号两种操作的区别

2、 信号的发送和捕捉函数

2.1 alarm函数

2.2 kill函数

2.3 raise函数

2.4 pause函数 

2.5 sleep函数和abort函数 

2.6 信号的发送与捕捉简要总结

3、信号的处理

3.1 signal函数

3.2 sigaction函数

进程间通讯方式比较

一些小疑问:

sleep函数有以下几个作用:

关于使用return和exit的一些要点:

time函数和ctime函数

共享内存中,一个进程用shmctl将该共享内存用IPC_RMID删除了,其他进程还能读到写入共享内存中的数据吗?为什么?

如在函数semget( mykey, numsems, IPC_CREAT | 0660 ) 中,0660是什么意思?


一、概述

        父子进程间如何实现信息沟通,之前了解到的有:父进程创建子进程,子进程是从父进程那里copy某些数据过来。只是子进程子进程退出的时候,调用exit(0),然后父进程调用wait(status)等待子进程退出获取子进程退出状态信息,把退出码返回给父进程,可通过宏来读取退出码。 还有的就是,exec族函数,如让A进程去启用B进程,A后面的代码就不跑了,去跑的。 但综上都不是真正意义上的通信,真正的通信,通过一进程写入信息,二进程可以读取,二进程写入信息,一进程可以读取。

进程间通信(IPC)介绍 进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。 IPC的方式通常由管道(包括无名管道和命名管道)、消息队列、信号量、共享储存,Socket、Streams等。其中Socket和Stream支持不同主机上的两个进程IPC 。

二、管道通信原理

管道,通常指无名管道

1、特点

            它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。

            它只能用于具有亲缘关系的进程之间的通信(也就是父子进程或者兄弟进程之间。)

           它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。 当父子进程退出后,管道就消失了,不会在磁盘中存在。 同时只能有父进程读,子进程写,或者交换,而不能同时父子进程都读或写。 (管道中数据,读走就没了,管道中不储存数据)

2、pipe函数原型:

#include <unistd.h>

int pipe(int pipefd[2]);

返回值:若成功返回0,失败返回-1.

当一个管道建立时,它会创建两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。 要关闭管道只需将这两个文件描述符关闭即可。

(当调用fork创建进程后,不知道先运行父进程还是子进程。如果是先运行子进程,没有写入,read没有数据,read会堵塞。这时候CPU就会被父进程争夺到)

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

//int pipe(int pipefd[2]);
//ssize_t  write(int  fd, const void *buf, size_t count);
//ssize_t read(int fd, void *buf, size_t count);


int main()
{
	int pipefd[2];
	int retpid;
	char buf[128]={0};

	if(pipe(pipefd) == -1)	
	{
		printf("pipe fail\n");
	}
	retpid = fork();
	if(retpid == 0)
	{
		printf("child process ...\n");
		close(pipefd[1]);
		read(pipefd[0],buf,128);
		printf("child read:(%s)\n",buf);
		exit(0);
	}else if(retpid > 0)
	{
		sleep(3);    //父进程睡眠等待3s,子进程先进行发现read读取不到数据,因此子进程会堵塞在read那,知道读取到数据
		printf("father process ...\n");
		close(pipefd[0]);
		write(pipefd[1],"From father:hello!",strlen("From father:hello!"));
		wait(NULL);

	}else{
		printf("creat child fail\n");
	}

	return 0;
}

 FIFO,也称为命名管道,它是一种文件类型

1、特点:

1. FIFO可以在无关的进程之间交换数据,与无名管道不同。

2. FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

一旦创建了一个 FIFO,就可以用一般的文件I/O函数操作它。

2、函数原型

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

参数:        

        mkfifo ()会依参数pathname建立特殊的FIFO文件,该文件必须不存在。然后创建一个该路径下的FIFO文件。

其中的 mode 参数与open函数中的 mode 相同。

返回值:若成功则返回0, 否则返回-1, 错误原因存于errno 中。

open默认没有指定非阻塞(O_NONBLOCK),即不设置O_NONBLOCK的话,默认open要阻塞,只读的open要阻塞到某一个其他进程为写而打开此FIFO。类似,只写 open 要阻塞到某个其他进程为读而打开它。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

//
//int mkfifo(const char *pathname, mode_t mode);
//


int main()
{
	if((mkfifo("./fifo",0666) == -1) && errno!=EEXIST )mkfifo目的是创建fifo,若fifo已经存在,则会报错,该判断保证调用失败且原因不是因为fifo文件已经存在导致的。 
	{
		printf("mkfifo fail\n");
		perror("reason");
	}

	if(open("./fifo",O_RDONLY) < 0)
	{
		printf("open fail\n");
		perror("reason:");
	}
	printf("open successfully\n");

	return 0;
}

if((mkfifo("./fifo",0666) == -1) && errno!=EEXIST ) //mkfifo目的是创建fifo,若管道fifo已经存在,则会报错,该判断保证调用失败且原因不是因为fifo文件已经存在导致的。 

 如图,open堵塞住,printf("open successfully\n");并未执行。

因此要在另一个执行程序进程为写而打开此FIFO后该进程才会继续进行。

代码 fiforead.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>

//
//int mkfifo(const char *pathname, mode_t mode);
//


int main()
{
	char readbuf[128];
	int n = 0;
	if((mkfifo("./fifo",0666) == -1) && errno!=EEXIST )
	{
		printf("mkfifo fail\n");
		perror("reason");
	}

	int fd = open("./fifo",O_RDONLY);
	if(fd < 0)
	{
		printf("open fail\n");
		perror("reason:");
	}
	printf("open successfully\n");

	while(1)
	{

		if(read(fd,readbuf,128) < 0)
		{
			printf("read fail\n");
			perror("reason");
		}
		printf("context:%s\n",readbuf);
		if(n++ == 5)
			break;
		sleep(1);
	}

	close(fd);
	return 0;
}

代码fifowrite.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

int main()
{
	char *writebuf = "hello from fifo!";
	int fd = open("./fifo",O_WRONLY);
	int n = 0;

	if(fd < 0)
	{
		printf("open fail\n");
		perror("reason");
	}
	while(1)
	{
		if(write(fd,writebuf,strlen(writebuf)) < 0)
		{
			printf("write fail\n");
			perror("reason");
		}else{
			if(n++ == 5)
			{
				printf("write successful\n");
				break;
			}
		}
		sleep(1);
	}

	close(fd);
	return 0;
}

二、System V 进程间通信

System V IPC (Inter-Processommunication,进程间通信) 包括三种进程通信方式,即消息队列、信号量和共享内存。这三种方式完全被Linux系统继承和兼容。

1.IPC的键值

        消息队列、信号灯、共享内存常用在Linux服务端编程的进程间通信环境中。而此三类编程函数在实际项目中都是用System V IPC函数实现的。

IPC 函数名称和说明如下:

1.1 key_t键和ftok函数

        函数ftok把一个已存在的路径名和一个整数标识符转换成一个key_t值,称为IPC键值(也称IPC key键值) 。ftok函数原型及说明如下:

 ftok的典型实现是调用stat函数,然后组合以下三个值:① pathname所在的文件系统的信息(stat结构的st_dev成员) ②该文件在本文件系统内的索引节点号(stat结构的st_ino成员) ③proj_id的低序8位(不能为 0)上述三个值的组合产生一个 32 位键。

→关于Linux下的stat()函数:

头文件

#include <sys/stat.h>
#include <unistd.h>

定义函数:    int stat(const char *file_name, struct stat *buf);
函数说明: 通过文件名filename获取文件信息,并保存在buf所指的结构体stat中
返回值:     执行成功则返回0,失败返回-1,错误代码存于errno
错误代码errno:
    ENOENT         参数file_name指定的文件不存在
    ENOTDIR        路径中的目录存在但却非真正的目录
    ELOOP          欲打开的文件有过多符号连接问题,上限为16符号连接
    EFAULT         参数buf为无效指针,指向无法存在的内存空间
    EACCESS        存取文件时被拒绝
    ENOMEM         核心内存不足
    ENAMETOOLONG   参数file_name的路径名称太长

结构体内部

struct stat
{
    dev_t     st_dev;     /* ID of device containing file */文件使用的设备号
    ino_t     st_ino;     /* inode number */    索引节点号 
    mode_t    st_mode;    /* protection */  文件对应的模式,文件,目录等
    nlink_t   st_nlink;   /* number of hard links */    文件的硬连接数  
    uid_t     st_uid;     /* user ID of owner */    所有者用户识别号
    gid_t     st_gid;     /* group ID of owner */   组识别号  
    dev_t     st_rdev;    /* device ID (if special file) */ 设备文件的设备号
    off_t     st_size;    /* total size, in bytes */ 以字节为单位的文件容量   
    blksize_t st_blksize; /* blocksize for file system I/O */ 包含该文件的磁盘块的大小   
    blkcnt_t  st_blocks;  /* number of 512B blocks allocated */ 该文件所占的磁盘块  
    time_t    st_atime;   /* time of last access */ 最后一次访问该文件的时间   
    time_t    st_mtime;   /* time of last modification */ /最后一次修改该文件的时间   
    time_t    st_ctime;   /* time of last status change */ 最后一次改变该文件状态的时间   
};
具体使用可查看原文链接:https://blog.csdn.net/weixin_51447909/article/details/115281943

代码举例:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>

//int stat(const char *pathname, struct stat *buf);
//
//



int main(int argc,char *argv[])
{
	if(argc != 2)
	{
		printf("usage:ftok (pathname)\n");
		return -1;
	}
	struct stat stat1;
	if(stat(argv[1],&stat1) == -1)
	{
		perror("stat fail reason");
	}

	printf("%s st_dev:%lx, st_ino:%lx, key:%x\n",argv[1],(unsigned long)stat1.st_dev,(unsigned long)stat1.st_ino,ftok(argv[1],0x01));
	printf("%s st_dev:%lx, st_ino:%lx, key:%x\n",argv[1],(unsigned long)stat1.st_dev,(unsigned long)stat1.st_ino,ftok(argv[1],0x02));
	printf("%s st_dev:%lx, st_ino:%lx, key:%x\n",argv[1],(unsigned long)stat1.st_dev,(unsigned long)stat1.st_ino,ftok(argv[1],0x03));

	return 0;
}

通过ftok返回的是根据文件(pathname) 信息和计划编号(proj_id) 合成的IPC key键值,从而避免用户使用key值的冲突proj_id值的意义让一个文件也能生成多个IPC key键值(因为由上述可知同一文件前两个值是相同的)。ftok利用同一文件最多可得到IPC key键值 Oxff (即 256) 个,因为ftok只取proj_id值二进制的后8 位即16 进制的后两位与文件信息合成IPC key键值

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值