Linux常见问题及解决方案

Linux常见问题及解决方案汇总,持续更新……

1. 运行自己编译的程序时提示找不到.so文件

例如:

./mnist_caffe: error while loading shared libraries: libarmnn.so: cannot open shared object file: No such file ordirectory

出现该问题的原因是运行./mnist_caffe的时候找不到依赖的libarmnn.so。实际上libarmnn.so是已经编译好的文件,其所在目录是/home/lg/armnnSDK/armnn
解决常用方法有两种,一是执行sudo gedit /etc/ld.so.conf,然后将libarmnn.so所在的目录/home/lg/armnnSDK/armnn加入其中,再执行sudo /sbin/ldconfig使其生效即可。不过一般对于自己编译生成的文件不这么做,自己编译生成的库一般采用方法二配置路径。二是执行sudo gedit ~/.bash_profile,然后加入export LD_LIBRARY_PATH=/home/lg/armnnSDK/armnn:$LD_LIBRARY_PATH。或是如果只在当前终端生效,只执行export LD_LIBRARY_PATH=/home/lg/armnnSDK/armnn:$LD_LIBRARY_PATH也是可以的。

2. 查看静态库中包含哪些.o文件

ar -tv libXXX.a

3. 查看静态库或.o中包含哪些函数

objdump -tT libXXX.aobjdump -tT libXXX.o

4. 如何生存静态库

ar -rcs libXXX.a A.o B.o
其中A.o B.o用下面方法生成,指定gcc -c生成的就是.o文件:
gcc -c -O3 A.c -I ./gcc -c -O3 B.c -I ./

5. 统计文件、文件夹个数

当前目录文件个数:ls -l|grep "^-"| wc -l
当前目录以.o结尾的文件个数:ls -l *.o|grep "^-"| wc -l
当前目录及子目录文件个数:ls -lR|grep "^-"| wc -l
当前目录文件夹包含的文件夹个数:ls -l|grep "^d"| wc -l
当前文件夹及子文件夹包含的文件夹个数ls -lR|grep "^d"| wc -l

5.1 递归地移动指定类型的文件到目的目录

find . -name '10-*.jpg' -exec mv {} ../ \;
find . -name '*.xml' -exec mv {} ../img \;
-exec mv {} ../img \; 运行mv命令;
{}代表find到的所有内容;
../表示移动的目标位置;
\; 表示结束/bin/mv命令。

6. Linux系统(Linux服务器、Linux设备、IPC等)挂载Windows的目录

6.1 cifs挂载

对于嵌入式开发往往开发源码在Windows上,编译在Linux上,这时如果将Windows目录挂载到Linux下则会大大提高开发效率。
对于IPC,串口登录IPC,进入IPC根目录/,一般挂载点选择/mnt/tmp,也可以是其它目录。
mount -t cifs //Windows的IP地址/Windows上要共享文件夹名字 /mnt/挂载点名字 -o username=XXX, password=XXX
例如在windows的D盘建立了文件夹win-mnt,然后设置了共享,假设Windows的IP地址是10.182.13.58,挂载方式是:
mount -t cifs //10.182.13.58/win-mnt ./mnt -o username=garylau, password=mypasswd
对于Linux系统其它设备也是类似,在Linux下创建好要挂载的目录(挂载点),将Windows目录挂载到Linux挂载点即可。
删除挂载:umount ./mnt
有时需要Linux下的挂载点不仅有读取的权限还要有写入的权限,此时的指令需要添加dir_mode=07 77,file_mode=0777,即:
mount -t cifs //Windows的IP地址/Windows上要共享文件夹名字 /mnt/挂载点名字 -o username=XXX,password=XXX,dir_mode=0777,file_mode=0777
如果挂载时报如下错误,则是由于NFS(Network File System,网络文件系统服务器)有多个版本,V2、V3、V4,而且各版本同时运行,因此挂载时需要说明版本号造成的。可通过指定版本号来解决:mount -t cifs //Windows的IP地址/Windows上要共享文件夹名字 /mnt/挂载点名字 -o username=XXX, password=XXX, vers=2.1

mount error(121): Remote I/O error
Refer to the mount.cifs(8) manual page (e.g. man mount.cifs)

** 6.2 nfs挂载**

