• C风格和C++风格的文件操作库函数总结

    由于C++是兼容C的,所以使用C++进行文件操作时,会发现有两套库函数可以使用,它们分别是C风格的和C++风格的。

    C风格的文件操作函数

    打开文件,关闭文件

    #include <stdio.h>
    FILE* fopen(const char* path, const char* mode);
    int fclose(FILE* fp);
    

    读取文件,写入文件

    #include <stdio.h>
    size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream);
    size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream);
    

    关于fread中两个表示大小的参数,在这个 stackoverflow 问题中得到了比较好的解答。

    改变文件指示器的位置

    #include <stdio.h>
    int fseek(FILE* stream, long offset, int whence);
    long ftell(FILE* stream);
    void rewind(FILE* stream);
    

    C++风格的文件操作函数

    C++将文件操作函数封装为 fstream 操作类,一个文件实际上就会对应一个 fstream 对象,一下函数都是 fstream 的成员函数。
    文件打开和关闭

    void open(const char* filename, ios_base::openmode mode = ios_base::in|ios_base::out);
    void open(const std::string& filename, ios_base::openmode mode = ios_base::in|ios_base::out);
    void close();
    

    对于文件的读写可以才采用流运算符重载的方法完成。

    friend fstream& operator << (fstream& ofs, const CRecord& objRecord);
    friend fstream& operator >> (fstream& ifs, CRecord& objRecord);
    

    题外话

    在liunx下,可以使用wc命令查看文件中的相关信息。wc命令会打印出该文件的行数、单词数、字符数。

  • 字节序与union类型

    字节序

    字节序是指在数据传输时,高位字节存储在内存中的较高位地址还是较低位地址。
    字节序分为大端字节序和小端字节序,两种字节序不能混用。
    大端字节序 :高位字节保存在较低位地址的内存中
    小端字节序 :高位字节保存在较高位地址的内存中

    int n = 0x12345678 为例,大端字节序内存中的存储的顺序为 0x12 0x34 0x56 0x78 ,小端字节序内存中的存储顺序为 0x78 0x56 0x34 0x12 。很明显可以看出大端字节序更符合人类的阅读习惯。

    常见的大端系统CPU:IBM z/Atchitecture
    常见的小端系统CPU:intel x86

    网络字节序使用的大端字节序,常用网络协议如IPv4、IPv6、TCP和UDP协议都是使用大端字节序完成数据传输的。

    查看当前系统的字节序

    通过 lscpu 命令,能得到cpu的大端和小端信息。

    lscpu | grep -i endian
    

    通过常用shell命令,在大端系统中会输出0,在小端系统中会输出1。

    echo -n I | od -to2 | head -n1 | cut -f2 -d" " | cut -c6
    

    借助python进行判断。

    python -c "import sys; print(sys.byteorder)"
    # or
    python -c "import sys;sys.exit(0 if sys.byteorder=='big' else 1)"
    

    借助linux的ELF进行判断,查看第6个字节,小端系统中为1,大端系统中为2。

    xxd -c 1 -l 6 /bin/bash
    # or
    hexdump -n 6 -C /bin/bash
    

    union类型

    可以使用union类型验证字节序,以下示例代码在不同的字节序下会有不同的输出。

    #include <stdio.h>
    #include <inttypes.h>
    
    typedef union un {
        int32_t x;
        char ch[4];
    }un;
    
    int main() {
        un u;
        u.ch[0] = 1;
        u.ch[1] = 2;
        u.ch[2] = 3;
        u.ch[3] = 4;
        printf("u.x=0x%x\n", u.x);
        return 0;
    }
    
  • 在Python日志中输出文件名和函数名

    Python内置了日志模块,在默认情况下输出的日志是不带文件名和函数名的,这样在排查问题时,遇到相似的日志就变得容易混淆,可以通过设置将输出的日志中带有文件名和函数名。参考了stackoverflow的回答,详细代码如下。

    import logging
    log = logging.getLogger('root')
    LOG_FORMAT = "%(filename)s:%(lineno)s %(funcName)s() %(message)s"
    logging.basicConfig(format=LOG_FORMAT)
    log.setLevel(logging.DEBUG)
    

    参考资料
    stackoverflow

  • Linux常用命令行指令 - xargs

    基础用法

    xargs 指令默认情况下从 stdin 中读取信息,按照空格分或换行区分开,并且执行输入的命令(默认的命令是 /bin/echo )。命令可以通过 xargs 的参数指定执行一次或多次,命令的执行参数为自身初始参数追加上由 stdin 读到的内容,命令在执行时会忽略空行。

    xargs reads items from the standard input, delimited by blanks (which can be protected with double or single quotes or a backslash) or newlines, and executes the command (default is /bin/echo) one or more times with any initial-arguments followed by items read from standard input. Blank lines on the standard input are ignored.

    在命令行输入 xargs 命令,输入 “a b c d” 后按回车,再按 ctrl+d 完成本次输入,则 xargs 的默认 echo 命令会将接收到的字符串打印出来,效果如下所示。

    # xargs
    a b c d
    a b c d
    

    xargs 单独使用时功能比较单一,但是它与其他指令一起使用十分强大。

    xargsfind 组合使用

    常见的使用方法是使用 find 指令找到指定的文件,组合以 xargs 指令实现对指定文件的操作。

    find . -name "demo_" | xargs file -i
    

    由于 xargs 是使用空格作为各个参数的分隔符,如果遇到的文件名中含有空格,则并不能按照预期处理。这时,可以使用 find 指令的 -print0 将含有空格的文件名转化为字符串,再结合 xargs-0 参数就能完成文件名中含有空格的处理,改进后的命令如下。

    find . -name "demo_" -print0 | xargs -0 file -i
    

    xargsgrep 组合使用

    常用命令如下,能够实现对指定规则文件的搜索匹配。

    find . -name "*.log" | xargs grep error
    

    使用 -n 参数让 xargs 的命令重复执行

    如果想用 find 指令完成多个匹配规则的搜索,则可以使用 -n 参数将 xargs 的内容分割成多个。
    下面的命令会执行3次 find 命令, xargs 会将接收的到的内容按照单词数为“1”分割,分3次传送给 find 指令。

    echo "*.c" "*.cpp" "*.h" | xargs -n 1 find . -name
    

    使用 -t 参数让 xargs 打印将要执行的命令

    xargs 将指令的命令打印出来主要是方便查看命令执行情况,也方便在出现错误时进行调试。

    find . -name *.log | xargs -t rm -f
    

    使用 -p 参数让 xargs 在执行命令前询问用户

    增加 -p 参数后, xargs 在每次执行命令前会将指令打印出来并询问用户,只有用户输入 “y” 时才会真正执行该条命令。

    echo "*.c" "*.cpp" "*.h" | xargs -n 1 -p find . -name
    

    使用 -I 参数让 xargs 进行参数替换

    xargs 默认会将读取到的内容追加在指令命令后面作为参数,使用 -I 参数能够控制命令参数的组合方式。
    以下命令会将当前目录中的可执行文件移动到bin文件夹下。

    find . -executable -type f -print0 | xargs -0 -I {} mv {} ./bin
    
  • Linux常用命令行指令 - ipcs

    IPCinter process communication 的缩写,这项技术能够让进程间相互通信。
    Q:每个进程都有自己的地址空间和独立的用户空间,那么进程间是如何通信的呢?
    A:内核,也就是操作系统的心脏,它能够访问整个操作系统的内存。我们可以要求内核分配一块用于进程间交互的空间。

    几种进程间通信的方法

    进程间通信的方法有很多,有些支持同机器上进程的信息交互,有些支持跨机器的进程交互。

    • 管道 : pipes,管道提供了进程间交换信息的方法。
    • 共享内存 : shared memory,一个进程创建一块其他进程能够访问的内存空间,多个进程可以通过共享内存进行数据交换。
    • 消息队列 : message queue,消息队列是一个固定结构、有序的内存段,多个进程可以存放和取回数据。
    • 信号量 : semaphores,信号量提供了多进程访问同一资源的同步机制,信号量不负责传递数据,它协调对共享资源的访问。

    常用ipcs指令

    列出所有的IPC设备

    ipcs -a
    

    列出所有的消息队列

    ipcs -q
    

    列出所有的信号量

    ipcs -s
    

    列出所有的共享内存

    ipcs -m
    

    获取与IPC设备信息

    ipcs -q -i msq_id
    

    列出IPC设备的限制

    ipcs -l
    

    列出IPC设备的创建者和拥有者

    ipcs -m -c
    

    列出最近使用IPC设备的进程id

    ipcs -m -p
    

    列出IPC设备的最后访问时间

    ipcs -s -t
    

    列出IPC设备的当前使用状态

    ipcs -u
    
  • 使用gdb调试多线程程序

    查看当前线程信息

    将进程中的各个线程信息显示出来

    (gdb) info threads
    

    切换到指定进程

    (gdb) thread tid
    

    向指定的线程发送自定的指令

    (gdb) thread apply tid/all args
    

    常用的指定是查看所有线程的调用堆栈 thread apply all bt ,这个指令与 pstack 命令有些相似。

    gdb默认会自动捕捉新产生线程
    会在产生一个新的线程时会显示出LWP的字样提示用户,LWP = light weight process
    可以设置gdb是否提示线程相关的事件

    (gdb) set print thread-events on/off
    (gdb) show print thread-events
    

    为指定的线程设置断点

    含有多线程的程序,可以为单独的线程设置断点

    (gdb) break linespec thread tid
    

    任何时候当你的程序在GDB模式下停止的时候,包括当前调试线程的所有线程都会停下来,不会对继续对当前进程造成更改。这时你可以在线程间进行切换,查看整个进程的执行状况。

    Whenever your program stops under GDB for any reason, all threads of execution stop, not just the current thread. This allows you to examine the overall state of the program, including switching between threads, without worrying that things may change underfoot.

    防止gdb自动切换线程

    在调试gdb程序时,在单步执行时,会出现线程间跳转切换,这样对跟踪代码执行状态十分不方便。
    可以通过设置 scheduler-locking 让gdb在所调试的线程中运行,防止线程的自动切换。

    (gdb) set scheduler-locking step
    

    可以执行以下命令查看当前 scheduler-locking 的设置

    (gdb) show scheduler-locking
    

    scheduler-locking 有三种模式

    1. off 任何线程在任何时候都能执行
    2. on 只有当前线程能够执行
    3. step 为单步执行优化的模式,比较适合一般的调试

    Set the scheduler locking mode. If it is off, then there is no locking and any thread may run at any time. If on, then only the current thread may run when the inferior is resumed. The step mode optimizes for single-stepping. It stops other threads from "seizing the prompt" by preempting the current thread while you are stepping. Other threads will only rarely (or never) get a chance to run when you step. They are more likely to run when you `next' over a function call, and they are completely free to run when you use commands like `continue', `until', or `finish'. However, unless another thread hits a breakpoint during its timeslice, they will never steal the GDB prompt away from the thread that you are debugging.