Linux系统编程:进程part_2(信号相关)

前言

这一节内容我没怎么认真写,就是纯当草草过了一遍,这部分不是很重要当然能掌握肯定更好。
更多的是有个印象然后知道遇到这样的问题能回想起来知道怎么解决即可(虽然不太可能遇到)。

信号量

在这里插入图片描述

实现PV操作

P:测试并加锁,sem <= 0 会阻塞,但如果sem > 0的话 就 --sem

V:解锁,即++sem

实现PV操作可以分为几步,首先定义PV操作然后是调用PV操作。

定义PV操作需要用到的系统调用为:
在这里插入图片描述
对该系统调用的一些解释:
在这里插入图片描述
信号量的简单使用:
在这里插入图片描述
SEM_UNDO的作用是在进程终止的时候,把减去的资源给加回来,这个在下面的生产者消费者问题中有所体现。

生产者消费者问题

在考研课程还有什么哲学家进餐问题乱七八糟的,这些只对考研有用,在实际工作生产中,只要懂消费者生产者问题即可,所以这个很重要,利用之前学过的知识很容易写出这样的代码:

#include <43func.h>
int main()
{
    int shmid = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0600);
    ERROR_CHECK(shmid, -1, "shmget");
    int *p = (int *)shmat(shmid, NULL, 0);
    ERROR_CHECK(p, (void *)-1, "shmat");
    p[0] = 10; // p[0] 表示仓库的个数
    p[1] = 0;  // p[1] 表示商品的个数
    int semid = semget(1000, 1, IPC_CREAT | 0600);
    ERROR_CHECK(semid, -1, "semget");
    int ret = semctl(semid, 0, SETVAL, 1);
    ERROR_CHECK(ret, -1, "semctl SETVAL");
    ret = semctl(semid, 0, GETVAL);
    ERROR_CHECK(ret, -1, "semctl GETVAL");
    printf("semval = %d\n", ret);
    struct sembuf P, V;
    P.sem_num = 0; //下标
    P.sem_op = -1; //对资源的影响
    P.sem_flg = SEM_UNDO;
    V.sem_num = 0;
    V.sem_op = 1;
    V.sem_flg = SEM_UNDO;
    if (fork() == 0)
    {
        while (1)
        {
            semop(semid, &P, 1);
            if (p[0] > 0)
            {
                printf("before produce, space = %2d, good = %2d, total = %d\n", p[0], p[1], p[0] + p[1]);
                --p[0];
                ++p[1];
                printf("after produce, space = %2d, good = %2d, total = %d\n", p[0], p[1], p[0] + p[1]);
            }
            semop(semid, &V, 1);
            // sleep(1);
        }
    }
    else if (fork() == 0)
    {
        while (1)
        {
            semop(semid, &P, 1);
            if (p[0] > 0)
            {
                printf("before produce, space = %2d, good = %2d, total = %d\n", p[0], p[1], p[0] + p[1]);
                --p[0];
                ++p[1];
                printf("after produce, space = %2d, good = %2d, total = %d\n", p[0], p[1], p[0] + p[1]);
            }
            semop(semid, &V, 1);
            // sleep(1);
        }
    }
    else
    {
        while (1)
        {
            semop(semid, &P, 1);
            if (p[1] > 0)
            {
                printf("before consume , space = %2d, good = %2d, total = %d\n", p[0], p[1], p[0] + p[1]);
                --p[1];
                ++p[0];
                printf("after consume, space = %2d, good = %2d, total = %d\n", p[0], p[1], p[0] + p[1]);
            }
            semop(semid, &V, 1);
            // usleep(100000);
        }
        wait(NULL);
    }
    shmdt(p);
    shmctl(shmid, IPC_RMID, NULL);
}

写这种PV操作时一定要分析清楚临界资源有哪些,一定要把所有对共享资源的访问保护到。

但是还是老话,实际工作当中我们不会用这样的方式,会用一些更加高效的手段,这后面再说。

消息队列

所谓消息队列其实有两种,我们这里说的是在进程间通信的消息队列,是一种IPC机制,而另一种消息队列是广义消息队列,它是一种网络通信的中间件,比如RacketMq之类的。

这里说的消息队列和之前说的管道其实非常相似,区别在于消息队列可以保留消息的边界。

什么意思?

先来说管道没有保留消息边界的含义:
比如我们用write系统调用写了两句消息,“你好”,“坏人”,通过管道传输给另一端,因为管道是流式消息,它根据先进先出原则在管道另一头的消息是:“人”“坏”“好”“你”,在管道另一边我们可以选择直接全部取出来也可以选择只读几个字,假如读三个字的话:“你好坏”,最后只剩个“人”了,消息含义就变了,
这就是由于管道没有保留消息边界所造成的:
在这里插入图片描述
后面学习网络编程时TCP也是流式传送数据的。