先下载nfs1219.exe,使用该软件将windows下要共享给linux的目录设置为nfs格式,再使用指令mount -t nfs -o nolock windowsIP:/windows盘符/盘符下路径 mnt/进行挂载。例如在windows的D盘建立了文件夹win-mnt,然后使用软件NFS Server添加了共享路径d:\win-mnt,假设Windows的IP地址是10.182.13.58,挂载方式是:mount -t nfs -o nolock 10.182.13.58:/d/win-mnt mnt/

7. 查找当前目录下的所有文件中是否含有某个字符串

find ./ |xargs grep -ri "VGGNetBody"
如果想只列出文件名则加上 -l
find ./ |xargs grep -ri "VGGNetBody -l"

8. undefined reference to sqrt

在使用sqrt等在#include 中的函数时虽然添加了头文件,但是在编译的时候仍然会报 undefined reference to sqrt的错误,此时在编译的指令中添加-lm即可,参考此处。

9. 如何在 Linux 中清除缓存(Cache)?

释放系统内存缓存:/proc/sys/vm/drop_caches
每个 Linux 系统有三种选项来清除缓存而不需要中断任何进程或服务。
仅清除页面缓存(PageCache)
sync; echo 1 > /proc/sys/vm/drop_caches
清除目录项和inode
sync; echo 2 > /proc/sys/vm/drop_caches
清除页面缓存,目录项和inode
sync; echo 3 > /proc/sys/vm/drop_caches

10. 查看内存使用情况

查看系统中内存使用情况:/proc/meminfo
进程的内存使用情况:/proc/28040/status
查询内存总使用率:free
查询进程 cpu 和内存使用占比:top
进程消耗内存占比和排序:ps aux –sort -rss

10.1 Linux free含义

linux下free命令详解
在这里插入图片描述
Mem 行(第二行)是内存的使用情况。
Swap 行(第三行)是交换空间的使用情况。
total 列显示系统总的可用物理内存和交换空间大小。
used 列显示已经被使用的物理内存和交换空间。
free 列显示还有多少物理内存和交换空间可用使用。
shared 列显示被共享使用的物理内存大小。
buff/cache 列显示被 buffer 和 cache 使用的物理内存大小。
available 列显示还可以被应用程序使用的物理内存大小。

11. 查看linux系统位数

a. getconf LONG_BIT
b. uname -a
c. uname -r
d. cat /proc/version

12. Makefile

简要说明写makefile的基本步骤。
a. 指明搜索路径,例如VPATH=src includevpath %.cpp srcvpath %.h include等;
b. 指明终极target,例如all: run
c. 指明中间目标,例如OBJ = main.o factorial.o hello.o等;
d. 生成中间目标,例如%.o : %.cpp等;
e. 指明clear要执行的操作,例如clean:; rm *.o run等。
文件名使用通配符
makefile中表示文件名时可以使用通配符。可使用的通配符有:*,?,[...]。但是在makefile中这些通配符并不是可以用在任何地方,makefile中通配符可以出现在一下两种场合:a. 可用在规则的目标、依赖中,make在读取makefile时会自动对其进行匹配处理(通配符展开);b.可出现在规则的命令中,通配符的通配处理是在shell中执行此命令时完成的。
除了这两种情况外的其它上下文中,不能直接使用通配符。而是需要通过函数wildcard来实现。

13. 理解内存分配

本小节主要参考你真的理解内存分配吗?如有不妥请联系修改或删除
test_virtual_physics_address.hpp

#include 
#include 

namespace test_virtual_physics_address {

    auto main() -> int {
        std::cout << "testing test_virtual_physics_address......" << std::endl;

        int *p = (int *)malloc(1024*1024*1024);
        _sleep(3600000);

		std::cout << "------------------------------" << std::endl;

        return 0;
    }
}

main.cpp

#include "test_virtual_physics_address.hpp"
#include 

int main()
{
    std::cout << "__cplusplus: " << __cplusplus << std::endl;

    test_virtual_physics_address::main();

    std::cout << "The end." << std::endl;
}

查看内存使用情况cat /proc/397/status

Linux常见问题及解决方案_第1张图片
图1 cat /proc/397/status

VmRSS表示进程使用的物理内存大小,但明明申请了1GB 的内存,为什么只显示使用 10560KB 的内存呢?这就涉及到物理内存与虚拟内存。

