跳转至

Tutorials

MPI

简介

  • Message Passing Interface (消息传递接口 MPI) is a standardized and portable message-passing standard designed to function on parallel computing architectures.[1]
  • The MPI standard defines the syntax 语法 and semantics 语意 of library routines that are useful to a wide range of users writing portable message-passing programs in C, C++, and Fortran.
  • There are several open-source MPI implementations (MPICH,Open MPI), which fostered the development of a parallel software industry, and encouraged development of portable and scalable large-scale parallel applications.

历史

  • 1994.6 MPI-1
  • 主要的MPI-1模型没有共享内存的概念,
  • point-to-point send/recieve, gather/reduce, synchronous, asynchronous,
  • MPI-2只有一个有限的分布式共享内存的概念。尽管如此,MPI程序通常在共享内存计算机上运行,MPICH和Open MPI都可以使用共享内存进行消息传输(如果可用的话)。
  • 围绕MPI模型(与显式共享内存模型相反)设计程序在NUMA体系结构上运行时具有优势,因为MPI鼓励内存局部性。显式共享内存编程是在MPI-3中引入的。

实现原理简介

虽然MPI属于OSI参考模型的第5层和更高层,但实现可以覆盖大多数层,其中在传输层中使用套接字和传输控制协议(TCP)。

与RDMA的区别

MPI hardware research focuses on implementing MPI directly in hardware, for example via processor-in-memory, building MPI operations into the microcircuitry of the RAM chips in each node. By implication, this approach is independent of language, operating system, and CPU, but cannot be readily updated or removed. MPI硬件研究的重点是直接在硬件中实现MPI,例如通过内存处理器,将MPI操作构建到每个节点中的RAM芯片的微电路中。通过暗示,这种方法独立于语言、操作系统和CPU,但是不能容易地更新或删除。

Another approach has been to add hardware acceleration to one or more parts of the operation, including hardware processing of MPI queues and using RDMA to directly transfer data between memory and the network interface controller(NIC 网卡) without CPU or OS kernel intervention. 另一种方法是将硬件加速添加到操作的一个或多个部分,包括MPI队列的硬件处理以及使用RDMA在存储器和网络接口控制器之间直接传输数据,而无需CPU或OS内核干预。

与管道的区别

