目录
共享内存中,一个进程用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 进程间通信
1.IPC的键值
消息队列、信号灯、共享内存常用在Linux服务端编程的进程间通信环境中。而此三类编程函数在实际项目中都是用System V 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键值。