13.1 物理内存与虚拟内存

物理内存:也就是安装在计算机中的内存条,比如安装了 2GB 大小的内存条,那么物理内存地址的范围就是 0 ~ 2GB。

虚拟内存:虚拟的内存地址。由于 CPU 只能使用物理内存地址,所以需要将虚拟内存地址转换为物理内存地址才能被 CPU 使用,这个转换过程由 MMU(Memory Management Unit,内存管理单元) 来完成。虚拟内存 大小不受 物理内存 大小的限制,在 32 位的操作系统中,每个进程的虚拟内存空间大小为 0 ~ 4GB。

程序中使用的内存地址都是虚拟内存地址,也就是说,通过malloc函数申请的内存都是虚拟内存。实际上,内核会为每个进程管理其虚拟内存空间,并且会把虚拟内存空间划分为多个区域,如下图所示:

Linux常见问题及解决方案_第2张图片
图1 cat /proc/397/status

上述各区域的作用:

  • 代码段:用于存放程序的可执行代码,段的基地址放在寄存器CS中,指令指针寄存器IP用来表示下一条指令在段中的偏移地址。
  • 数据段:用于存放程序的全局变量和静态变量以及程序处理的数据,段的基地址存放在寄存器DS中。对数据段中的某个数据进行操作时,直接在汇编代码中通过立即数或寄存器来指定偏移地址。
  • 堆空间:用于存放由malloc申请的内存。
  • 栈空间:用于存放函数的参数和局部变量。
  • 内核空间:存放 Linux 内核代码和数据。

段,本质上是把内存上的某一块连续的存储空间,专门存储某一类的数据。之所以能够这么做,是因为 CPU 通过以上几个寄存器,让我们这样的“安排”称为一种可能。CPU 将内存中的某个段的内容当做代码,是因为 CS:IP 指向了那里;CPU 将某个段当做栈,是因为 CS:SP 指向了那里。

13.2 brk指针

通过malloc申请的内存地址是由堆空间分配的(其实还有可能从mmap区分配,这种情况暂时忽略)。在内核中,使用一个名为brk的指针来表示进程的堆空间的顶部,如下图2所示:

Linux常见问题及解决方案_第3张图片
图2 brk指针

通过移动brk指针就可以达到申请(向上移动)和释放(向下移动)堆空间的内存。例如申请1024字节时,只需要把brk向上移动1024字节即可,如下图3所示

Linux常见问题及解决方案_第4张图片
图3 移动brk指针达到申请释放内存的目的

事实上,malloc函数就是通过移动brk指针来实现申请和释放内存的,Linux 提供了一个名为brk()的系统调用来移动brk指针。

13.3 内存映射

malloc函数只是移动brk指针,但并没有申请物理内存,虚拟内存地址必须映射到物理内存地址才能被使用。如下图4所示:

Linux常见问题及解决方案_第5张图片
图4 虚拟地址映射到物理地址

虚拟地址首先经过段转换,得到线性地址;然后线性地址再经过分页转换,得到最终的物理地址。具体转换过程如图5所示:

Linux常见问题及解决方案_第6张图片
图5 虚拟地址映射到物理地址过程

只有触发缺页异常时才会触发申请新的物理内存;缺页异常触发的条件是对(没有映射过的)虚拟地址进行读写操作。Linux 内核会对缺页异常进行修复,修复过程如下:

  • 获取触发缺页异常的虚拟内存地址(即读写该虚拟内存地址导致了缺页异常)。
  • 查看此虚拟内存地址是否被申请(是否在brk指针内),如果不在brk指针内,将会导致 Segmention Fault错误(也就是常见的coredump),进程将会异常退出。
  • 如果虚拟内存地址在brk指针内,那么将此虚拟内存地址映射到物理内存地址上,完成缺页异常修复过程,并且返回到触发异常的地方进行运行。
    由此可知,不对申请的虚拟内存地址进行读写操作是不会触发申请新的物理内存。所以,这就解释了在章节13开始处为什么申请1GB的内存,但实际上只使用了10560KB的物理内存。

14. 内存对齐

The malloc() and calloc() functions return a pointer to the allocated memory, which is suitably aligned for any built-in type.

