[GNU/Linux]标准Makefile 1.2 使用动态库

######### 标准Makefile Lv1.2 / 使用动态库 ########
EXE=helloworld
SUBDIR=src object

#CXXFLAGS:编译选项, LDFLAGS:链接选项
CXXFLAGS += -I/home/mytest/example/include/
LDFLAGS += -L/home/mytest/example/lib -lexample

CXX_SOURCES =$(foreach dir,$(SUBDIR), $(wildcard $(dir)/*.cpp))
CXX_OBJECTS=$(patsubst  %.cpp, %.o, $(CXX_SOURCES))
DEP_FILES  =$(patsubst  %.o,  %.d, $(CXX_OBJECTS))

$(EXE): $(CXX_OBJECTS)
    g++  $(CXX_OBJECTS) -o $(EXE) $(LDFLAGS)
    
%.o: %.cpp
    g++  -c  $(CXXFLAGS) -MMD $<  -o  $@

-include $(DEP_FILES)

clean: 
    rm  -rf  $(CXX_OBJECTS)  $(DEP_FILES)  $(EXE)

test:
    echo $(CXX_OBJECTS)

 

[GNU/Linux]代码中调用系统命令

#include <stdio.h>

// 返回: -1, 命令行出错; >=0, 实际输出的字节数
int execute(const char* cmdline, char* output, int maxsize)
{
    // 执行命令, 获取输出流
    FILE* fp = popen(cmdline, "r");
    if(!fp) return -1;
    
    // 读取maxsize-1个字节
    int size = 0; // 已读取的字节数
    maxsize -= 1;
    while(!feof(fp))
    {
        int n = fread(output+size, 1, maxsize - size, fp);
        if(n<=0) break;
        size += n;
    }
    
    // 关闭输出
    pclose(fp);
    
    output[size] = 0;
    return size; // 实际读取的字节数	
}

int main()
{
    char buf[1024];
    int n = execute("ifconfig", buf, 1024);
    if(n> 0)
    {
        buf[n] = 0;
        printf("------ output -------\n");
        printf("%s\n", buf);
    }
    return 0;
}

 

[GNU/Linux]系统调用读取目录

dir.h

#ifndef _DIR_INFO_H
#define _DIR_INFO_H

// 《语法篇》的第30章,STL里的一个类模板
#include <vector>
using namespace std;

struct DirInfo
{
    char name[256];
};

int listdir(const char*  dir, vector<DirInfo>& results);


#endif

dir.cpp

#include <stdio.h>
#include <string.h>

#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>

#include "dir.h"

// 失败:返回-1;成功,返回0
int listdir(const char* dir, vector<DirInfo>& results)
{
    // 打开目录
    DIR *  _dir = opendir(dir);
    if(!_dir)
    {	
        return -1;
    }
    
    // 读取目录
    dirent* entry = readdir(_dir);
    while(entry)
    {
        const char* name = entry->d_name;
        if(strcmp(name, ".") != 0
            && strcmp(name, "..") != 0)
        {
            //printf("found entry: %s \n", name);
            DirInfo info;
            strcpy(info.name, name);
            results.push_back(info);
        }
        
        entry = readdir(_dir);
    }
    
    // 关闭目录
    closedir(_dir);
    return 0;

}

main.cpp

#include <stdio.h>

#include "dir.h"

int main()
{
    vector<DirInfo> results;
    int ret = listdir("/home/mytest/project/K12_02/aaa", results);
    if(ret == 0)
    {
        for(int i=0; i<results.size(); i++)
        {
            printf("got: %s \n", results[i].name);
        }	
    }	
    
    return 0;
}

 

[GNU/Linux]互斥体和信号量

互斥体:概念请参考《应用篇》里的讲解。
在pthread库中提供互斥体的实现。
创建Mutex对象
pthread_mutex_t hMutex;
pthread_mutex_init(&hMutex, NULL);  // 创建
pthread_mutex_destroy(&hMutex);  // 销毁

 

使用Mutex
// 获取锁,阻塞等待。。。
if(pthread_mutex_lock(&hMutex) ==0)
{
//… 访问共享数据 …
// 释放锁
pthread_mutex_unlock(&hMutex);
}

 

另一种方式:trylock, 如果不能获锁,则立即返
回。
// 获取锁,阻塞等待。。。
if(pthread_mutex_trylock(&hMutex) == 0)
{
//… 访问共享数据 …
// 释放锁
pthread_mutex_unlock(&hMutex);
}

 

信号量:概念请参考《应用篇》里的讲解。
#include <unistd.h>
#include <semaphore.h>
创建Semaphore对象
sem_t hSem;
sem_init(&hSem, 1, initial_value);
sem_destroy(&hSem);

wait/post操作
sem_wait(&hSem);
sem_post(&hSem);
超时等待 sem_timedwait

 

[GNU/Linux]线程的面向对象封装

Thread.h

#ifndef _THREAD_H
#define _THREAD_H

#include <pthread.h>
#include <unistd.h>
#include <time.h>

class Thread
{
public:
    Thread();
    virtual ~Thread();

    // 创建并启动
    virtual int Run();

    // 等待和收回资源
    static void Join(Thread* thrd);

    // Sleep函数
    static void Msleep(int ms);
    static void Sleep(int s);

public:
    virtual int Routine() = 0;

private:
    pthread_t hThread;
};






#endif

Thread.cpp

#include <stdio.h>
#include "Thread.h"

Thread::Thread() 
{
}

Thread::~Thread()
{
}

static void* Thread_Proc_Linux(void* param)
{
    Thread* thrd = (Thread*) param;
    thrd->Routine();
    return NULL;
}

int Thread::Run()
{
    // 创建线程
    if(pthread_create(&hThread, NULL, 
        Thread_Proc_Linux, this) < 0)
    {
        return -1;
    }

    return 0;
}

void Thread::Join(Thread* th)
{
    pthread_join(th->hThread, NULL);
}

void Thread::Msleep(int ms)
{
    timespec ts;
    ts.tv_sec = ms / 1000;
    ts.tv_nsec = (ms % 1000) * 1000000;
    nanosleep(&ts, NULL);
}

void Thread::Sleep(int s)
{
    sleep(s);
}

 

main.cpp

#include <stdio.h>

#include "Thread.h"

class MyTask : public Thread
{
public:
    virtual int Routine()
    {
        for(int i=0; i<10; i++)
        {
            printf("MyTask: %d \n", i);
            Thread::Sleep(1);
        }
        return 0;
    }
};
// 
int main()
{
    MyTask t;
    t.Run();
    
    Thread::Join(&t);	
    printf("main exit.\n");
    return 0;
}

 

[GNU/Linux]线程的终止

#include <stdio.h>

// linux API / pthread
#include <unistd.h>
#include <pthread.h>

int quit_flag = 0; 

// 线程入口函数
void* Thread_Main(void* context)
{
    for(int i=0; i<10 && !quit_flag ; i++)
    {
        printf("in thread main: %d ...\n", i);
        sleep(1);
    }
    printf("thread exit.\n"); // 线程自然退出
    return NULL;
}

// 
int main()
{
    // 创建线程(同时启动线程)
    pthread_t handle;
    if(pthread_create(&handle, NULL, Thread_Main, NULL) < 0)
    {
        printf("failed to create thread!\n");
        return -1;
    }
    
    //getchar();
    //quit_flag=1;
    
    pthread_join(handle, NULL);
    
    printf("main exit.\n");
    return 0;
}

 

[GNU/Linux]pthread_create的参数说明

#include <stdio.h>
#include <stdlib.h>

// linux API / pthread
#include <unistd.h>
#include <pthread.h>

struct ThreadParam
{
    int id;
    int max;
};

// 线程入口函数
void* Thread_Main(void* context)
{
    ThreadParam* p = (ThreadParam*) context;
    for(int i=0; i<p->max; i++)
    {
        printf("thread: %d,  count: %d\n", p->id, i);
        sleep(1);
    }
    free(p);
    
    printf("thread : %d  exit . \n");
    return NULL;
}

// 
int main()
{
    // 创建线程(同时启动线程)
    pthread_t h1, h2;
    
    // 创建第1个线程
    ThreadParam* p1 = (ThreadParam*) malloc(sizeof(ThreadParam));
    p1->id = 1;
    p1->max = 10;
    if(pthread_create(&h1, NULL, Thread_Main, p1) < 0)
    {
        printf("failed to create thread!\n");
        return -1;
    }
    
    // 创建第2个线程
    ThreadParam* p2 = (ThreadParam*) malloc(sizeof(ThreadParam));
    p2->id = 2;
    p2->max = 20;
    if(pthread_create(&h2, NULL, Thread_Main, p2) < 0)
    {
        printf("failed to create thread!\n");
        return -1;
    }
    
    
    getchar();
    return 0;
}

 

[GNU/Linux]使用pthread库创建线程

#include <stdio.h>

// linux API / pthread
#include <unistd.h>
#include <pthread.h>

void  msleep(int ms)
{
    timespec ts;
    ts.tv_sec = ms / 1000;
    ts.tv_nsec = (ms % 1000) * 1000000;
    nanosleep(&ts, NULL);
}


// 线程入口函数
void* Thread_Main(void* context)
{
    while(1)
    {
        printf("i am a thread ...\n");
        //::sleep(1);
        msleep(100); // 500ms
    }
    return NULL;
}

// 
int main()
{
    // 创建线程(同时启动线程)
    pthread_t handle;
    if(pthread_create(&handle, NULL, Thread_Main, NULL) < 0)
    {
        printf("failed to create thread!\n");
        return -1;
    }
    
    getchar();
    return 0;
}

 

[GNU/Linux]Linux下读写文件

  • 以写方式打开文件
    int fd = open(“/home/mytest/123.txt”,
    O_WRONLY | O_CREAT,
    0644);
    参数1: 文件路径
    参数2:     标识位,O_WRONLY表示写入,
    O_CREAT表示当不存在时,创建新文件
    参数:0644,表示创建文件时的权限位
    (0644: 字面常量,8进制)

 

  • 以读方式打开文件
    int fd = open(“/home/mytest/123.txt”,
    O_RDONLY);
    参数1:文件路径
    参数2:O_RDONLY: 以只读方式打开
    参数3:省略

 

  • 写文件:
    write(fd,  buf,  n);
    读文件:
    int n = read(fd, buf, maxsize);
    关闭文件:
    close(fd);
    fd:  file descriptor文件描述符,是一个整数

 

  • 以下三者选一:
    O_RDONLY 只读方式
    O_WRONLY 以只写方式打开文件
    O_RDWR 以可读写方式打开文件
    额外的标识位:
    O_CREAT可与O_WRONLY联用,若欲打开的文件不存在则自
    动建立该文件
    O_TRUNC  可与O_WRONLY联用,在打开文件时清空文件
    O_APPEND可与O_WRONLY联用,表示追加内容
    O_NONBLOCK 表示以“非阻塞”方式读/写数据时

read.cpp

// ANSI C
#include <stdio.h>

// Linux API
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


int main()
{
    // 注:你是否对/home/mytest/123.txt有读权限
    int fd = open("/home/mytest/123.txt", O_RDONLY);
    if(fd < 0)
    {
        printf("failed to open file!\n");
        return -1;
    }
    
    char data[12];
    int n = read(fd, data, 12);	
    if(n>0)
    {
        data[n] = 0;
        printf("read: %s \n", data);
    }
    close(fd);
}

write.cpp

// ANSI C
#include <stdio.h>

// Linux API
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


int main()
{
    // 注:你是否对/home/mytest/目录有写权限?
    int fd = open("/home/mytest/123.txt", 
            O_WRONLY|O_CREAT, 
            0644);
    if(fd < 0)
    {
        printf("failed to open file!\n");
        return -1;
    }
    
    char data[12] = "hello";
    write(fd, data, 5);	
    close(fd);
}

stdc.cpp

// ANSI C
#include <stdio.h>
#include <string.h>

int main()
{
    // 注:你是否对/home/mytest/目录有写权限?
    FILE* fp = fopen("/home/mytest/aaa.txt", "wb");
    if(!fp)
    {
        // 无权访问
        printf("failed to open file!\n");
        return -1;
    }
    
    // 使用linux的标准换行符\n
    char buf[] = "hello\nworld\n";
    fwrite(buf, 1, strlen(buf), fp);
    fclose(fp);
}

 

 

[GNU/Linux]静态库的创建与使用

静态库,static library
标准命名: libxxx.a
第一步:编译,得到*.o文件
第二步:打包
ar ‐rcs   libxxx.a   file1.o  file2.o … fileN.o
(注:ar只是将*.o文件打个包而已,并非“链
接”)

演示:
g++ ‐c test1.cpp ‐o test1.o
g++ ‐c test2.cpp ‐o test2.o
ar ‐rcs libtest.a test1.o test2.o
查看静态库中的符号
nm  libtest.a
作者: 邵发

 

静态库的交付物:
‐ 头文件 *.h
‐ 库文件 libxxx.a

 

由于libtest.a本质是就是test1.o和test2.o打个包而
已,因此可以像.o文件一样使用
比较:
g++ main.cpp test1.o  test2.o  ‐o helloworld
g++ main.cpp  libtest.a ‐o helloworld
也就是说,在命令行里直接加上libtest.a的全路
径是可以的

 

如果 libtest.so 和 libtest.a同时存在。。。
则默认优先连接 libtest.so

 

当 libtest.so 和 libtest.a同时存在,以下两种方式可以强制
使用静态库:
① 使用全路径 【推荐】
g++ main.cpp  build/libtest.a ‐o helloworld
② ‐static : 强制所有的库都使用静态库版本【行不通】
g++ main.cpp ‐o helloworld ‐static ‐Lbuild ‐ltest
缺点:
所有的库(包括libc , libstdc++)都必须提供静态库版本,少
一个都不行。。
注:centos默认安装时不带 libc.a libstdc++.a ..