进程间通信都是Inter-process communication(IPC)的一种。常见有如下几种:

  1. 文件,进程写文件到磁盘,其余进程能并行读取。
  2. Memory-mapped file 存储在内存里的文件
  3. signal,多为控制信号
  4. 信号量(计数器)
  5. Network Socket
  6. Message queue 消息队列(没用过
  7. 管道
  8. Anonymous pipe 匿名管道(命令行的结果传递|
    1. 可用于单向进程间通信(IPC)的单FIFO通信通道
    2. A unidirectional data channel using standard input and output.
  9. named pipe 有名管道
    1. 持久化,mkfifo,具有p的文件属性
    2. cat tail的例子说明,不建立写读连接会阻塞。
  10. Shared memory 共享内存(OpenMP
  11. Message passing 消息传递(类似MPI

与OpenMP的关系

线程共享存储器编程模型(如Pthreads和OpenMP)和消息传递编程(MPI/PVM)可以被认为是互补的,并且有时在具有多个大型共享存储器节点的服务器中一起使用。

基本概念

后四个是MPI-2独有的

  1. Communicator 进程组
  2. Point-to-point basics 点对点同步异步通信
  3. Collective basics 集体通信(eg. alltoall
  4. Derived data types 派生数据类型(自定义传输数据结构
  5. One-sided communication
  6. MPI-2定义了三个单边通信操作,分别是对远程存储器的写入、从远程存储器的读取以及跨多个任务对同一存储器的归约操作。
  7. Dynamic process management 类似进程池?没用过
  8. 并行文件IO

编程

C++ 查看在哪个节点

#include <unistd.h>
char hostname[100];
gethostname(hostname,sizeof(hostname));
printf( "Hello world from process %d of %d: host: %s\n", rank, size, hostname);

运行命令

输出X个当前机器hostname

mpirun -np 6 -machinefile ./machinelist ./a.out 即可多节点执行。

问题

MPI_Finalize()之后 ,MPI_Init()之前 https://www.open-mpi.org/doc/v4.0/man3/MPI_Init.3.php

不同的进程是怎么处理串行的部分的?都执行(重复执行?)。执行if(rank=num),那岂不是还要同步MPI_Barrier()。

而且写同一个文件怎么办?

对等模式和主从模式

MPI的两种最基本的并行程序设计模式 即对等模式和主从模式。

对等模式:各个部分地位相同,功能和代码基本一致,只不过是处理的数据或对象不同,也容易用同样的程序来实现。

主从模式:分为主进程和从进程,程序通信进程之间的一种主从或依赖关系 。MPI程序包括两套代码,主进程运行其中一套代码,从进程运行另一套代码。

程序并行可行性分析

圈收缩(cycle shrinking)-此变换技术一般用于依赖距离大于1的循环中,它将一个串行循环分成两个紧嵌套循环,其中外层依然串行执行,而内层则是并行执行(一般粒度较小)

https://shaojiemike.notion.site/41b9f62c4b054a2bb379316f27da5836

MPI消息

预定义类型消息——特殊MPI_PACKED

MPI_PACKED预定义数据类型被用来实现传输地址空间不连续的数据项 。

int MPI_Pack(const void *inbuf,
             int incount,
             MPI_Datatype datatype, void *outbuf, int outsize, int *position, MPI_Comm comm)
int MPI_Unpack(const void *inbuf, int insize, int *position,
               void *outbuf, int outcount, MPI_Datatype datatype, MPI_Comm comm)
The input value of position is the first location in the output buffer to be used for packing. position is incremented by the size of the packed message,

and the output value of position is the first location in the output buffer following the locations occupied by the packed message. The comm argument is the communicator that will be subsequently used for sending the packed message.

//Returns the upper bound on the amount of space needed to pack a message
int MPI_Pack_size(int incount, MPI_Datatype datatype, MPI_Comm comm, int *size)
例子: 这里的A+i*j应该写成A+i*2吧???

派生数据类型(Derived Data Type)

来定义由数据类型不同且地址空间不连续的数据项组成的消息。

//启用与弃用数据类型
int MPI_Type_commit(MPI_Datatype * datatype)
int MPI_Type_free(MPI_Datatype * datatype)
//相同数据类型
int MPI_Type_contiguous(int count, MPI_Datatype oldtype, MPI_Datatype * newtype)
//成块的相同元素组成的类型,块之间具有相同间隔
int MPI_Type_vector(int count,
                    int blocklength, int stride, MPI_Datatype oldtype, MPI_Datatype * newtype)

//成块的相同元素组成的类型,块长度和偏移由参数指定
int MPI_Type_indexed(int count,
                     const int *array_of_blocklengths,
                     const int *array_of_displacements,
                     MPI_Datatype oldtype, MPI_Datatype * newtype)

//由不同数据类型的元素组成的类型, 块长度和偏移(肯定也不一样)由参数指定
int MPI_Type_struct(int count,
                    int *array_of_blocklengths,
                    MPI_Aint * array_of_displacements,
                    MPI_Datatype * array_of_types, MPI_Datatype * newtype)

通讯域映射为网格表示

MPI_Cart_create 确定了虚拟网络每一维度的大小后,需要为这种拓扑建立通信域。组函数MPI_Cart_create可以完成此任务,其声明如下:

// Makes a new communicator to which topology拓扑 information has been attached
int MPI_Cart_create(
    MPI_Comm old_comm,//旧的通信域。这个通讯域中的所有进程都要调用该函数
    int dims,//网格维数 number of dimensions of cartesian grid (integer)
    int* size,//长度为dims的数组,size[j]是第j维的进程数, integer array of size ndims specifying the number of processes in each dimension
    int* periodic,//长度为dims的数组,如果第j维有周期性,那么periodic[j]=1,否则为0
    int reorder,//进程是否能重新被编号,如果为0则进程在新的通信域中仍保留在旧通信域的标号
    MPI_Comm* cart_comm//该函数返回后,此变量将指向新的笛卡尔通信域
);

int MPI_Cart_rank(MPI_Comm comm, const int coords[], int *rank)
//Determines process rank in communicator given Cartesian location
//该函数的作用是通过进程在网格中的坐标获得它的进程号

int MPI_Cart_coords(MPI_Comm comm, int rank, int maxdims, int coords[])
//Determines process coords in cartesian topology given rank in group
//该函数的作用是确定某个线程在虚拟网格中的坐标

通信域划分

int MPI_Comm_create(MPI_Comm comm, MPI_Group group, MPI_Comm * newcomm)
//Creates a new communicator

int MPI_Comm_split(MPI_Comm comm, int color, int key, MPI_Comm * newcomm)
将某个通信域进一步划分为几组

组间通信域

点对点通信

特殊的函数

int MPI_Sendrecv(const void *sendbuf, int sendcount, MPI_Datatype sendtype,
                 int dest, int sendtag,
                 void *recvbuf, int recvcount, MPI_Datatype recvtype,
                 int source, int recvtag, MPI_Comm comm, MPI_Status * status)
int MPI_Sendrecv_replace(void *buf, int count, MPI_Datatype datatype,
                         int dest, int sendtag, int source, int recvtag,
                         MPI_Comm comm, MPI_Status * status)
特别适用于在进程链(环)中进行“移位”操作,而避免在通讯为阻塞方式时出现死锁。

There is also another error. The MPI standard requires that the send and the receive buffers be disjoint不相交 (i.e. they should not overlap重叠), which is not the case with your code. Your send and receive buffers not only overlap but they are one and the same buffer. If you want to perform the swap in the same buffer, MPI provides the MPI_Sendrecv_replace operation.

//MPI标准阻塞通信函数,没发出去就不会结束该命令。
MPI_Send(sb, buf_size, MPI_INT, other, 1, MPI_COMM_WORLD);
                /*其中sb为发送缓冲区首地址, 
                  buf_size为发送数据量, 
                  MPI_INT 为发送数据的类型,
                  other为发送目标进程,(发送给other)
                  1的位置为tag,
                  MPI_COMM_WORLD为通信子*/
MPI_Recv(rb, buf_size, MPI_INT, other, 1, MPI_COMM_WORLD, &status);
                /*与发送类似,从other接收消息,status见下面*/

是否会导致死锁

可能大家会想到这会死锁,如下图:

但是实际情况可能并不会死锁,这与调用的MPI库的底层实现有关

MPI_Send将阻塞,直到发送方可以重用发送方缓冲区为止。当缓冲区已发送到较低的通信层时,某些实现将返回给调用方。当另一端有匹配的MPI_Recv()时,其他一些将返回到呼叫者。

但是为了避免这种情况,可以调换Send与Recv的顺序,或者使用MPI_Isend()或MPI_Issend()代替非阻塞发送,从而避免死锁。

梯形积分

/*
        梯形积分法,计算y=sin x 在[0,pi]上的积分
        @ trap 梯形积分串行程序
        @total_inte 最终积分结果
        */
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include<math.h>
#include "mpi.h"
using namespace std;

const double a = 0.0;
const double b = 3.1415926;
int n = 100;
double h = (b - a) / n;

double trap(double a, double b, int n, double h)
{
    double*x = new double[n + 1];
    double*f = new double[n + 1];
    double inte = (sin(a) + sin(b)) / 2;
    for (int i = 1; i<n + 1; i++) {
        x[i] = x[i - 1] + h;   /*x_0=a,x_n=b*/
        f[i] = sin(x[i]);
        inte += f[i];
    }
    inte = inte*h;    /* inte=h*[f(a)/2+f(x_1)+...f(x_{n-1})+f(b)/2]*/
    return inte;
}

int main(int argc, char * argv[])
{
    int myid, nprocs;
    int local_n;
    double local_a;
    double local_b;
    double total_inte;

    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &myid);   /* get current process id */
    MPI_Comm_size(MPI_COMM_WORLD, &nprocs); /* get number of processes */

    local_n = n / nprocs; //任务划分
    local_a = a + myid*local_n*h;
    local_b = local_a + local_n*h;
    double local_inte = trap(local_a, local_b, local_n, h);

    if (myid != 0) //通信结果
    {
        MPI_Send(&local_inte, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
    }
    else
    {
        total_inte = local_inte;
        for (int i = 1; i<nprocs; i++)
        {
            MPI_Recv(&local_inte, 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
            total_inte += local_inte;
        }
    }
    if (myid == 0)
    {
        printf("integral output is %d", total_inte);
    }
    MPI_Finalize();

    return 0;
}

群集通讯

一个进程组中的所有进程都参加的全局通信操作。

实现三个功能:通信、聚集和同步。 1. 通信功能主要完成组内数据的传输 2. 聚集功能在通信的基础上对给定的数据完成一定的操作 3. 同步功能实现组内所有进程在执行进度上取得一致

常见的通讯

//将一个进程中得数据发送到所有进程中的广播函数
MPI_Bcast(void* data_p,int count,MPI_Datatype datatype, int scr_process,MPI_Comm comm);
注意data_p在root 或者scr_process进程里是发送缓存也是接收缓存,但是在其余进程里是接收缓存。 MPI_Scatter?

区别

  1. MPI_Scatter与MPI_Bcast非常相似,都是一对多的通信方式,不同的是后者的0号进程将相同的信息发送给所有的进程,而前者则是将一段array的不同部分发送给所有的进程,其区别可以用下图概括:
  2. MPI_Gather,作用是从所有的进程中将每个进程的数据集中到根进程中,同样根据进程的编号对array元素排序,
  3. 接收缓冲由三元组标识,发送缓冲由三元组标识,所有非Root进程忽略接收缓冲。
  4. MPI_Allgather 当数据分布在所有的进程中时,MPI_Allgather将所有的数据聚合到每个进程中。
  5. Allgather操作相当于每个进程都作为ROOT进程执行了一次Gather调用,即每一个进程都按照Gather的方式收集来自所有进程(包括自己)的数据。
  6. MPI_GATHERV扩展了功能,提供新的参数disp,是一个整数数组,包含存放从每个进程接收的数据相对于recvbuf的偏移地址
  7. MPI_alltoall()
  8. 等价于每个进程作为Root进程执行了一次MPI_Scatter散播操作。
    int MPI_Allgather(void * sendbuff, int sendcount, MPI_Datatype sendtype, 
                      void * recvbuf, int recvcount, MPI_Datatype recvtype, 
                      MPI_Comm comm)
    int MPI_Allgatherv(void * sendbuff, int sendcount, MPI_Datatype sendtype, 
                       void * recvbuf, int * recvcounts, int * displs, 
                       MPI_Datatype recvtype, MPI_Comm comm)
    
    recvcount gather和allgather是一样的

number of elements received from any process (integer)

注意

  1. 通信域中的所有进程必须调用群集通信函数。如果只有通信域中的一部分成员调用了群集通信函数而其它没有调用,则是错误的。
  2. 除MPI_Barrier以外,每个群集通信函数使用类似于点对点通信中的标准、阻塞的通信模式。也就是说,一个进程一旦结束了它所参与的群集操作就从群集函数中返回,但是并不保证其它进程执行该群集函数已经完成
  3. 一个群集通信操作是不是同步操作取决于实现。MPI要求用户负责保证他的代码无论实现是否同步都必须是正确的。 ???与后面矛盾了 mpich官网说明的。
  4. 关于同步最后一个要注意的地方是:始终记得每一个你调用的集体通信方法都是同步的。
  5. https://mpitutorial.com/tutorials/mpi-broadcast-and-collective-communication/zh_cn/
  6. 在MPI-3.0之前MPI中的所有集合操作都是阻塞的,这意味着在返回之后使用传递给它们的所有缓冲区是安全的.特别是,这意味着当其中一个函数返回时,会收到所有数据.(但是,它并不意味着所有数据都已发送!)因此,如果所有缓冲区都已有效,则在集合操作之前/之后MPI_Barrier不是必需的(或非常有用).
  7. 对用户的建议:为保证程序正确性而依赖于集合操作中同步的副作用是很危险的作法.例如,即便一个特定的实现策略可以提供一个带有同步副作用的广播通信例程, 但标准却不支持它,因此依赖于此副作用的程序将不可移植.从另一方面讲,一个正确的、可移植的程序必须能容忍集合操作可能带来同步这样 一个事实.尽管一个程序可以丝毫不依赖于这种同步的副作用,编程时也必须这样做.这个问题在4.12节中还将进一步讨论(对用户的建议结尾) https://scc.ustc.edu.cn/zlsc/cxyy/200910/MPICH/mpi41.htm
  8. 关于不同的进程运行同一句Bcast的效果
  9. 当根节点(在我们的例子是节点0)调用 MPI_Bcast 函数的时候,data 变量里的值会被发送到其他的节点上。当其他的节点调用 MPI_Bcast 的时候,data 变量会被赋值成从根节点接受到的数据。
  10. 所以如果有进程无法到达该语句Bcast,同步的性质会导致到达Bcast的命令需要等待。

聚合

MPI聚合的功能分三步实现 * 首先是通信的功能,即消息根据要求发送到目标进程,目标进程也已经收到了各自需要的消息; * 然后是对消息的处理,即执行计算功能; * 最后把处理结果放入指定的接收缓冲区。

MPI提供了两种类型的聚合操作: 归约和扫描。

聚合——归约

int MPI_Reduce(
    void *input_data, /*指向发送消息的内存块的指针 */
    void *output_data, /*指向接收(输出)消息的内存块的指针 */
    int count,/*数据量*/
    MPI_Datatype datatype,/*数据类型*/
    MPI_Op operator,/*规约操作*/
    int dest,/*要接收(输出)消息的进程的进程号*/
    MPI_Comm comm);/*通信器,指定通信范围*/
// operator可以有:求最大值 MPI_MAX 最小值 求累加和 累乘积 逻辑操作  

// 求和语句
MPI_Reduce(&local_int,&total_int,1,MPI_DOUBLE,MPI_SUM,0,MPI_COMM_WORLD);

//另外有时候需要将得到的结果放入所有的线程中
MPI_Allreduce(void* input_data_p,void*output_data_p, int count,MPI_Datatype datatype,MPI_Op operator, MPI_Comm comm);

//每一个进程都对排在它前面的进程进行归约操作。
MPI_scan(SendAddress, RecvAddress, Count, Datatype, Op, Comm)

自定义归约操作

int MPI_Op_create(MPI_User_function *function, int commute, MPI_Op *op)

//function    用户自定义的函数(函数)
//commute   如果commute=ture, 则此操作同时也是可交换的。如果commute=false,则此操作不满足交换律。
            else 按进程号升序进行Op操作
//op              自定义归约操作名

int  MPI_Op_free(MPI_Op *op) //将用户自定义的归约操作撤销, 将op设置成MPI_OP_NULL。
用户自定义函数 function
typedef void MPI_User_function(void *invec, void *inoutvec, int *len, MPI_Datatype *datatype)
for(i=0;i<*len;i++)  {
    *inoutvec = *invec USER_OP *inoutvec;
    inoutvec++;  invec++;
}
必须具备四个参数: 1. invec 和 inoutvec 分别指出将要被归约的数据所在的缓冲区的首地址, 2. len指出将要归约的元素的个数, datatype 指出归约对象的数据类型

也可以认为invec和inoutvec 是函数中长度为len的数组, 归约的结果重写了inoutvec 的值。

梯形积分(MPI_Reduce)
 /*
@local_inte:send buffer;
@total_inte:receive buffer;
@MPI_SUM:MPI_Op;
@dest=0,rank of the process obtaining the result.
*/ 中间改成这个

MPI_Reduce(&local_inte, &total_inte, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);

注意事项

  1. 除了#include "mpi.h"

需要进一步的研究学习

MPI_Group https://www.rookiehpc.com/mpi/docs/mpi_group.php

并行IO文件

1997年推出了MPI的最新版本MPI-2

MPI-2加入了许多新特性,主要包括 * 动态进程(Dynamic Process) * 远程存储访问(Remote Memory Access) * 并行I/O访问(Parallel I/O Access) * MPI-1没有对并行文件I/O给出任何定义,原因在于并行I/O过于复杂,很难找到一个统一的标准。 more

遇到的问题

数据发送和收集

开题缘由、总结、反思、吐槽~~

参考文献

https://blog.csdn.net/susan_wang1/article/details/50033823

https://blog.csdn.net/u012417189/article/details/25798705

是否死锁: https://stackoverflow.com/questions/20448283/deadlock-with-mpi

https://mpitutorial.com/tutorials/

http://staff.ustc.edu.cn/~qlzheng/pp11/ 第5讲写得特别详细

https://www.mpich.org/static/docs/latest/www3/

Keyboard

It's a fucking crazy thing when you reuse a Bluetooth device, because forget how to make pair.

logitech K780

My keyboard encounter Poor contact of keyboard keys, esepeacially the ctrl

change Win/Mac/IOS configurations

iOS fn + i

Mac OS X fn + o

Windows fn + p

LEOPOLO FC980M

Bluetooth pair

Read more: official ref and ref_photo

It seems that just

  1. Open the battery cover
  2. insert AAA battery and Set the power switch to the ON position.

you can Turn on the Bluetooth.

Answer from TAOBAO

连接蓝牙方法:(我们键盘没有送蓝牙适配器)需要您电脑有蓝牙功能,

  1. 第一步背后大开关打到on,
  2. 第二步用取卡针捅一下大开关下面的孔、进入配对环节,
  3. 第三步打开电脑蓝牙搜蓝牙键盘的型号按提示连接就行。参考

Windows weird option 输入 FC980MBT 的PIN,也可以选择关闭,尤其是鼠标也需要输入时:

  1. type 00000 using original keyboard,click confirm.
  2. type 00000 using new keyboard, enter.

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。

Komga

简介

漫画或者PDF的jellyfin版本 , 类似的还有 基于docker的smanga

安利文简单使用文

选择

Rather than browse rouman online, high-resolution pivix pictures seems more worthy to be downloaded and maintained.

But first you need a much bigger NAS.

安装

  • 通过Docker安装(Docker on Windows 体验不好)
  • 通过java运行

Windows

  • 由于在portainer.io里路径有问题,选择直接在docker里点击image run创建容器。
  • 如果输入数据来源多,建议设置子目录data/1 and data/2。 and Please think carefully because restart container will triger the following bugs:

But docker on Windows remains many bugs:

  1. Failed to restart the docker engine
  2. deadlock between first free volume to delete container and first stop already stepped container to free related volume.

Linux docker

In http://brainiac.acsalab.com:2333/

step1: map remote data to local visual disk

  1. map windows disk to linux
  2. map Nas disk to linux sudo mount.cifs //synology.acsalab.com/Entertainment /synology -o user=xxx vers=3.0

Step2 : docker

采取第一种, 在 portainer.io的local的stack里使用docker compose部署

---
version: '3.3'
services:
  komga:
    image: gotson/komga
    container_name: komga
    volumes:
      - type: bind
        source: /mnt/e/commonSoftware/komga/config # Database and Komga configurations
        target: /config
      - type: bind
        source: /mnt/e/commonSoftware/komga/data # Location of your data directory on disk. Choose a folder that contains both your books and your preferred import location for hardlinks to work.
        target: /data/komga
      - type: bind
        source: /etc/timezone #alternatively you can use a TZ environment variable, like TZ=Europe/London
        target: /etc/timezone
        read_only: true
    ports:
      - 2333:8080 # 应用内部的 8080 到机器的2333端口。由于机器的8080被qBit占用了
    user: "1000:1000"
    # remove the whole environment section if you don't need it
    environment:
      - <ENV_VAR>=<extra configuration>
    restart: unless-stopped

Docker in Ugreen Nas

  1. easy pull official komga image
  2. set mount disk
  3. set port

komga V.S. smanga V.S Kavita

  1. exhentai-manga-manager only on widnows
  2. smanga,
    1. 优点: 1. 带标签和收藏,维持三级目录, 2. 独特的目录设计。
    2. 缺点: 1. png读取正常,但是zip解压过于缓慢 2. PDF的支持暂时欠缺: 1. 无法阅读 2. PDF阅读与decompress的冲突 3. 元数据不能自由编辑,只能编辑标签。 4. 不能读取根目录的文件,但是能选择单行本,或许可以解决这个问题,但是需要尝试。 5. 无法读到过深的文件夹。
    3. 小结:本来寄希望于这个all in one, 但是问题太多,还是只适合刮削好的资源。 可以不断完善和尝试。
    4. 举例:所有韩漫,
  3. Kavita
    1. 优点: 1. 纯zip文件能读到深处的文件夹 2. 日漫带标签和收藏,有缩略图。 3. 元数据能自由编辑
    2. 缺点: 1. 奇怪的文件名识别规则,导致 2. 杂乱的类型(zip, png, pdf)会导致目录混乱 3. 不能读取根目录的文件
    3. 小结:适合高度组织过后的内容
    4. 举例:2022单行本,零散日漫单行本。和calibre处理后的文件。
  4. komga
    1. 优点: 1. 维持原始目录结构,稳定简洁。 2. 元数据能自由编辑 3. 根目录文件能识别
    2. 缺点: 1. 没有收藏,保存和标签等功能,不适合碎片化连续看。
    3. 小结:适合混乱的内容,靠文件夹的并列和包含关系,维护逻辑关系。
    4. 举例:杂志(没有子文件夹包裹,识别不了),漫之学院(太大),日漫大合集(混乱的结构)

需要图书/漫画刮削

有刮削建议 smanga

有封面图和备注详细角色和类型信息。

dockers komga smanga Kavita
单一大PDF文件加载 缓慢 缓慢
格式支持 zip,cbz,pdf 部分zip不支持bug,不支持cbz zip,pdf ,cbz
如何支持单文件夹多图片 每个文件夹单独压缩成zip反而支持
自定义元数据
任意位置标签
已知bug 容器会自动关机(有待进一步测试)
总体评价 基础完善稳定,但是定制化不足 有用的定制化 全,但是不维持原目录有点恶心,导致必须按照类型整理。
Manga vs comic

"Manga" 和 "comic" 是两个术语,通常用于描述不同地区和文化中的漫画,其中 "manga" 常用于日本漫画,而 "comic" 通常用于西方漫画,包括美国漫画。

  1. Manga(漫画):
  2. 地域: "Manga" 是日本的一种漫画形式,是日本漫画的通用术语。
  3. 特点: Manga 的特点包括从右到左的阅读顺序,经常包含有关日本文化和社会的元素,以及广泛的主题和风格。

  4. Comic(漫画):

  5. 地域: "Comic" 是一个通用的英语词汇,用于描述西方国家的漫画,主要是美国漫画。
  6. 特点: Comic 的特点包括从左到右的阅读顺序,以及通常较大的页数和较大的漫画行业。

需要注意的是,"manga" 和 "comic" 不仅仅是描述漫画的词汇,它们还代表了不同的创作风格、文化和产业。虽然在某些上下文中可能会使用这两个词汇来泛指漫画,但在讨论时最好根据具体的地域和文化使用适当的术语。

其余未汉化版本

LANraragi

推荐文件结构

kativa

Library Root
  ┖── Series Name
      ┖── Series Name SP01 Special Name.cbz
      ┖── abc.cbz
  • Kavita enforces that all files are within folders from the library root. Files at library root will be ignored.
  • kativa 会将不符合文件夹命名Series Name的新文件(e.g.,abc.cbz),当作新的作品单独列出在根目录。这十分傻逼,对内容的共振度的要求也太高了。出现一个命名不规范的文件就会乱套。
  • kativa 对文件类型也会做特殊处理,对png, cbz, zip文件会有不同的阅读器,最搞笑的是 kativa会把子章节cbz的同名图片单独用个文件夹放置,而不是识别出章节的封面图,kativa会读取子章节cbz的内容当作封面图。

smanga

可以看得出作者有特殊的设计。

但是美中不足的是对于子文件夹的支持不够,太深的文件读取不到。

单行本

理论如此,但是实际貌似会卡住。

komga

Komga支持CBZ/CBR、EPUB、PDF格式。对于漫画而言,个人觉得cbz1是最简单、兼容性最高的格式。

建议的文件结构如下:

.
└── libraryManga
    ├──            ├──  1话.cbz
       └──  9话 .cbz
    └── 辉夜大小姐想让我告白
        ├── 01话 .cbz
        └── 02话.cbz
3 directories, 4 files
  • libraryManga表示库名,下一层结构区分不同的漫画,更下一层则存储漫画文件
  • komga相对于kavita的异同
    • 都不会识别根目录下的文件
    • 但是komga不会管Series文件夹的子文件夹,会认为不存在,全部打散。kavata由于命名的原因会将Series文件夹的子文件夹内的内容当作新的系列,提到根目录显示。

内容的组织

需要考虑的点

内容的组织考虑的是一个平衡,每个lib下应该只有40个左右的内容。

  • 根据类别分类:
  • 韩漫,日漫,杂志
  • 根据时间分类:每个月大约有50部左右

下载的内容的特点:

  • 日漫,很多是单行本没有系列的,所以系列的这一级文件夹可以使用时间代替
  • 有些库太大了,估计有一千本漫画(漫之学院),必须分开。
  • 有些库还是识别不了 合集/A/PDF/*

实践

看多少,刮削多少。

内容的刮削

calibre + ehentai

见 Calibre and its Pugins for e-hentai Books 一文

好奇?这些日本的单行本有元数据吗?来自哪里的?

漫画大部分来自E-hentai/Exhentai 1

还有 nheitai hanime1的漫画站就是这个的克隆。

里番-> 桜都字幕组 -> Nyaa里站,同时南+

失败:BangumiKomga

  • 一个从Bangumi获取元数据并填充的Python脚本
  • 但是自动识别刮削的成功率很低,强烈建议在Bangumi中先找到对应漫画后把链接贴到Komga系列作品链接处,标签写为cbl,配置好后在目录下运行python processMetadata.py,即可近乎完美的给漫画加上海报和信息了
  • 但是Bangumi又没有本子的内容

集成订阅平台:tachidesk

尝试后发现是,类似RSS的漫画网页集成浏览器(B站,腾讯漫画,18+漫画)。实现订阅,跟踪,一键下载。 由于生态很不错,不用担心订阅链接失效。

Bugs docker stopped in UGREEEN

我猜测是内存不够

kavita自动关机
[Kavita] [2023-12-08 07:21:00.508 +00:00  194] [Information] Serilog.AspNetCore.RequestLoggingMiddleware HTTP GET /api/image/series-cover?seriesId=759&apiKey=39714029-85f9-446c-9834-9ad384fda00d responded 304 in 0.9718 ms
[Kavita] [2023-12-08 07:21:50.010 +00:00  188] [Information] Serilog.AspNetCore.RequestLoggingMiddleware HTTP POST /api/account/refresh-token responded 200 in 2630.1327 ms
[Kavita] [2023-12-08 07:21:50.043 +00:00  193] [Information] Serilog.AspNetCore.RequestLoggingMiddleware HTTP GET /api/license/valid-license?forceCheck=false responded 200 in 17.1837 ms
[Kavita] [2023-12-08 07:21:50.045 +00:00  182] [Information] Serilog.AspNetCore.RequestLoggingMiddleware HTTP POST /hubs/messages/negotiate?negotiateVersion=1 responded 200 in 5.7189 ms
[Kavita] [2023-12-08 07:21:50.131 +00:00  168] [Information] Serilog.AspNetCore.RequestLoggingMiddleware HTTP GET /api/device responded 200 in 107.7960 ms
Server is shutting down. Please allow a few seconds to stop any background jobs...
You may now close the application window.
[Kavita] [2023-12-08 07:21:55.785 +00:00  179] [Information] Serilog.AspNetCore.RequestLoggingMiddleware HTTP POST /hubs/messages/negotiate?negotiateVersion=1 responded 200 in 0.1795 ms
[Kavita] [2023-12-08 07:21:55.789 +00:00  45] [Information] Microsoft.Hosting.Lifetime Application is shutting down...
[Kavita] [2023-12-08 07:22:26.562 +00:00  179] [Fatal]  Host terminated unexpectedly
System.AggregateException: One or more hosted services failed to stop. (The operation was canceled.)
---> System.OperationCanceledException: The operation was canceled.
  at System.Threading.CancellationToken.ThrowOperationCanceledException()
  at System.Threading.CancellationToken.ThrowIfCancellationRequested()
  at Hangfire.Processing.TaskExtensions.WaitOneAsync(WaitHandle waitHandle, TimeSpan timeout, CancellationToken token)
  at Hangfire.Processing.BackgroundDispatcher.WaitAsync(TimeSpan timeout, CancellationToken cancellationToken)
  at Hangfire.Server.BackgroundProcessingServer.WaitForShutdownAsync(CancellationToken cancellationToken)
  at Microsoft.Extensions.Hosting.Internal.Host.StopAsync(CancellationToken cancellationToken)
  --- End of inner exception stack trace ---
  at Microsoft.Extensions.Hosting.Internal.Host.StopAsync(CancellationToken cancellationToken)
  at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.WaitForShutdownAsync(IHost host, CancellationToken token)
  at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
  at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
  at API.Program.Main(String[] args) in /home/runner/work/Kavita/Kavita/API/Program.cs:line 115
komga自动关机

查看对应docker日志, 猜测容器运行时,可能会受到资源限制,例如内存不足、CPU 使用过高等。如果容器超过了资源限制,可能会被系统强制关闭。

2023-12-07T11:28:39.022Z  INFO 1 --- [taskProcessor-4] o.g.komga.application.tasks.TaskHandler  : Task FindBooksWithMissingPageHash(libraryId='0EEB1WDMHPFT0', priority='0') executed in 722.922us
2023-12-07T11:28:39.080Z  INFO 1 --- [taskProcessor-4] o.g.komga.application.tasks.TaskHandler  : Executing task: FindDuplicatePagesToDelete(libraryId='0EEB1WDMHPFT0', priority='0')
2023-12-07T11:28:39.096Z  INFO 1 --- [taskProcessor-4] o.g.komga.application.tasks.TaskEmitter  : Sending tasks: []
2023-12-07T11:28:39.096Z  INFO 1 --- [taskProcessor-4] o.g.komga.application.tasks.TaskHandler  : Task FindDuplicatePagesToDelete(libraryId='0EEB1WDMHPFT0', priority='0') executed in 16.090346ms
2023-12-07T13:38:16.408Z  INFO 1 --- [ionShutdownHook] o.s.b.w.e.tomcat.GracefulShutdown        : Commencing graceful shutdown. Waiting for active requests to complete
2023-12-07T13:38:16.638Z  INFO 1 --- [tomcat-shutdown] o.s.b.w.e.tomcat.GracefulShutdown        : Graceful shutdown complete
2023-12-07T13:38:18.871Z  INFO 1 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : SqliteTaskPool - Shutdown initiated...
2023-12-07T13:38:18.883Z  INFO 1 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : SqliteTaskPool - Shutdown completed.
2023-12-07T13:38:18.887Z  INFO 1 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : SqliteUdfPool - Shutdown initiated...
2023-12-07T13:38:18.889Z  INFO 1 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : SqliteUdfPool - Shutdown completed.
____  __.
|    |/ _|____   _____    _________
|      < /  _ \ /     \  / ___\__  \
|    |  (  <_> )  Y Y  \/ /_/  > __ \_
|____|__ \____/|__|_|  /\___  (____  /
        \/           \//_____/     \/

Version: 1.8.4

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

https://sspai.com/post/79100

GDB

GDB 基本命令

  • -next单步执行
  • -step单步进入
  • finish 跳出当前函数
  • 跳出当前函数 finish: Continue running until just after function in the selected stack frame returns. Print the returned value (if any). This command can be abbreviated as fin.
  • -continue继续执行到下一个断点
  • -until继续运行到指定位置

运行带参数程序

  • gdb --args 正常程序+参数
  • 进入gdb后运行 set args 参数

break断点

  • f 打印当前文件 ,便于打断点
  • info breakpoints 查看已经的断点
  • del 3 删除NUM=3的第三个断点
给某个结构体内的函数全部上break
rbreak file.cpp:.*TemplateClass.*

GDB 打印信息

  • 参数 show args
  • 局部变量 info locals
  • 修改变量 p result=20
  • 函数调用栈 bt

打印结构体class

gef➤  p -raw-values off -- this->TotalCycles
gef➤  p this                                                                                                                                                                                                                         
$11 = (llvm::mca::SummaryView * const) 0x7fffffffcc08                                                                                                                                                                                
gef➤  p *this

打印指针变量

  • 二维指针 p **matrix@3@3
  • 一维指针 p *matrix@3
  • 或者转换为数组 p *(int *)matrix@3
  • 或者转换为数组 p *(int (*)[3])matrix

打印数组

(gdb) p array[60]@10
$9 = {60, 61, 62, 63, 64, 65, 66, 67, 68, 69}

可以看到打印了array数组第60~69个元素的值。如果要打印从数组开头连续元素的值,也可使用这个命令:“p *array@num”:

(gdb) p *array@10
$2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

GDB中查看指定内存地址内容的指令

examine命令(简写是x)

格式:x /nfu <addr> 说明:

  • x是examine的缩写
  • n表示要显示的内存单元的个数
  • f表示显示方式,可取如下值
  • x按十六进制格式显示变量。
  • d按十进制格式显示变量。
  • u按十进制格式显示无符号整型。
  • o按八进制格式显示变量。
  • t按二进制格式显示变量。
  • a按十六进制格式显示变量。
  • i指令地址格式
  • c按字符格式显示变量。
  • f按浮点数格式显示变量。
  • u表示一个地址单元的长度
  • b表示单字节,
  • h表示双字节,
  • w表示四字节,
  • g表示八字节

example:

(gdb) x 0x8049948
0x8049948: 0x20726f46
(gdb) x/s 0x8049948
0x8049948: "For NASA,space is still a high priority."
(gdb) x/4 0x7fffe536dbc0 # display 4 bytes info?
0x7fffe536dbc0: 0x0     0x0     0x9d835 0x0

打印寄存器值(表格)

layout split
layout regs
tui reg general

gef 界面

效果

真好用,什么都会打印

安装gef

bash -c "$(curl -fsSL http://gef.blah.cat/sh)"

# 没有网,手动下 https://gef.blah.cat/py ,替换
$ wget -O ~/.gdbinit-gef.py -q https://gef.blah.cat/py
$ echo source ~/.gdbinit-gef.py >> ~/.gdbinit

注意:gdbtui 与 gef 不太兼容

需要进一步的研究学习

之前gdbtui的使用写在notion上了

https://shaojiemike.notion.site/GDB-d53a1be0b6324234a2ae43b7c27a23b8## 遇到的问题 暂无

开题缘由、总结、反思、吐槽~~

参考文献

PPT acadamic figure

🏗施工中🏗

🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧

觉得有意义写,先占个位子。还没写好呢,建议不要看(逻辑内容都没想清楚),不要急~~

🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧

核心思想

画图是为了解释文字难以说清的事情。

  1. 明确要说明的观点和内容。需要时完整但是不复杂的内容,复杂的内容会导致内容的丢失。
  2. 明确需要图形化的对象(除开)
  3. 明确对象的重要性,越重要-> 越大、颜色越鲜艳、字体越粗。(大小如果是写实的就保持实际)
  4. 对象间的关系(通常用小字说明): 顺序大小关系(箭头),集合关系(花括号,虚实方框包裹)
  5. 保持画面和谐
  6. 对象的空间分布均匀,大小均匀,有层次。主要对象居中且保持对齐。
  7. 颜色有限,重点突出
  8. 最终效果:读者知道按照什么顺序看图,图形的关键是什么,作者想说明什么。

其实这些都是自然的东西,好像也不需要总结。

常见类型

  1. 流程图(只是展示理清每个关键步骤,还是更具体到每个步骤的核心实现方法)
  2. 实例说明图(抽象概念往往难以理解,实例说明更容易理解)
  3. 复杂概念的关系图

注意事项

图形的选择、大小和位置

  1. 虚实方框包裹 表示 弱或强的同类型关系

字体的选择、大小、位置、字体和粗细斜体

颜色

  1. 基本配色(统一的重要性关系): 红 > 黄 > 蓝/绿 > 灰/黑
  2. 颜色赋予的含义:红最重要, 黑色一般时baseline

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。

Script: Email notifications when machine is free

outline

login to free SMTP server(qq, google) to send email to others.

send email using command line

  1. check ping mail.ustc.edu.cn
  2. MUST : set gmail open to support SMTP and apppassword
  3. or config qq email

IMAP, POP3 or SMTP

mailutils

According to ref1.

# install ca-certificates
# After new a google accounts, 
# config according to ref1
sudo apt-get install mailutils
$ echo "Hello world" | mail -s "Test" [email protected]

send but the same, more crazy thing is the -v and -d flag is not supported. and --debug-level=trace0 isn't recognized. many same question in StackOverflow

sudo tail -n 30 /var/log/mail.log or mail.error show more info.

ssmtp

We try ref2 ssmtp, sudo vim /etc/ssmtp/ssmtp.conf

TLS_CA_FILE=/etc/pki/tls/certs/ca-bundle.crt
root=[email protected]
mailhub=smtp.gmail.com:587
rewriteDomain=gmail.com
AuthUser=shaojieemail
AuthPass={apppassword}
FromLineOverride=YES
UseSTARTTLS=Yes
UseTLS=YES
hostname=snode6

The config get work but not well configed, e.g., TLS_CA_FILE

sending a email using gmail took about 13 mins.

$ ssmtp 943648187@qq.com < mail.txt
......
[->] Received: by snode6 (sSMTP sendmail emulation); Wed, 06 Sep 2023 15:42:05 +0800
[->] From: "Shaojie Tan" <[email protected]>
[->] Date: Wed, 06 Sep 2023 15:42:05 +0800
[->] test server email sending
[->]
[->] .
[<-] 250 2.0.0 OK  1693986323 5-20020a17090a1a4500b0026b4ca7f62csm11149314pjl.39 - gsmtp
[->] QUIT
[<-] 221 2.0.0 closing connection 5-20020a17090a1a4500b0026b4ca7f62csm11149314pjl.39 - gsmtp

sendmail

$ sendmail [email protected] < mail.txt
sendmail: Authorization failed (535 5.7.8  https://support.google.com/mail/?p=BadCredentials e7-20020a170902b78700b001c0c79b386esm8725297pls.95 - gsmtp)

get to work after well config gmail setting.

Speed Compare

command snode6 time(mins) icarus1
mail 4 1s
ssmtp 13
sendmail 6

send email by python

ref using QQ apppassword and python.

email notifications

  1. Create a Bash Script: Create a Bash script that checks the CPU usage and sends an email if it's below 30%. For example, create a file named cpu_check.sh:
#!/bin/bash

# Get CPU usage percentage
cpu_usage=$(top -b -n 1 | grep '%Cpu(s):' | awk '{print $2}' | cut -d'.' -f1)
echo "cpu_usage : ${cpu_usage} on $(hostname)"
# Check if CPU usage is below 30%
if [ "$cpu_usage" -lt 30 ]; then
   echo "beyond threshold : ${cpu_usage} on "
    # Send an email
    echo "CPU usage is ${cpu_usage} below 30% on $(hostname)" | mail -s "Low CPU Usage Alert on $(hostname)" [email protected]
fi

Make the script executable:

chmod +x cpu_check.sh

Modify [email protected] with your actual email address.

  1. Schedule the Script: Use the cron scheduler to run the script at regular intervals. Edit your crontab by running:
crontab -e

Add an entry to run the script, for example, every 5 minutes:

*/5 * * * * /staff/shaojiemike/test/cpu_check.sh >> /staff/shaojiemike/test/cpu_check.log
# Run every 15 minutes during working hours (9 am to 7 pm)
*/15 9-19 * * * /path/to/your/script.sh

Replace /path/to/cpu_check.sh with the actual path to your Bash script.

  1. Save and Exit: Save the crontab file and exit the text editor.

Now, the script will run every 5 minutes (adjust the cron schedule as needed) and send an email notification if the CPU usage is below 50%. You should receive an email when the condition is met.

Please note that this is a basic example, and you can modify the script to include more details or customize the notification further as needed. Additionally, ensure that your server is configured to send emails; you may need to configure SMTP settings for the mail or sendmail command to work correctly.

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。