5.进程相关3

本文详细介绍了共享内存的概念、调用步骤和相关函数,通过实例展示了如何在C语言中创建和使用共享内存进行进程间的通信。此外,还探讨了守护进程的定义、创建过程以及其与会话、进程组的关系,提供了创建守护进程的代码示例。

10.共享内存

10.1 基本概念

image-20211117140602038

这种进程之前通信的速度更快,因为是直接操作内存,内核的参与度更少。

​ 如果是管道:先将要传输的数据从管道中拷贝到内核区,然后内核1将内核中的数据发送给内核2。内核2再将数据拷贝到进程2的用户区中所以处理速度比较慢。

并且可以实现多个进程之间的内存共享

10.2 共享内存调用步骤

image-20211117141424831

1.创建共享内存并获得标识符

2.将这块共享内存添加到调用进程的虚拟内存中

3.得到这个共享内存在虚拟内存中的起始位置

////// 开始使用共享内存,直到使用完毕

4.将共享内存与进程分离,这时候别的进程可能还在使用,不能立即删除

5.销毁共享内存

10.3 共享内存相关函数

image-20211117141818674

1.API

image-20211117142429096

image-20211117142441510

image-20211117142455427

image-20211118104748135

image-20211117142516297

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.代码演示

image-20211118113742637

4.ftok 的使用

因为前面共享内存的 id 都是自己指定的,比如说 100 ,使用上面的函数可以将让系统为自己制定一个系统共享内存 id

10.4 共享内存的相关问题

10.4.1 共享内存的关联进程信息

image-20211118121122249

可以使用上面代码的方式查看,也可以使用下面命令行的方式查看

image-20211118114546950

比较常用的指令:

ipcs -m 查看有所有共享内存,并查看有多少个进程正在对其进行占用

image-20211118120705737

ipcrm -m shmid 删除某个共享内存段

10.4.2 共享内存是否可以多次删除

image-20211118121310622

10.4.3 共享内存和内存映射的区别

匿名映射只能在两个有关系的进程之间通信

image-20211118121341255

重要:11.守护进程

11.1 终端,进程组,会话之间的关系

11.1.1 终端

image-20211118121845965

查看当前终端的进程号,一个终端是一个进程,终端不一样 pid 不一样

echo $$ // 在命令行输入

11.1.2 进程组

image-20211118122151567

11.1.3 会话

image-20211118122549079

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 被创建的两个命令行属于同一个会话

image-20211118122858608

11.1.5 关于进程组和会话的相关函数

image-20211118124138590

获取当前的组 id

获得某个进程的组 id

设置某个进程的进程组 id

获取进程的会话 id

设置某个进程的会话 id

11.2 守护进程

11.2.1 守护进程定义

守护进程是一直存在的,在后台执行的一种特殊进程,他不是由控制台发起的,一般用于周期性的完成某种任务

image-20211118124447859

11.2.2 创建一个守护进程

代码保存在 lesson28 daemon.c 中

1.API

image-20211118132028707

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.代码演示

image-20211118140525658
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值