许多计算机体系结构要求特定的类型必须放在特定的内存地址上。例如它可能要求指针的地址必须是4的倍数或double的地址必须是8的倍数。如果没有这个约束条件,可能会导致运行时硬件异常。有些体系结构是如果齐位条件获得满足便提供最佳效率。

数据对齐分为自然对齐和强制对齐,注意这里讲的对齐是指数据地址的对齐。 自然对齐,各个类型变量的内存地址必须是其类型本身的整数倍。结构体对齐到成员最大长度类型的整数倍。

不同的硬件平台对存储空间的处理上有很大的不同,有些平台对某些类型的数据只能从特定地址开始存取否则就会出错;另外一些平台可能不会出错但会影响处理效率,本来一个指令周期就可以完成的操作,可能需要两个指令周期还需要把数据拼在一起处理。例如,对于从奇数边界去访问unsigned short型变量,显然不符合对齐的规定。在x86上,类似的操作只会影响效率,但是在MIPS或者sparc上,可能就是一个error,因为它们要求必须字节对齐。

14.1 对于标准数据类型,其地址是它的长度的整数倍。

操作系统自己也是这么做的,如下代码展示了栈上(跟堆上变量只是在内存不同区而已)变量地址。

#include 
#include 
#include 

namespace test_align {

    auto main() -> int {
        std::cout << "testing test_align......\n" << std::endl;
        
        unsigned char a = 0;
        std::cout << typeid(a).name() << std::endl;
        printf("&a = %p, sizeof(a) = %d\n", &a, sizeof(a));
        if(size_t(&a) % sizeof(a) == 0) {
            std::cout << "a's address starts with times of sizeof(a)." << std::endl;
        }
        std::cout << std::endl;
        
        short b = 0;
        std::cout << typeid(b).name() << std::endl;
        printf("&b = %p, sizeof(b) = %d\n", &b, sizeof(b));
        if(size_t(&b) % sizeof(b) == 0) {
            std::cout << "b's address starts with times of sizeof(b)." << std::endl;
        }
        std::cout << std::endl;

        int c = 0;
        std::cout << typeid(c).name() << std::endl;
        printf("&c = %p, sizeof(c) = %d\n", &c, sizeof(c));
        if(size_t(&c) % sizeof(c) == 0) {
            std::cout << "c's address starts with times of sizeof(c)." << std::endl;
        }
        std::cout << std::endl;

        long d = 0;
        std::cout << typeid(d).name() << std::endl;
        printf("&d = %p, sizeof(d) = %d\n", &d, sizeof(d));
        if(size_t(&d) % sizeof(d) == 0) {
            std::cout << "d's address starts with times of sizeof(d)." << std::endl;
        }
        std::cout << std::endl;

        long long e = 0;
        std::cout << typeid(e).name() << std::endl;
        printf("&e = %p, sizeof(e) = %d\n", &e, sizeof(e));
        if(size_t(&e) % sizeof(e) == 0) {
            std::cout << "e's address starts with times of sizeof(e)." << std::endl;
        }
        std::cout << std::endl;

        float f = 0;
        std::cout << typeid(f).name() << std::endl;
        printf("&f = %p, sizeof(f) = %d\n", &f, sizeof(f));
        if(size_t(&f) % sizeof(f) == 0) {
            std::cout << "f's address starts with times of sizeof(f)." << std::endl;
        }
        std::cout << std::endl;

        double g = 0;
        std::cout << typeid(g).name() << std::endl;
        printf("&g = %p, sizeof(g) = %d\n", &g, sizeof(g));
        if(size_t(&g) % sizeof(g) == 0) {
            std::cout << "g's address starts with times of sizeof(g)." << std::endl;
        }
        std::cout << std::endl;

        return 0;
    }
}

cmake -G "Visual Studio 15 2017" -A Win32 ..结果如下:

unsigned char
&a = 0059F743, sizeof(a) = 1
a's address starts with times of sizeof(a).

short
&b = 0059F734, sizeof(b) = 2
b's address starts with times of sizeof(b).

int
&c = 0059F728, sizeof(c) = 4
c's address starts with times of sizeof(c).

long
&d = 0059F71C, sizeof(d) = 4
d's address starts with times of sizeof(d).

__int64
&e = 0059F70C, sizeof(e) = 8