消息队列的做法则不同,还是以上面说的为例,发送方发送你好坏人,每一条消息都会以数据包的形式发送,会一个包一个包的发送给另一端,另一端自然只能一个包一个包的接收,这样就可以保留消息边界:
在这里插入图片描述
后面学习网络编程时UDP也是按数据包的形式传送的。

我们通过msgget系统调用来创建消息队列,注意介绍的依然是System V标准的系统调用,因为Posix标准的太难用:
在这里插入图片描述
对该系统调用的一些解析(注意消息队列是先进先出的):
在这里插入图片描述
代码简单测试:
发送数据的程序:

#include <43func.h>
typedef struct msgbuf{
    long mtype;
    char mtext[256];
} myMsg_t;
int main(){
    int msqid = msgget(1000,IPC_CREAT|0600);
    ERROR_CHECK(msqid,-1,"msgget");
    myMsg_t msg1;//Huangxiaoming
    myMsg_t msg2;//Wuyifan
    myMsg_t msg3;//Caixukun
    msg1.mtype = 1;
    strcpy(msg1.mtext,"Ganenguoqusuoyou,weilairenshijiaren");
    msg2.mtype = 2;
    strcpy(msg2.mtext,"skr skr~");
    msg3.mtype = 3;
    strcpy(msg3.mtext,"jinitaimei");
    msgsnd(msqid,&msg1,strlen(msg1.mtext), 0);
    msgsnd(msqid,&msg2,strlen(msg2.mtext), 0);
    msgsnd(msqid,&msg3,strlen(msg3.mtext), 0);
    puts("send over");

}

接收数据的程序:

#include <43func.h>
typedef struct msgbuf{
    long mtype;
    char mtext[256];
} myMsg_t;
int main(){
    int msqid = msgget(1000,IPC_CREAT|0600);
    ERROR_CHECK(msqid,-1,"msgget");
    long type;
    printf("who are you? 1 huangxiaoming 2 wuyifan 3 caixukun\n");
    scanf("%ld",&type);
    myMsg_t msg;
    memset(&msg,0,sizeof(msg));
    //msgrcv(msqid,&msg,sizeof(msg.mtext),type,0);
    //msgrcv(msqid,&msg,sizeof(msg.mtext),0,0);
    int ret = msgrcv(msqid,&msg,sizeof(msg.mtext),0,IPC_NOWAIT);
    ERROR_CHECK(ret,-1,"msgrcv");
    printf("you are %ld, msg = %s\n", type, msg.mtext);
}

proc文件系统

我们来介绍一下这个东西,我们在根目录下打开可以看见该文件目录,cd进行展示会发现很多文件大小都是0:
在这里插入图片描述
这是因为proc文件系统不是一个真正的磁盘系统,而是一个伪文件系统。

proc是process的缩写,所以proc文件系统的内容是操作系统的运行状态在文件系统中的映射。

这样做的好处是可以像修改文件一样去修改操作系统的属性。

proc文件目录下面的数字开头的内容其实对应的就是OS中的每一个进程,数字代表进程的PID号。

在这里插入图片描述
我们还可以进入这个sys文件目录下,即系统进程下去查看很多信息:
在这里插入图片描述
这里面有个kernel内核信息,我们可以在内核文件夹里去修改很多的内容,比如改共享内存的内容啊改消息队列的内容啊之类的。

信号

信号是一种软件层面的异步事件机制。

信号可能是进程发给进程的,也有可能是操作系统发给进程的。

而与之对应的在硬件层面的异步事件机制是中断。

使用man 7 signal命令查看信号手册:
在这里插入图片描述

信号默认行为

在这里插入图片描述

从上到下分别是:终止、忽略、终止并生成core、暂停、恢复。

在这里插入图片描述
可以看见上面的每个信号都会对应一个默认的行为。

我们学习这一节的内容,就是为了实现更改默认的信号行为,所以上面的内容只是铺垫。

信号产生的时机

在这里插入图片描述
进程或者操作系统或者硬件产生信号,然后递送到目标进程,中间会有一个传送的时间间隔,我们接下来的事情不会去管产生信号的行为,而是去修改传送信号的时间以及目标进程递送信号的行为。

当信号产生时

在这里插入图片描述

更改默认的信号行为

现在我们试图让信号递送时不再执行默认操作,而是调用一个函数。

这里我们可以用signal系统调用看看,用man 2 signal命令查看其man手册:
在这里插入图片描述
这个系统调用的作用是用来注册一个信号处理行为,注册的含义是等到信号到来时才会调用。
简单使用代码测试一下:
在这里插入图片描述

低速系统调用

低速系统调用是指可能陷入永久等待的系统调用。

在这里插入图片描述
signal特点之一就是一次注册,永久生效,如何改变这种永久生效的效果为让注册只生效一次?
可以使用SIG_DFL,设置其为默认的行为模式:
在这里插入图片描述
特点之二时,递送A时,会将A假如mask,其它信号不会加入mask,且会自动重启低速系统调用。

为了更好的控制上述特点,我们可以使用sigaction来替代signal:
在这里插入图片描述
虽然sigaction可以更好的控制信号,但是也更加复杂了,主要体现在其参数结构体的设计上:
在这里插入图片描述
简单测试:

