10.共享内存
10.1 基本概念
这种进程之前通信的速度更快,因为是直接操作内存,内核的参与度更少。
如果是管道:先将要传输的数据从管道中拷贝到内核区,然后内核1将内核中的数据发送给内核2。内核2再将数据拷贝到进程2的用户区中所以处理速度比较慢。
并且可以实现多个进程之间的内存共享
10.2 共享内存调用步骤
1.创建共享内存并获得标识符
2.将这块共享内存添加到调用进程的虚拟内存中
3.得到这个共享内存在虚拟内存中的起始位置
////// 开始使用共享内存,直到使用完毕
4.将共享内存与进程分离,这时候别的进程可能还在使用,不能立即删除
5.销毁共享内存
10.3 共享内存相关函数
1.API



2.代码
创建了两个文件, write_shm.c 创建共享内存并向共享内存中写入数据,read_shm.c 从共享内存中读取数据,读取完数据之后接着释放共享内存。而对于 write_shm.c 来说,它只能等到 read 程序读取完数据后再释放共享内存的占有
不论是创建还是直接调用共享内存,所有的进程都需要按顺序完成上面的步骤
(1)write_shm.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
int main(){
//1. 创建共享内存
int shmid = shmget(100,4096,IPC_CREAT|0664); // 内存段标志为 100
// 2.将共享内存添加到虚拟空间中
void* ptr = shmat(shmid,NULL,0); // 返回共享内存的首地址
//3.定义相关共享内存的操作
char * s = "hello world";
memcpy(ptr,s,strlen(s)+1); // 将数据从一个地址拷贝到另一个地址
printf("按任意键继续\n");
getchar();
// 4.写完数据之后解除关联
shmdt(ptr);
// 5.删除共享内存
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
(2) read_shm.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
int main(){
// 1.获取共享内存
int shmid = shmget(100,0,IPC_CREAT); // 使用和刚才定义时相同的 id ,因为这里是获取所以大小为 0 ,最后可以不指定权限
// 2.将共享内存映射
void * ptr = shmat(shmid,NULL,0);
//3.读数据操作
printf("%s\n",(char*)ptr);
// 4.解除关联
shmdt(ptr);
// 5.删除共享内存
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
3.代码演示
4.ftok 的使用
因为前面共享内存的 id 都是自己指定的,比如说 100 ,使用上面的函数可以将让系统为自己制定一个系统共享内存 id
10.4 共享内存的相关问题
10.4.1 共享内存的关联进程信息

可以使用上面代码的方式查看,也可以使用下面命令行的方式查看
比较常用的指令:
ipcs -m 查看有所有共享内存,并查看有多少个进程正在对其进行占用
ipcrm -m shmid 删除某个共享内存段
10.4.2 共享内存是否可以多次删除

10.4.3 共享内存和内存映射的区别
匿名映射只能在两个有关系的进程之间通信
重要:11.守护进程
11.1 终端,进程组,会话之间的关系
11.1.1 终端
查看当前终端的进程号,一个终端是一个进程,终端不一样 pid 不一样
echo $$ // 在命令行输入
11.1.2 进程组
11.1.3 会话
11.1.4 进程组,会话,控制终端之间的关系
我们一共执行两个指令,因为第一个指令的最后加了一个 &,第一个指令执行在后端, ,第二个指令执行在前端。
1.我们会有一个 bash 指令也就是控制终端,它的进程组为它的 id 号 400 ,这个进程组中还包含着它的父进程。
与此同时 bash 还会创建一个会话,会话的 id 也是 bash 的 id 400
2.其次我们执行 find 指令,find 指令是一个进程,它的id 是 658,并且 find 指令创建了一个进程组,这个进程组的组 id 就是第一个创建进程的进程 id 658,它是在会话 400 中的。
wc 是 find 拼接的一个进程,所以它也在进程组 658 中同样也在 400 这个会话中
3.sort 和 uniq 又是两个进程,因为在同一个命令中所以他们属于同一个进程组,又因为他们在同一个 bash 中被创建所以他们属于同一个会话
同一个命令行被创建的两个进程属于同一个进程组,同一个 bash 被创建的两个命令行属于同一个会话
11.1.5 关于进程组和会话的相关函数
获取当前的组 id
获得某个进程的组 id
设置某个进程的进程组 id
获取进程的会话 id
设置某个进程的会话 id
11.2 守护进程
11.2.1 守护进程定义
守护进程是一直存在的,在后台执行的一种特殊进程,他不是由控制台发起的,一般用于周期性的完成某种任务
11.2.2 创建一个守护进程
代码保存在 lesson28 daemon.c 中
1.API
1.为什么要创建子进程杀死父进程
为了防止这是一个组长进程。会话是根据进程组的名字定义的。一个首进程的进程号为 100 ,他的进程组也是 100 ,通过这个进程组创建一个会话,会话的 id 也是 100 ,进程组也是 100 ,在这样的情况下就包含了在不同的会话中两个进程组号都为 100 的进程组,发生了冲突。
2.为什么子进程要开启一个新会话
因为子进程是由父进程创建的,所在的会话也是父进程所在会话,也就是 bash 当中。但是因为这是守护进程是不能被 bash 控制台杀死的,所以要让子进程重新创建一个会话
最终就是使用父进程的进程组去创建一个属于子进程的会话
3.重定向输入输出文件的描述符
因为守护进程和控制台是相互独立的,所以要将守护进程的输入输出重定向到某个文件中
4.如何杀死守护进程
kill -9 守护进程id
2.代码
这里实现创建一个守护进程,守护进程可以每隔 2 s 就向一个文件写入当前时间。这个进程没有办法使用 Ctrl+c 的方法进行终止
(0)导入相应的库
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <signal.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
(1) 杀死子进程,创建父进程
// 1.使用 fork 指令创建相应的子进程
pid_t pid = fork();
// 杀死父进程
if(pid>0) exit(0);
(2)在子进程中创建会话
setsid();
(3) 设置掩码,让子进程同父进程一样拥有相应的权限
// 3.设置掩码
umask(022);
(4) 更改当前工作目录,一般为 root 目录,这里设置为当前目录
// 4.更进程改工作目录
chdir("/home/C++/lesson28");
(5)关闭进程输入输出重定向,不再输入输出到控制台
// 5.关闭,重定向文件描述符脱离终端
int fd = open("/dev/null", O_RDWR);
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
(6) 相关业务逻辑
定义定时器,每隔两秒中断一次进程,转向执行相关的时间读写操作
// 6.业务逻辑
// 捕捉定时信号,先定义信号捕捉,再定义信号
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = work; // 定义信号的处理操作
sigemptyset(&act.sa_mask);
sigaction(SIGALRM, &act, NULL);
// 设置定时器,每隔两秒运行一次
struct itimerval val;
val.it_value.tv_sec = 2;
val.it_value.tv_usec = 0;
val.it_interval.tv_sec = 2;
val.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &val, NULL); // 创建定时器
对中断的处理操作
void work(int num) {
// 捕捉到信号之后,获取系统时间,写入磁盘文件
time_t tm = time(NULL);
struct tm * loc = localtime(&tm);
char * str = asctime(loc);
int fd = open("time.txt", O_RDWR | O_CREAT | O_APPEND, 0664); // 需要对文件内容进行追加
write(fd ,str, strlen(str));
close(fd);
}
(7) 使用 while 循环使得守护进程一直执行
while(1);
3.代码演示
本文详细介绍了共享内存的概念、调用步骤和相关函数,通过实例展示了如何在C语言中创建和使用共享内存进行进程间的通信。此外,还探讨了守护进程的定义、创建过程以及其与会话、进程组的关系,提供了创建守护进程的代码示例。
1360

被折叠的 条评论
为什么被折叠?