float
&f = 0059F700, sizeof(f) = 4
f's address starts with times of sizeof(f).

double
&g = 0059F6F0, sizeof(g) = 8
g's address starts with times of sizeof(g).

cmake -G "Visual Studio 15 2017" -A x64 ..结果如下:

unsigned char
&a = 000000B7C39FFA84, sizeof(a) = 1
a's address starts with times of sizeof(a).

short
&b = 000000B7C39FFAA4, sizeof(b) = 2
b's address starts with times of sizeof(b).

int
&c = 000000B7C39FFAC4, sizeof(c) = 4
c's address starts with times of sizeof(c).

long
&d = 000000B7C39FFAE4, sizeof(d) = 4
d's address starts with times of sizeof(d).

__int64
&e = 000000B7C39FFB08, sizeof(e) = 8
e's address starts with times of sizeof(e).

float
&f = 000000B7C39FFB24, sizeof(f) = 4
f's address starts with times of sizeof(f).

double
&g = 000000B7C39FFB48, sizeof(g) = 8
g's address starts with times of sizeof(g).

14.2 结构体的对齐

结构体的自然对齐,成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量类型所占用的字节数的倍数, 且结构的大小为该结构中占用最大空间的类型所占用的字节数的倍数

char: 偏移量必须为sizeof(char) 即1的倍数
short: 偏移量必须为sizeof(short) 即2的倍数
int: 偏移量必须为sizeof(int) 即4的倍数
float: 偏移量必须为sizeof(float) 即4的倍数
double: 偏移量必须为sizeof(double) 即8的倍数

结构体对齐示例:

#include 
#include 

namespace test_align {
    auto main() -> int {
        std::cout << "testing test_align......\n" << std::endl;

        typedef struct _ST_A_
        {
           char c1;
           int i;
           char c2;
           double d;
        }stA;
        
        typedef struct _ST_B_
        {
           char c1;
           char c2;
           int i;
           double d;
        }stB;

        printf("sizeof double, int, char = %d, %d, %d\n", sizeof (double), sizeof (int), sizeof (char));
        printf("sizeof stA = %d, stB = %d\n", sizeof(stA), sizeof(stB));
        
        return 0;
    }
}

输出:

sizeof double, int, char = 8, 4, 1
sizeof stA = 24, stB = 16

14.2 结构体中有嵌套符合成员时的对齐

当结构体中有嵌套符合成员时,复合成员相对于结构体首地址偏移量是复合成员最宽基本类型大小的整数倍。

#include 
#include 

namespace test_align {
    auto main() -> int {
        std::cout << "testing test_align......\n" << std::endl;

        typedef struct _ST_A_
        {
           char c1;
           int i;
           char c2;
           double d;
        }stA;
        
        typedef struct _ST_C_
        {
           char c;
           stA sta;
        }stC;

        printf("sizeof double, int, char = %d, %d, %d\n", sizeof (double), sizeof (int), sizeof (char));
        printf("sizeof stA = %d, stC = %d\n", sizeof(stA), sizeof(stC));

        return 0;
    }
}

输出:

sizeof double, int, char = 8, 4, 1
Sizeof stA = 24, stC = 32

14.3 强制对齐#pragma pack(n)

使用指令#pragma pack(n),C编译器将按照n个字节对齐;使用指令#pragma pack(),取消自定义字节对齐方式

#pragma pack(n)用来设定变量以n字节对齐方式,变量存放的起始地址的偏移量:

  1. 如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式
  2. 如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。
  3. 结构的总大小也有一个约束条件,如果n大于等于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须是n的倍数。

示例1:

#include 
#include 

namespace test_align {