#include <43func.h>
void sigFunc(int num){
    printf("before, num = %d\n", num);
    sleep(3);
    printf("after, num = %d\n", num);
}
void sigFunc3(int num, siginfo_t *siginfo , void * p){
    printf("num = %d\n", num);
    printf("sender pid = %d\n", siginfo->si_pid);
}
int main(){
    struct sigaction act;
    memset(&act,0,sizeof(act));
    act.sa_handler = sigFunc;
    //act.sa_sigaction = sigFunc3;
    //act.sa_flags = SA_RESTART|SA_SIGINFO|SA_RESETHAND;
    //act.sa_flags = SA_RESTART|SA_NODEFER;
    act.sa_flags = SA_RESTART;
    sigaddset(&act.sa_mask,SIGQUIT);
    int ret = sigaction(SIGINT,&act,NULL);
    ERROR_CHECK(ret,-1,"sigaction");
    //ret = sigaction(SIGQUIT,&act,NULL);
    //ERROR_CHECK(ret,-1,"sigaction");
    char buf[100] = {0};
    read(STDIN_FILENO,buf,sizeof(buf));
    printf("buf = %s\n", buf);
    //while (1)
    //{
    //}
    
}

sa_mask

在这里插入图片描述

sa_mask用来指定递送过程中的额外屏蔽信号

在这里插入图片描述

sigprocmask实现全程阻塞

在这里插入图片描述
简单测试:
在这里插入图片描述
运行结果:
在这里插入图片描述

获取pending集合

在这里插入图片描述

pause等待信号

在这里插入图片描述

kill发送信号

在这里插入图片描述

简单代码测试:
在这里插入图片描述
运行结果:
在这里插入图片描述
使用raise系统调用可以给自己发信号:
在这里插入图片描述
简单的代码测试:
在这里插入图片描述
运行效果:
在这里插入图片描述
在实现有序退出时该函数还是有用的。

alarm系统调用:定闹钟

在这里插入图片描述

可以简单的使用一下:
在这里插入图片描述

时钟

在这里插入图片描述

在这里插入图片描述
简单使用:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
#include<iostream> #include<ctime> #include<chrono> #include<string> #include<filesystem> #include<fstream> #include<sstream> #include<thread> #include<boost/filesystem.hpp> const uintmax_t MAX_LOGS_SIZE = 10ull * 1024ull * 1024ull * 1024ull; //const uintmax_t MAX_LOGS_SIZE = 10ull; void create_folder(std::string folder_name) { boost::filesystem::create_directory(folder_name); std::string sub_foldername=folder_name+"/logs_ros"; boost::filesystem::create_directory(sub_foldername); } std::string get_current_time() { auto now = std::chrono::system_clock::now(); std::time_t now_c = std::chrono::system_clock::to_time_t(now); std::tm parts = *std::localtime(&now_c); char buffer[20]; std::strftime(buffer, sizeof(buffer), "%Y-%m-%d-%H-%M", &parts); return buffer; } void check_logs_size() { std::string logs_path = "/home/sage/logs/"; boost::filesystem::path logs_dir(logs_path); std::uintmax_t total_size = 0; for (const auto& file : boost::filesystem::recursive_directory_iterator(logs_dir)) { if (boost::filesystem::is_regular_file(file)) { total_size += boost::filesystem::file_size(file); } } if (total_size > MAX_LOGS_SIZE) { boost::filesystem::path earliest_dir; std::time_t earliest_time = std::time(nullptr); for (const auto& dir : boost::filesystem::directory_iterator(logs_dir)) { if (boost::filesystem::is_directory(dir)) { std::string dir_name = dir.path().filename().string(); std::tm time_parts = {}; std::istringstream ss(dir_name); std::string part; std::getline(ss, part, '-'); time_parts.tm_year = std::stoi(part) - 1900; std::getline(ss, part, '-'); time_parts.tm_mon = std::stoi(part) - 1; std::getline(ss, part, '-'); time_parts.tm_mday = std::stoi(part); std::getline(ss, part, '-'); time_parts.tm_hour = std::stoi(part); std::getline(ss, part, '-'); time_parts.tm_min = std::stoi(part); std::time_t dir_time = std::mktime(&time_parts); if (dir_time < earliest_time) { earliest_time = dir_time; earliest_dir = dir.path(); } } } if (!earliest_dir.empty()) { boost::filesystem::remove_all(earliest_dir); } } } int main() { std::string logs_path = "/home/sage/logs/"; while (true) { std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); std::time_t now_c = std::chrono::system_clock::to_time_t(now); std::tm parts = *std::localtime(&now_c); if (parts.tm_min % 10 == 0) { std::string folder_name = logs_path + get_current_time(); create_folder(folder_name); } check_logs_size(); std::this_thread::sleep_for(std::chrono::minutes(1)); } return 0; }修改为ros节点
06-09

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

在地球迷路的怪兽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值