<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>LINKONG&apos;s Blog</title><description>Welcome!</description><link>https://kuchikirei.github.io/</link><language>zh_CN</language><item><title>Linux下通过C++调用POSIX线程库实现经典多线程问题</title><link>https://kuchikirei.github.io/posts/os2/</link><guid isPermaLink="true">https://kuchikirei.github.io/posts/os2/</guid><pubDate>Wed, 17 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%94%9F%E4%BA%A7%E8%80%85%E6%B6%88%E8%B4%B9%E8%80%85%E9%97%AE%E9%A2%98&quot;&gt;生产者消费者问题&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;本质是为了解决多个线程在信号量机制的协调下，实现有序协作，确保在单一时刻内只有一个线程能够访问修改缓冲区内的数据。&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E4%BF%A1%E5%8F%B7%E9%87%8F&quot;&gt;信号量机制&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在计算机中使用原子级操作，实现对计算机资源的计数。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;原子级操作：在计算机的控制下，将内存地址中的数据值进行增加或删除，同时将该步骤封装成为一个完整的步骤，不可被打断，此区间内任何其余操作都将被屏蔽&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;信号量机制下的核心操作&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Somephore&lt;/strong&gt;: 申请计数器，设置计数器的大小。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P&lt;/strong&gt;: 申请操作，申请一个资源，并将资源数量 &lt;strong&gt;-1&lt;/strong&gt;，如果此时资源数量为 &lt;strong&gt;0&lt;/strong&gt;, 那么暂时阻塞该线程。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;V&lt;/strong&gt;: 释放操作, 释放一个资源，并将资源数量 &lt;strong&gt;+1&lt;/strong&gt;，并唤醒因为资源不足而被P操作阻塞的线程。&lt;/p&gt;
&lt;h4&gt;Linux下的POSIX信号量库&lt;/h4&gt;
&lt;p&gt;相关对应操作:&lt;br /&gt;
Somephore: sem_t&lt;br /&gt;
P: sem_wait();&lt;br /&gt;
V: sem_post();&lt;/p&gt;
&lt;h3&gt;生产者消费者问题的详细解决&lt;/h3&gt;
&lt;h1&gt;&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;通过定义当前剩余空位置以及已有位置来定义缓冲区，节约系统资源，无需消耗过多资源来进行资源的判断。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;sem_t full, empty;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;定义互斥锁，确保在一个时刻只有一个线程可以进行操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;sem_t mutex;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在线程执行的过程中，首先需要查看是否拥有足够的相对应的资源，然后申请互斥锁，如果申请成功，那么进行函数段的运行；运行结束后，在进行互斥锁的释放，同时释放生产出来的资源。如此循环直至，因为&lt;strong&gt;P&lt;/strong&gt;操作申请不到足够的资源而阻塞。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;定义生产者函数，申请空闲位置，申请成功后申请互斥锁，在申请到互斥锁后进行函数段内的操作；函数段内的操作完成后释放互斥锁，同时释放生产出的代码。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;void productor(){
    sem_wait(&amp;amp;empty); //只要有空位置就进行申请
    sem_wait(&amp;amp;mutex); //申请互斥锁

    /*
        执行的程序段
    */

    sem_post(&amp;amp;mutex); //释放互斥锁
    sem_post(&amp;amp;full);  //释放生产出来的资源
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;定义消费者函数，申请已有位置，申请成功后申请互斥锁，在申请到互斥锁后进行函数段内的操作；函数段内的操作完成后释放互斥锁，同时释放消耗后的位置。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;void consumer(){
    sem_wait(&amp;amp;full); //只要有产品就进行申请
    sem_wait(&amp;amp;mutex); //申请互斥锁

    /*
        执行的程序段
    */

    sem_post(&amp;amp;mutex); //释放互斥锁
    sem_post(&amp;amp;full);  //释放消耗后的位置
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&lt;/h1&gt;
&lt;h3&gt;初始化&lt;/h3&gt;
&lt;p&gt;使用&lt;code&gt;sem_init&lt;/code&gt;初始化未命名信号量。&lt;br /&gt;
&lt;strong&gt;参考格式&lt;/strong&gt;:&lt;code&gt;sem_init(sem_t *sem, int pshared, unsigned int value)&lt;/code&gt;&lt;br /&gt;
&lt;strong&gt;sem&lt;/strong&gt;: 申请的信号量;&lt;br /&gt;
&lt;strong&gt;pshared&lt;/strong&gt;: 控制信号量的范围,(&lt;strong&gt;0&lt;/strong&gt;代表线程间共享，&lt;strong&gt;非0&lt;/strong&gt;代表进程间共享)。&lt;br /&gt;
&lt;strong&gt;value&lt;/strong&gt;: 信号量的初始值。&lt;br /&gt;
在&lt;strong&gt;main&lt;/strong&gt;函数中对缓冲区以及互斥锁进行初始化:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sem_init(&amp;amp;mutex, 0, 1); //初始化互斥锁
sem_init(&amp;amp;full, 0, 0);  //初始化已有产品
sem_init(&amp;amp;empty, 0, N); //初始化空位置的值为N
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;将代码段封装成为单个线程&lt;/h3&gt;
&lt;p&gt;添加头文件库&lt;code&gt;phread.h&lt;/code&gt;。&lt;/p&gt;
&lt;h4&gt;创建线程&lt;/h4&gt;
&lt;p&gt;使用&lt;code&gt;pshared_t&lt;/code&gt;创建线程。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pshared_t p, c; //使用pshared_t作变量类型，创建两个线程p,c
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;对已有代码段进行封装&lt;/h5&gt;
&lt;p&gt;对原有代码段进行更改：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void *productor(void *arg){
    while(true){
        sem_wait(&amp;amp;empty);
        sem_wait(&amp;amp;mutex);

        /*
            执行的程序段
        */

        sem_post(&amp;amp;mutex);
        sem_post(&amp;amp;full);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;较原有代码更改后对函数类型进行了更改，重定义为&lt;code&gt;void*&lt;/code&gt;，同时允许接收一个参数&lt;code&gt;arg&lt;/code&gt;，代表需要使用该程序段的线程，允许函数的复用。&lt;br /&gt;
在&lt;code&gt;main&lt;/code&gt;中使用&apos;pshared_create()&apos;对代码段进行封装。&lt;br /&gt;
&lt;strong&gt;参考格式&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pthread_create(pthread_t *thread, const pthread_attr_t *attr, void*(*start_routine)(void*), void *arg),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;thread&lt;/strong&gt;: 线程名称。&lt;br /&gt;
&lt;strong&gt;attr&lt;/strong&gt;: 线程属性，通常使用&lt;code&gt;NULL&lt;/code&gt;传递默认参数。
&lt;strong&gt;start_routine&lt;/strong&gt;: 接收封装完成的线程函数，该函数类型必须为&lt;code&gt;void*&lt;/code&gt;。&lt;br /&gt;
&lt;strong&gt;arg&lt;/strong&gt;: 传递给线程的参数，无则填&lt;code&gt;NULL&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pthread_creat(p, NULL, productor, NULL);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;完成对&lt;strong&gt;生产者&lt;/strong&gt;函数的封装，同理完成对消费者函数的封装。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void *consumer(void *arg){
    while(true){
        sem_wait(&amp;amp;full);
        sem_wait(&amp;amp;mutex);

        /*
            执行的程序段
        */

        sem_post(&amp;amp;mutex);
        sem_post(&amp;amp;empty);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在main中封装:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pthread(c, NULL, consumer, NULL);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;为保证程序健壮性，设置监管程序:
&lt;code&gt;pthread_join()&lt;/code&gt;，用于在线程结束时回收资源。&lt;br /&gt;
&lt;strong&gt;参考格式&lt;/strong&gt;:&lt;code&gt;pthread_join(pthread_t thread, void **retval)&lt;/code&gt;。&lt;br /&gt;
&lt;strong&gt;retval&lt;/strong&gt;：用于接收可能存在的&lt;code&gt;void *&lt;/code&gt;类型的返回值(没有就是&lt;code&gt;NULL&lt;/code&gt;)。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pthread_join(c, NULL);
pthread_join(p, NULL);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;参考实例&lt;/h3&gt;
&lt;p&gt;附完整的伪代码:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;thread.h&amp;gt;
#include &amp;lt;semaphore.h&amp;gt;

#define N 5

sem_t mutex, full, empty;

void *productor(void *arg){
    while(true){
        sem_wait(&amp;amp;empty);
        sem_wait(&amp;amp;mutex);

        //执行的代码段

        sem_post(&amp;amp;mutex);
        sem_post(&amp;amp;full);
    }
}

void *consumer(void *arg){
    while(true){
        sem_wait(&amp;amp;full);
        sem_wait(&amp;amp;mutex);

        //执行的代码段

        sem_post(&amp;amp;mutex);
        sem_post(&amp;amp;empty);
    }
}

int main(){
    sem_init(&amp;amp;mutex, 0, 1);
    sem_init(&amp;amp;full, 0, 0);
    sem_init(&amp;amp;empty, 0, N);

    pthread_t p, c;
    pthread_create(p, NULL, productor, NULL);
    pthread_create(c, NULL, consumor, NULL);

    pthread_join(p, NULL);
    pthread_join(c, NULL);
}&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>C/C++进程的基础操作</title><link>https://kuchikirei.github.io/posts/os1/</link><guid isPermaLink="true">https://kuchikirei.github.io/posts/os1/</guid><pubDate>Mon, 15 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;新进程的创建&lt;/h3&gt;
&lt;p&gt;调用&lt;code&gt;unistd.h&lt;/code&gt;库中的&lt;code&gt;fork()&lt;/code&gt;语句,会创建一个新的子进程，在单核中，CPU的调度列表上会有两个进程，原有的&lt;strong&gt;父进程&lt;/strong&gt;与新创建出的&lt;strong&gt;子进程&lt;/strong&gt;，两进程并发执行，互不干扰；但是在子进程中，子进程会进行标记，将自身PID标记成为0，用于身份区分。&lt;/p&gt;
&lt;p&gt;调用&lt;code&gt;sys/types.h&lt;/code&gt;库下的&lt;code&gt;pid_t&lt;/code&gt;类型，来显式定义新变量的类型。&lt;br /&gt;
&lt;em&gt;(在大多数linux计算机中&lt;code&gt;unistd.h&lt;/code&gt;库会自动链接&lt;code&gt;sys/types.h&lt;/code&gt;库)&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;unistd.h&amp;gt;

int main(){

    pid_t pid = fork();
    //也可以使用 int 来进行定义，但是可能会出现PID超出int上限的情况

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;测试是否出创建了两个进程&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;

using namespace std;

int main(){
    pid_t pid = fork();
    
    if(pid == 0) cout &amp;lt;&amp;lt; &quot;here is child process&quot; &amp;lt;&amp;lt; endl;
    else cout &amp;lt;&amp;lt; &quot;here is parents process&quot; &amp;lt;&amp;lt; endl;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;查看编译结果:&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;images/20260616_1.png&quot; alt=&quot;编译结果&quot; /&gt;&lt;br /&gt;
可以看到程序被父进程和子进程分别调用了一次。&lt;/p&gt;
&lt;h3&gt;自定义错误参数的返回&lt;/h3&gt;
&lt;p&gt;在进程创建的过程中，当今进程创建失败时会弹出报错消息，此时我们可以利用&lt;code&gt;unistd.h&lt;/code&gt;库下的&lt;code&gt;perror&lt;/code&gt;函数，来对报错消息进行自定义注释。
例如:&lt;code&gt;perror(&quot;进程创建失败!&quot;)&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;父进程接收子进程的返回值&lt;/h3&gt;
&lt;p&gt;进程执行完毕后不会立即进行销毁，而是会进入类似阻塞状态并保留相对应的状态，因此需要调用&lt;code&gt;unitsd.h&lt;/code&gt;库下的&lt;code&gt;exit()&lt;/code&gt;函数进行资源解放。&lt;br /&gt;
同时&lt;code&gt;sys/wait.h&lt;/code&gt;库下的&lt;code&gt;wait()&lt;/code&gt;函数常与&lt;code&gt;exit()&lt;/code&gt;函数进行配套使用，在&lt;strong&gt;父进程&lt;/strong&gt;中调用&lt;code&gt;wait()&lt;/code&gt;函数会将父进程进行阻塞，此时父进程会等待子进程的结束，并尝试接收&lt;strong&gt;子进程&lt;/strong&gt;中主动调用&lt;code&gt;exit()&lt;/code&gt;函数的返回值。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;sys/wait.h&amp;gt;

using namespace std;

int main(){
    pid_t pid = fork();
    
    if(pid &amp;lt; 0){
        perror(&quot;进程创建失败!&quot;);
    }else if(pid == 0){
        exit(1);
    }else{
        int status;
        wait(&amp;amp;status);
        cout &amp;lt;&amp;lt; status;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;编译并运行:&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;images/20260616_2.png&quot; alt=&quot;编译结果&quot; /&gt;&lt;br /&gt;
得到编译结果，此时我们发现返回值由&lt;strong&gt;1&lt;/strong&gt;变成了&lt;strong&gt;256&lt;/strong&gt;，这是因为内核为了在一个int类型中插入更多信息，因此对返回值进行了左移运算，左移了8位，因此从&lt;strong&gt;00000000 00000001&lt;/strong&gt;变成了&lt;strong&gt;00000001 00000000&lt;/strong&gt;，即从&lt;strong&gt;1&lt;/strong&gt;变为了&lt;strong&gt;256&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;我们可以使用宏进行逆向操作
&lt;code&gt;WEXITSTATUS()&lt;/code&gt;。
此时可以得到正确的值:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;images/20260616_3.png&quot; alt=&quot;编译结果&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;如何获取PID&lt;/h3&gt;
&lt;p&gt;直接说结论:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;获取自身PID: 在父进程中直接输出,在子进程中需要使用&lt;code&gt;getpid()&lt;/code&gt;函数。&lt;/li&gt;
&lt;li&gt;获取父进程PID: 均需要使用&lt;code&gt;getppid()&lt;/code&gt;函数。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对上文代码进行更新:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;sys/wait.h&amp;gt;

using namespace std;

int main(){
    pid_t pid = fork();
    
    if(pid &amp;lt; 0){
        perror(&quot;进程创建失败!&quot;);
    }else if(pid == 0){
        
    //new
        cout &amp;lt;&amp;lt; getpid() &amp;lt;&amp;lt; &apos; &apos;
             &amp;lt;&amp;lt; getppid() &amp;lt;&amp;lt; endl;
        
        exit(1);
    }else{
        int status;
        wait(&amp;amp;status);
        cout &amp;lt;&amp;lt; WEXITSTATUS(status);

    //new
        cout &amp;lt;&amp;lt; pid &amp;lt;&amp;lt; &apos; &apos;
             &amp;lt;&amp;lt; getppid();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;查看编译结果:&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;images/20260616_4.png&quot; alt=&quot;编译结果&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以看到已经成功打印了。&lt;/p&gt;
</content:encoded></item><item><title>C++ nlohmann/json的基本操作</title><link>https://kuchikirei.github.io/posts/json/</link><guid isPermaLink="true">https://kuchikirei.github.io/posts/json/</guid><pubDate>Sat, 06 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;nlohmann/json库提供了一种格式用于存储，支持多种类型的格式存储读取&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;解释了几种基础操作&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;json文件的声明&lt;/li&gt;
&lt;li&gt;json数组中元素的插入和读取&lt;/li&gt;
&lt;li&gt;json对象的键值映射&lt;/li&gt;
&lt;li&gt;C++类与json类的相互转化&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;github地址：https://github.com/nlohmann/json&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;json文件的声明：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;j = nlohmann::json();&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;nlohmann::json几乎支持C++中所有类型&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;创建测试变量：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;std::string a = &quot;hello world&quot;;
int b = 10;
float c = 1.1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;在声明时进行插入:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;j = nlohmann::json{
    {&quot;a&quot;, a},
    {&quot;b&quot;, b},
    {&quot;c&quot;, c}
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;j = nlohmann::json();
j[&quot;a&quot;] = a;
j[&quot;b&quot;] = b;
j[&quot;c&quot;] = c;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;从json变量中读取数据&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;j.at(&quot;a&quot;).get_to(a);
j.at(&quot;b&quot;).get_to(b);
j.at(&quot;c&quot;).get_to(c);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;创建一个json数组&lt;/strong&gt;
声明
&lt;code&gt;nlohmann::json j = nlohmann::json::array();&lt;/code&gt;
&lt;strong&gt;插入操作：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;j.push_back(a);
j.push_back(b);
j.push_back(c);
显示长度：
j.size();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;在C++类中的自动调用&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class test{
private:
    std::string a;
    int b;
public:
    test(){
        std::cin &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b;
    }
    ~test();
    
    //使用friend实现外部调用,实现自动转化
    friend void to_json(nlohmann::json &amp;amp; j, const test &amp;amp; t){
        j = nlohmann::json{
            {&quot;a&quot;, t.a},
            {&quot;b&quot;, t.b}
        };
    }

    friend void from_json(const nlohmann::json &amp;amp; j, test &amp;amp; t){
        j.at(&quot;a&quot;).get_to(t.a);
        j.at(&quot;b&quot;).get_to(t.b);
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;json对象的键值映射&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;//创建一个json对象/

j = nlohmann::json::object();

//新建键对数组
const std::string arr[2] = {&quot;a&quot;, &quot;b&quot;};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以用于区分插入的数据,内部数据可以采用数组遍历的方式进行循环读取&lt;/p&gt;
</content:encoded></item></channel></rss>