    auto main() -> int {
        std::cout << "testing test_align......\n" << std::endl;

#pragma pack(1)
        typedef struct _ST_A_
        {
           char c1;
           int i;
           char c2;
           double d;
        }stA;
#pragma pack()

// n=2,对于小于2字节的char对齐方式是2;对于大于2字节的int double使用自然对齐
#pragma pack(2)
        typedef struct _ST_B_
        {
           char c1;
           int i;
           char c2;
           double d;
        }stB;
#pragma pack()


#pragma pack(3)   // [MinGW Makefiles] warning: alignment must be a small power of two, not 3 
                  // [Visual Studio 15 2017] warning C4086: 杂注参数应为“1”、“2”、“4”、“8”或者“16”
        typedef struct _ST_C_
        {
           char c1;
           int i;
           char c2;
           double d;
        }stC;
#pragma pack()


#pragma pack(4)
        typedef struct _ST_D_
        {
           char c1;
           int i;
           char c2;
           double d;
        }stD;
#pragma pack()


#pragma pack(8)
        typedef struct _ST_E_
        {
           char c1;
           int i;
           char c2;
           double d;
        }stE;
#pragma pack()

// n=16,比最大的double字节数还大,则使用自然对齐方式
#pragma pack(16)
        typedef struct _ST_F_
        {
           char c1;
           int i;
           char c2;
           double d;
        }stF;
#pragma pack()


        typedef struct _ST_DEFAULT_
        {
           char c1;
           int i;
           char c2;
           double d;
        }stDefault;
        
        printf("sizeof stA = %d, stB = %d\n", sizeof(stA), sizeof(stB));
        printf("sizeof stC = %d, stD = %d\n", sizeof(stC), sizeof(stD));
        printf("sizeof stE = %d, stF = %d\n", sizeof(stE), sizeof(stF));
        printf("sizeof stDefault = %d\n", sizeof(stDefault));

        return 0;
    }
}

输出:

sizeof stA = 14, stB = 16
sizeof stC = 24, stD = 20
sizeof stE = 24, stF = 24
sizeof stDefault = 24

注意上面#pragma pack(3)时应该是使用了自然对齐方式,而不是自定义的3

示例2:

#include 
#include 

namespace test_align {
    auto main() -> int {
        std::cout << "testing test_align......\n" << std::endl;

#pragma pack(8)
        struct s1
        {
           short a;
           long b;
        };
        
        struct s2
        {
           char c;
            s1 d;
        long long e;
        };
        
        struct s3
        {
            char c;
            short a;
            long b;
            long long e;
        };
#pragma pack()

        printf("sizeof s1 = %d\n", sizeof(s1));
        printf("sizeof s2 = %d\n", sizeof(s2));
        printf("sizeof s3 = %d\n", sizeof(s3));

        return 0;
    }
}

(Visual Studio 15 2017)输出:

sizeof s1 = 8
sizeof s2 = 24
sizeof s3 = 16

15. 将安装好的软件路径添加到环境变量

例如将anaconda3的bin路径添加到环境变量:
gedit ~/.bashrc
添加如下内容:

# 添加anaconda3路径
export PATH=/home/lg/05.Softwares/anaconda3/bin:$PATH

然后执行:
source ~/.bashrc

16. 卸载软件

sudo apt-get remove flameshot,flameshot是一款截图软件。

17. 查看某个目录挂载在哪个硬盘

df -k 目录,例如df -k /home
由下图可知home目录挂载在磁盘/dev/nvme1n1p3上,挂载点是根目录/
请添加图片描述

18. 查看Linux系统发行版本的命令

cat /etc/issue
在这里插入图片描述

19. 查看用户组

cat /etc/group

20. 查看显卡型号

lspci | grep -i nvidia或者ubuntu-drivers devices
查看驱动版本sudo dpkg --list | grep nvidia-*cat /proc/driver/nvidia/version
查看ubuntu系统发布版本号
cat /etc/issuelsb_release -a
查看内核版本号uname -sr``uname -a
查看CPU个数cat /proc/cpuinfo | grep "physical id" | uniq | wc -l
查看CPU核数cat /proc/cpuinfo | grep "cpu cores" | uniq
查看CPU型号cat /proc/cpuinfo | grep 'model name' |uniq
GPU列表信息nvidia-smi
显示显卡信息nvidia-settings
查看显卡型号lshw -c video

21. ubuntu22.04设置没了?

sudo apt-get install unity-control-center
sudo apt-get install gnome-control-center

Reference

  1. 你真的理解内存分配吗?
  2. Linux从头学03:如何告诉 CPU,代码段、数据段、栈段在内存中什么位置?
  3. Linux字节对齐的那些事
  4. 标题:自然对齐
  5. The Linux Programming Interface
Linux常见问题及解决方案_第7张图片
The Linux Programming Interface

你可能感兴趣的:(Linux,Linux)