C语言高级编程技巧与最佳实践

目录

  1. 宏定义与预处理技巧
  2. 内存管理高级技巧
  3. 函数指针与回调机制
  4. 数据结构设计
  5. 并发与多线程
  6. 错误处理与异常机制
  7. 性能优化技巧
  8. 调试与测试技巧
  9. 跨平台编程
  10. 安全编程实践

宏定义与预处理技巧

1. 条件编译与平台检测

// 平台检测
#if defined(_WIN32) || defined(_WIN64)
    #define PLATFORM_WINDOWS
#elif defined(__linux__)
    #define PLATFORM_LINUX
#elif defined(__APPLE__)
    #define PLATFORM_MACOS
#endif

// 编译器检测
#if defined(__GNUC__)
    #define COMPILER_GCC
#elif defined(_MSC_VER)
    #define COMPILER_MSVC
#endif

// 版本检测
#if __STDC_VERSION__ >= 201112L
    #define C11_SUPPORTED
#endif

2. 强大的宏技巧

// 字符串化和连接
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define CONCAT(a, b) a##b

// 获取数组长度
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

// 容器of宏(从成员指针获取容器指针)
#define container_of(ptr, type, member) ({          \
    void *__mptr = (void *)(ptr);                    \
    ((type *)(__mptr - offsetof(type, member))); })

// 最大最小值
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))

// 交换变量(不使用临时变量)
#define SWAP(a, b) do { typeof(a) temp = a; a = b; b = temp; } while(0)

// 编译时断言
#define STATIC_ASSERT(condition, message) \
    typedef char static_assertion_##message[(condition) ? 1 : -1]

// 可变参数宏
#define DEBUG_PRINT(fmt, ...) \
    fprintf(stderr, "[DEBUG] %s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)

3. 现代C语言特性

// C11泛型选择
#define generic_max(a, b) _Generic((a), \
    int: max_int, \
    float: max_float, \
    double: max_double \
)(a, b)

// 静态断言(C11)
_Static_assert(sizeof(int) >= 4, "int must be at least 4 bytes");

// 线程局部存储(C11)
_Thread_local int thread_var;

内存管理高级技巧

1. 内存池设计

typedef struct {
    void *memory;
    size_t size;
    size_t used;
    size_t block_size;
} memory_pool_t;

memory_pool_t* create_pool(size_t size, size_t block_size) {
    memory_pool_t *pool = malloc(sizeof(memory_pool_t));
    pool->memory = malloc(size);
    pool->size = size;
    pool->used = 0;
    pool->block_size = block_size;
    return pool;
}

void* pool_alloc(memory_pool_t *pool, size_t size) {
    if (pool->used + size > pool->size) return NULL;
    void *ptr = (char*)pool->memory + pool->used;
    pool->used += size;
    return ptr;
}

2. 智能指针模拟

typedef struct {
    void *ptr;
    void (*deleter)(void*);
    int *ref_count;
} smart_ptr_t;

smart_ptr_t make_smart_ptr(void *ptr, void (*deleter)(void*)) {
    smart_ptr_t sp = {ptr, deleter, malloc(sizeof(int))};
    *sp.ref_count = 1;
    return sp;
}

smart_ptr_t smart_ptr_copy(smart_ptr_t sp) {
    ++(*sp.ref_count);
    return sp;
}

void smart_ptr_free(smart_ptr_t *sp) {
    if (--(*sp->ref_count) == 0) {
        sp->deleter(sp->ptr);
        free(sp->ref_count);
    }
}

3. 内存对齐

// C11对齐
_Alignas(16) char aligned_buffer[256];

// 手动对齐
#define ALIGN_UP(x, align) (((x) + (align) - 1) & ~((align) - 1))
#define IS_ALIGNED(x, align) (((x) & ((align) - 1)) == 0)

void* aligned_malloc(size_t size, size_t alignment) {
    void *ptr = malloc(size + alignment - 1 + sizeof(void*));
    if (!ptr) return NULL;
    
    void **aligned_ptr = (void**)(((uintptr_t)ptr + sizeof(void*) + alignment - 1) & ~(alignment - 1));
    aligned_ptr[-1] = ptr;
    return aligned_ptr;
}

函数指针与回调机制

1. 面向对象风格编程

// 虚函数表模拟
typedef struct {
    void (*destroy)(void *self);
    void (*print)(void *self);
    int (*compare)(void *self, void *other);
} vtable_t;

typedef struct {
    vtable_t *vtable;
    // 具体数据
} object_t;

// 多态调用
#define CALL_METHOD(obj, method, ...) \
    ((obj)->vtable->method((obj), ##__VA_ARGS__))

2. 状态机实现

typedef enum {
    STATE_IDLE,
    STATE_RUNNING,
    STATE_PAUSED,
    STATE_STOPPED
} state_t;

typedef struct {
    state_t current_state;
    int (*handlers[4])(void *context, int event);
} state_machine_t;

int handle_idle(void *context, int event) {
    switch (event) {
        case EVENT_START:
            return STATE_RUNNING;
        default:
            return STATE_IDLE;
    }
}

3. 插件系统设计

typedef struct {
    const char *name;
    int version;
    int (*init)(void);
    void (*cleanup)(void);
    void* (*create_instance)(void);
} plugin_interface_t;

// 动态加载插件
#ifdef _WIN32
    #include <windows.h>
    #define LOAD_PLUGIN(name) LoadLibrary(name)
    #define GET_SYMBOL(handle, name) GetProcAddress(handle, name)
#else
    #include <dlfcn.h>
    #define LOAD_PLUGIN(name) dlopen(name, RTLD_LAZY)
    #define GET_SYMBOL(handle, name) dymbol(handle, name)
#endif

数据结构设计

1. 链表实现

typedef struct list_node {
    void *data;
    struct list_node *next;
    struct list_node *prev;
} list_node_t;

typedef struct {
    list_node_t head;
    size_t size;
    void (*destructor)(void*);
} list_t;

// 双向链表操作
void list_insert_after(list_t *list, list_node_t *node, void *data) {
    list_node_t *new_node = malloc(sizeof(list_node_t));
    new_node->data = data;
    new_node->next = node->next;
    new_node->prev = node;
    
    if (node->next) node->next->prev = new_node;
    node->next = new_node;
    list->size++;
}

2. 哈希表实现

typedef struct hash_entry {
    char *key;
    void *value;
    struct hash_entry *next;
} hash_entry_t;

typedef struct {
    hash_entry_t **buckets;
    size_t bucket_count;
    size_t size;
    unsigned int (*hash_func)(const char*);
} hash_table_t;

unsigned int djb2_hash(const char *str) {
    unsigned int hash = 5381;
    int c;
    while ((c = *str++)) hash = ((hash << 5) + hash) + c;
    return hash;
}

3. 环形缓冲区

typedef struct {
    char *buffer;
    size_t size;
    size_t read_pos;
    size_t write_pos;
    int full;
} ring_buffer_t;

int ring_buffer_write(ring_buffer_t *rb, const char *data, size_t len) {
    size_t available = rb->size - ring_buffer_size(rb);
    if (len > available) return -1;
    
    for (size_t i = 0; i < len; i++) {
        rb->buffer[rb->write_pos] = data[i];
        rb->write_pos = (rb->write_pos + 1) % rb->size;
        if (rb->write_pos == rb->read_pos) rb->full = 1;
    }
    return len;
}

并发与多线程

1. 线程安全的数据结构

#include <pthread.h>

typedef struct {
    int value;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
} thread_safe_counter_t;

void counter_increment(thread_safe_counter_t *counter) {
    pthread_mutex_lock(&counter->mutex);
    counter->value++;
    pthread_cond_signal(&counter->cond);
    pthread_mutex_unlock(&counter->mutex);
}

int counter_wait_for(thread_safe_counter_t *counter, int target) {
    pthread_mutex_lock(&counter->mutex);
    while (counter->value < target) {
        pthread_cond_wait(&counter->cond, &counter->mutex);
    }
    pthread_mutex_unlock(&counter->mutex);
    return counter->value;
}

2. 读写锁实现

typedef struct {
    pthread_mutex_t mutex;
    pthread_cond_t read_cond;
    pthread_cond_t write_cond;
    int readers;
    int writers;
    int waiting_writers;
} rwlock_t;

void rwlock_rdlock(rwlock_t *rwlock) {
    pthread_mutex_lock(&rwlock->mutex);
    while (rwlock->writers > 0 || rwlock->waiting_writers > 0) {
        pthread_cond_wait(&rwlock->read_cond, &rwlock->mutex);
    }
    rwlock->readers++;
    pthread_mutex_unlock(&rwlock->mutex);
}

3. 无锁编程

#include <stdatomic.h>

typedef struct {
    atomic_int value;
} atomic_counter_t;

void atomic_counter_increment(atomic_counter_t *counter) {
    atomic_fetch_add(&counter->value, 1);
}

int atomic_counter_get(atomic_counter_t *counter) {
    return atomic_load(&counter->value);
}

错误处理与异常机制

1. 错误码系统

typedef enum {
    ERROR_SUCCESS = 0,
    ERROR_INVALID_PARAM = -1,
    ERROR_OUT_OF_MEMORY = -2,
    ERROR_FILE_NOT_FOUND = -3,
    ERROR_PERMISSION_DENIED = -4
} error_code_t;

#define RETURN_ON_ERROR(expr) do { \
    error_code_t err = (expr); \
    if (err != ERROR_SUCCESS) return err; \
} while(0)

// 带上下文的错误处理
typedef struct {
    error_code_t code;
    const char *message;
    const char *file;
    int line;
} error_context_t;

2. 异常模拟机制

#include <setjmp.h>

typedef struct {
    jmp_buf jump_buffer;
    int error_code;
    const char *error_message;
} exception_context_t;

static __thread exception_context_t *current_exception = NULL;

#define TRY \
    do { \
        exception_context_t __exception_ctx; \
        __exception_ctx.error_code = 0; \
        if (setjmp(__exception_ctx.jump_buffer) == 0) { \
            current_exception = &__exception_ctx;

#define CATCH(error_var) \
        } else { \
            error_var = current_exception->error_code;

#define END_TRY \
        } \
        current_exception = NULL; \
    } while(0);

#define THROW(code, message) \
    do { \
        if (current_exception) { \
            current_exception->error_code = code; \
            current_exception->error_message = message; \
            longjmp(current_exception->jump_buffer, 1); \
        } \
    } while(0)

3. 资源管理RAII

typedef struct {
    void *resource;
    void (*cleanup)(void*);
} raii_guard_t;

#define RAII_VAR(type, name, init, cleanup_func) \
    type name = init; \
    raii_guard_t __guard_##name = {&name, (void(*)(void*))cleanup_func}; \
    __attribute__((cleanup(raii_cleanup))) raii_guard_t *__raii_##name = &__guard_##name;

static void raii_cleanup(raii_guard_t **guard) {
    if ((*guard)->resource && (*guard)->cleanup) {
        (*guard)->cleanup((*guard)->resource);
    }
}

性能优化技巧

1. 缓存友好的数据结构

// 结构体打包优化
struct __attribute__((packed)) packed_struct {
    char a;
    int b;
    short c;
};

// 缓存行对齐
#define CACHE_LINE_SIZE 64
struct __attribute__((aligned(CACHE_LINE_SIZE))) cache_aligned_struct {
    int data[16];
};

2. 分支预测优化

// 静态分支预测
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)

void optimized_function(int *array, size_t size) {
    if (unlikely(size == 0)) return;
    
    for (size_t i = 0; likely(i < size); i++) {
        process_element(array[i]);
    }
}

3. 内联汇编优化

// 获取时间戳计数器
static inline uint64_t rdtsc(void) {
    uint32_t lo, hi;
    __asm__ __volatile__("rdtsc" : "=a" (lo), "=d" (hi));
    return ((uint64_t)hi << 32) | lo;
}

// 内存屏障
#define MEMORY_BARRIER() __asm__ __volatile__("" ::: "memory")

4. SIMD优化

#ifdef __SSE2__
#include <emmintrin.h>

void vector_add(float *a, float *b, float *result, size_t n) {
    size_t i = 0;
    for (; i + 4 <= n; i += 4) {
        __m128 va = _mm_load_ps(&a[i]);
        __m128 vb = _mm_load_ps(&b[i]);
        __m128 vr = _mm_add_ps(va, vb);
        _mm_store_ps(&result[i], vr);
    }
    // 处理剩余元素
    for (; i < n; i++) {
        result[i] = a[i] + b[i];
    }
}
#endif

调试与测试技巧

1. 调试宏

#ifdef DEBUG
    #define DBG_PRINT(fmt, ...) \
        fprintf(stderr, "[DEBUG] %s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
    #define ASSERT(condition) \
        do { \
            if (!(condition)) { \
                fprintf(stderr, "Assertion failed: %s at %s:%d\n", \
                        #condition, __FILE__, __LINE__); \
                abort(); \
            } \
        } while(0)
#else
    #define DBG_PRINT(fmt, ...) do {} while(0)
    #define ASSERT(condition) do {} while(0)
#endif

// 性能计时
#define TIME_IT(code, result_var) \
    do { \
        clock_t start = clock(); \
        code; \
        result_var = ((double)(clock() - start)) / CLOCKS_PER_SEC; \
    } while(0)

2. 单元测试框架

typedef struct {
    const char *name;
    void (*test_func)(void);
    int passed;
    int failed;
} test_case_t;

#define TEST_CASE(name) \
    static void test_##name(void); \
    static test_case_t test_case_##name = {#name, test_##name, 0, 0}; \
    static void test_##name(void)

#define ASSERT_EQ(expected, actual) \
    do { \
        if ((expected) != (actual)) { \
            fprintf(stderr, "Assertion failed: %s != %s at %s:%d\n", \
                    #expected, #actual, __FILE__, __LINE__); \
            current_test->failed++; \
        } else { \
            current_test->passed++; \
        } \
    } while(0)

3. 内存泄漏检测

#ifdef DEBUG_MEMORY
static size_t total_allocated = 0;
static size_t allocation_count = 0;

void* debug_malloc(size_t size, const char *file, int line) {
    void *ptr = malloc(size + sizeof(size_t));
    if (ptr) {
        *(size_t*)ptr = size;
        total_allocated += size;
        allocation_count++;
        printf("ALLOC: %zu bytes at %s:%d\n", size, file, line);
        return (char*)ptr + sizeof(size_t);
    }
    return NULL;
}

void debug_free(void *ptr, const char *file, int line) {
    if (ptr) {
        size_t *size_ptr = (size_t*)((char*)ptr - sizeof(size_t));
        total_allocated -= *size_ptr;
        allocation_count--;
        printf("FREE: %zu bytes at %s:%d\n", *size_ptr, file, line);
        free(size_ptr);
    }
}

#define malloc(size) debug_malloc(size, __FILE__, __LINE__)
#define free(ptr) debug_free(ptr, __FILE__, __LINE__)
#endif

跨平台编程

1. 平台抽象层

// 线程抽象
#ifdef _WIN32
    #include <windows.h>
    typedef HANDLE thread_t;
    typedef CRITICAL_SECTION mutex_t;
    #define THREAD_CREATE(thread, func, arg) \
        (thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, arg, 0, NULL))
    #define THREAD_JOIN(thread) WaitForSingleObject(thread, INFINITE)
    #define MUTEX_INIT(mutex) InitializeCriticalSection(mutex)
    #define MUTEX_LOCK(mutex) EnterCriticalSection(mutex)
    #define MUTEX_UNLOCK(mutex) LeaveCriticalSection(mutex)
#else
    #include <pthread.h>
    typedef pthread_t thread_t;
    typedef pthread_mutex_t mutex_t;
    #define THREAD_CREATE(thread, func, arg) pthread_create(&thread, NULL, func, arg)
    #define THREAD_JOIN(thread) pthread_join(thread, NULL)
    #define MUTEX_INIT(mutex) pthread_mutex_init(mutex, NULL)
    #define MUTEX_LOCK(mutex) pthread_mutex_lock(mutex)
    #define MUTEX_UNLOCK(mutex) pthread_mutex_unlock(mutex)
#endif

2. 文件路径处理

#ifdef _WIN32
    #define PATH_SEPARATOR '\\'
    #define PATH_SEPARATOR_STR "\\"
#else
    #define PATH_SEPARATOR '/'
    #define PATH_SEPARATOR_STR "/"
#endif

char* join_path(const char *dir, const char *file) {
    size_t dir_len = strlen(dir);
    size_t file_len = strlen(file);
    char *result = malloc(dir_len + file_len + 2);
    
    strcpy(result, dir);
    if (dir[dir_len - 1] != PATH_SEPARATOR) {
        strcat(result, PATH_SEPARATOR_STR);
    }
    strcat(result, file);
    return result;
}

3. 字节序处理

// 网络字节序转换
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
    #define IS_BIG_ENDIAN 1
#else
    #define IS_BIG_ENDIAN 0
#endif

static inline uint32_t swap_endian_32(uint32_t val) {
    return ((val & 0x000000FF) << 24) |
           ((val & 0x0000FF00) << 8)  |
           ((val & 0x00FF0000) >> 8)  |
           ((val & 0xFF000000) >> 24);
}

#define hton32(x) (IS_BIG_ENDIAN ? (x) : swap_endian_32(x))
#define ntoh32(x) hton32(x)

安全编程实践

1. 缓冲区溢出防护

// 安全字符串操作
size_t safe_strncpy(char *dest, size_t dest_size, const char *src, size_t count) {
    if (dest_size == 0) return 0;
    
    size_t copy_len = (count < dest_size - 1) ? count : dest_size - 1;
    memcpy(dest, src, copy_len);
    dest[copy_len] = '\0';
    return copy_len;
}

// 格式化字符串安全检查
#define SAFE_PRINTF(buffer, size, format, ...) \
    do { \
        int __result = snprintf(buffer, size, format, ##__VA_ARGS__); \
        if (__result < 0 || (size_t)__result >= size) { \
            /* 处理溢出 */ \
            buffer[size - 1] = '\0'; \
        } \
    } while(0)

2. 输入验证

// 整数溢出检查
static inline int safe_add(int a, int b, int *result) {
    if ((b > 0 && a > INT_MAX - b) || (b < 0 && a < INT_MIN - b)) {
        return -1; // 溢出
    }
    *result = a + b;
    return 0;
}

// 指针验证
#define VALIDATE_PTR(ptr) \
    do { \
        if (!(ptr)) { \
            return ERROR_INVALID_PARAM; \
        } \
    } while(0)

3. 安全随机数

#include <time.h>
#include <stdlib.h>

// 密码学安全随机数(需要平台支持)
#ifdef __linux__
    #include <sys/random.h>
    int secure_random_bytes(void *buf, size_t len) {
        return getrandom(buf, len, 0) == (ssize_t)len ? 0 : -1;
    }
#else
    // 简单的伪随机数生成器
    static unsigned long long rand_state = 1;
    
    void srand64(unsigned long long seed) {
        rand_state = seed;
    }
    
    unsigned long long rand64(void) {
        rand_state = rand_state * 6364136223846793005ULL + 1;
        return rand_state;
    }
#endif

附录:最佳实践总结

编码规范

  1. 命名约定:使用清晰的命名,避免缩写
  2. 注释风格:使用Doxygen风格注释
  3. 错误处理:始终检查返回值
  4. 内存管理:遵循RAII原则
  5. 线程安全:明确标识线程安全函数

性能优化原则

  1. 先测量后优化:使用性能分析工具
  2. 算法优先:选择合适的数据结构和算法
  3. 避免过早优化:保持代码可读性
  4. 缓存友好:考虑数据局部性
  5. 编译器优化:合理使用编译器优化选项

安全编码原则

  1. 输入验证:永远不要信任外部输入
  2. 边界检查:防止缓冲区溢出
  3. 最小权限:使用最小必要权限
  4. 安全函数:使用安全的字符串函数
  5. 代码审查:定期进行安全代码审查

这份指南涵盖了C语言编程中的高级技巧和最佳实践,从基础的宏定义到复杂的并发编程,从性能优化到安全编码,希望能帮助您提升C语言编程水平。

发表在 linux文章 | 留下评论

PCAKET_HOST等相关宏定义介绍

好的,我们来用中文详细解释一下 PACKET_HOST:

核心含义:

PACKET_HOST 是 Linux 内核中定义的一个常量(通常在头文件 中),它表示捕获到的网络数据包的目的地是本机。

详细说明:

1. 适用场景:
◦ 当你使用 AF_PACKET / PF_PACKET 套接字(也称为 SOCK_RAW + ETH_P_ALL 或 SOCK_DGRAM)进行原始网络链路层数据包捕获时,会接触到它。

◦ 这种类型的套接字允许你的程序直接接收流经网络接口(网卡)的底层数据包,包括不是发给本机的包(如果你开启了混杂模式)。

2. 来源和作用:
◦ 当你的原始套接字使用 recvfrom(), recvmsg() 等系统调用接收一个数据包时,除了数据内容本身,你还会收到一个地址结构 struct sockaddr_ll。

◦ 这个 struct sockaddr_ll 结构体有一个非常重要的成员叫做 sll_pkttype。

◦ sll_pkttype 的值就是由 PACKET_HOST, PACKET_BROADCAST, PACKET_MULTICAST, PACKET_OTHERHOST, PACKET_OUTGOING 等常量表示的。

◦ sll_pkttype = PACKET_HOST 明确告诉你:这个刚刚捕获到的数据包是发往本机操作系统网络协议栈的! 也就是说,这个包的目标 MAC 地址是本机网卡的 MAC 地址(或者如果接口支持,是配置给该接口的多个 MAC 地址之一)。

3. 与其他包类型的区别:
◦ PACKET_BROADCAST: 广播包 (目标 MAC 地址为 FF:FF:FF:FF:FF:FF)。

◦ PACKET_MULTICAST: 多播包 (目标 MAC 地址属于多播地址范围)。

◦ PACKET_OTHERHOST: 目的地不是本机的包。在正常模式下,网卡会过滤掉这类包。只有开启了网卡的“混杂模式”(Promiscuous Mode),你的原始套接字才能接收到这种数据包。例如,捕获同一局域网中其他主机之间的通信就需要它。

◦ PACKET_OUTGOING: 表示此包是由本机发送出去的数据包(当套接字设置为捕获所有包 ETH_P_ALL 时可能也会收到本机发出的包)。

关键总结:

• PACKET_HOST 不是配置项,而是捕获包时的一个“标记”或“属性”。

• 它是 struct sockaddr_ll 中 sll_pkttype 字段的一个可能取值。

• 它的值明确表示:“捕获到的这个数据包本来是打算交给本机操作系统协议栈处理的”(例如,一个发送给你电脑 IP 地址的 TCP SYN 包,一个 ICMP echo request 包等)。

• 在编写抓包工具(如 tcpdump、Wireshark 的底层)或自定义网络监控/安全程序时,检查 sll_pkttype 的值(特别是与 PACKET_HOST, PACKET_OTHERHOST 的比较)是理解捕获包流的关键。

通俗比喻:

想象一个繁忙的邮局分拣中心:

• PACKET_HOST: 信件上写着你的名字和地址,是寄给你的。

• PACKET_BROADCAST: 信件上写着“此片区所有居民收”,是广播通知。

• PACKET_MULTICAST: 信件上写着“某兴趣小组成员收”,是多播通知。

• PACKET_OTHERHOST: 信件上写的是别人的名字地址,你本不该看到,但邮局工作人员(在特殊模式下)也让你看了。

在代码中如何识别:
#include
#include #include // 或 netinet/if_ether.h
// … 其他头文件 …

int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); // 创建原始套接字
// … 绑定到接口等操作 …

struct sockaddr_ll sll;
socklen_t sll_len = sizeof(sll);
unsigned char buffer[1500]; // 常见 MTU 大小的缓冲区

// 接收数据包
ssize_t packet_size = recvfrom(sock, buffer, sizeof(buffer), 0,
(struct sockaddr*)&sll, &sll_len);

if (packet_size > 0) {
// 检查包的“类型属性” sll_pkttype
switch(sll.sll_pkttype) {
case PACKET_HOST:
printf(“捕获到一个本机接收包!\n”);
// … 处理目的地址是本机的包 …
break;
case PACKET_BROADCAST:
printf(“捕获到一个广播包!\n”);
break;
case PACKET_MULTICAST:
printf(“捕获到一个多播包!\n”);
break;
case PACKET_OTHERHOST:
printf(“捕获到一个目的地是其他主机的包!\n”);
// … 处理目的地址不是本机的包 (混杂模式下捕获) …
break;
default:
printf(“捕获到一个未知类型的包 (%d)\n”, sll.sll_pkttype);
}
}

希望这个详细的中文解释能帮助你清晰理解 PACKET_HOST 的含义和作用!

发表在 linux文章 | 留下评论

creat系统调用及示例

creat – 创建文件

函数介绍

creat系统调用用于创建新文件或截断已存在的文件。它是一个简化的文件创建函数,等价于open(path, O_WRONLY|O_CREAT|O_TRUNC, mode)

函数原型

#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>

int creat(const char *pathname, mode_t mode);

功能

创建新文件或清空已存在的文件,返回只写权限的文件描述符。

参数

  • const char *pathname: 要创建的文件路径名
  • mode_t mode: 文件权限模式(如0644)

返回值

  • 成功时返回文件描述符(非负整数)
  • 失败时返回-1,并设置errno

特殊限制

  • 文件总是以只写模式打开
  • 如果文件已存在,会被截断为0字节
  • 不支持追加模式等高级选项

相似函数

  • open(): 更灵活的文件打开函数
  • mkstemp(): 创建安全的临时文件

示例代码

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>

int main() {
    int fd;
    
    printf("=== Creat函数示例 ===\n");
    
    // 示例1: 基本文件创建
    printf("示例1: 基本文件创建\n");
    fd = creat("test_creat.txt", 0644);
    if (fd == -1) {
        perror("创建文件失败");
    } else {
        printf("成功创建文件,fd=%d\n", fd);
        write(fd, "Hello from creat!", 17);
        close(fd);
    }
    
    // 示例2: 重复创建(会截断原文件)
    printf("示例2: 重复创建(截断原文件)\n");
    fd = creat("test_creat.txt", 0644);
    if (fd != -1) {
        printf("重新创建文件(原内容被清空)\n");
        write(fd, "New content", 11);
        close(fd);
    }
    
    // 示例3: 权限测试
    printf("示例3: 不同权限创建\n");
    int fd1 = creat("file_600.txt", 0600);  // 仅所有者读写
    int fd2 = creat("file_644.txt", 0644);  // 所有者读写,其他用户读
    int fd3 = creat("file_666.txt", 0666);  // 所有用户读写
    
    if (fd1 != -1) {
        printf("创建0600权限文件\n");
        close(fd1);
    }
    if (fd2 != -1) {
        printf("创建0644权限文件\n");
        close(fd2);
    }
    if (fd3 != -1) {
        printf("创建0666权限文件\n");
        close(fd3);
    }
    
    // 清理测试文件
    unlink("test_creat.txt");
    unlink("file_600.txt");
    unlink("file_644.txt");
    unlink("file_666.txt");
    
    return 0;
}
https://app-blog.csdn.net/csdn/aiChatNew

creat – 创建文件

函数介绍

函数原型

功能

参数

返回值

特殊限制

相似函数

示例代码

Markdown 1391 字数 99 行数 当前行 1, 当前列 3

HTML 1330 字数 77 段落

保存草稿 发布文章

发表在 linux文章 | 留下评论

sysinfo系统调用及示例

sysfs 和 sysinfo 函数详解

1. 函数介绍

sysfs

sysfs 是Linux内核提供的虚拟文件系统,用于导出内核对象的信息到用户空间。它以文件和目录的形式呈现系统硬件、驱动程序、设备状态等信息,是现代Linux系统管理和监控的重要接口。

sysinfo

sysinfo 是Linux系统调用,用于获取系统的整体统计信息,包括内存使用情况、系统负载、运行时间等。它提供了一种编程方式来获取系统状态信息。

2. 函数原型

sysfs

#include <sysfs/libsysfs.h>  // 需要安装libsysfs-dev包

// sysfs库函数(非系统调用)
struct sysfs_bus *sysfs_open_bus(const char *name);
struct sysfs_device *sysfs_open_device(const char *bus_name, const char *dev_name);
char *sysfs_get_device_attr(const char *devpath, const char *attr_name);

sysinfo

#include <sys/sysinfo.h>

int sysinfo(struct sysinfo *info);

3. 功能

sysfs

  • 提供内核对象的结构化信息访问
  • 导出硬件设备、驱动程序、总线等信息
  • 支持动态查询设备状态和属性

sysinfo

  • 获取系统内存使用统计
  • 查询系统负载和运行时间
  • 获取进程和用户统计信息

4. 参数

sysfs

  • *const char name: 总线或设备名称
  • *const char devpath: 设备路径
  • *const char attr_name: 属性名称

sysinfo

  • *struct sysinfo info: 指向sysinfo结构的指针

5. 返回值

sysfs

  • 成功: 返回相应的句柄或字符串
  • 失败: 返回NULL,并设置errno

sysinfo

  • 成功: 返回0
  • 失败: 返回-1,并设置errno

6. 相似函数,或关联函数

sysfs相关:

  • opendir/readdir: 目录遍历
  • open/read: 文件读取
  • /sys/: sysfs挂载点

sysinfo相关:

  • getloadavg: 获取系统负载平均值
  • getrusage: 获取资源使用情况
  • /proc/: procfs文件系统

7. 示例代码

示例1:基础sysinfo使用

#include <sys/sysinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>

/**
 * 显示系统信息
 */
void show_system_info() {
    struct sysinfo si;
    time_t current_time;
    char time_str[64];
    
    printf("=== 系统信息 ===\n");
    
    // 获取系统信息
    if (sysinfo(&si) == -1) {
        perror("获取系统信息失败");
        return;
    }
    
    // 显示时间信息
    current_time = time(NULL);
    strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", localtime(&current_time));
    printf("当前时间: %s\n", time_str);
    
    // 显示启动时间
    time_t boot_time = current_time - si.uptime;
    strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", localtime(&boot_time));
    printf("系统启动时间: %s\n", time_str);
    printf("系统运行时间: %ld 天 %ld 小时 %ld 分钟\n",
           si.uptime / 86400,
           (si.uptime % 86400) / 3600,
           (si.uptime % 3600) / 60);
    
    // 显示负载信息
    printf("\n系统负载:\n");
    printf("  1分钟平均负载: %.2f\n", (double)si.loads[0] / (1 << SI_LOAD_SHIFT));
    printf("  5分钟平均负载: %.2f\n", (double)si.loads[1] / (1 << SI_LOAD_SHIFT));
    printf("  15分钟平均负载: %.2f\n", (double)si.loads[2] / (1 << SI_LOAD_SHIFT));
    
    // 显示内存信息
    printf("\n内存信息:\n");
    printf("  总内存: %.2f GB\n", si.totalram * si.mem_unit / (1024.0 * 1024.0 * 1024.0));
    printf("  可用内存: %.2f GB\n", si.freeram * si.mem_unit / (1024.0 * 1024.0 * 1024.0));
    printf("  共享内存: %.2f GB\n", si.sharedram * si.mem_unit / (1024.0 * 1024.0 * 1024.0));
    printf("  缓冲区内存: %.2f GB\n", si.bufferram * si.mem_unit / (1024.0 * 1024.0 * 1024.0));
    
    // 显示交换信息
    printf("\n交换信息:\n");
    printf("  总交换空间: %.2f GB\n", si.totalswap * si.mem_unit / (1024.0 * 1024.0 * 1024.0));
    printf("  可用交换空间: %.2f GB\n", si.freeswap * si.mem_unit / (1024.0 * 1024.0 * 1024.0));
    
    // 显示进程信息
    printf("\n进程信息:\n");
    printf("  当前进程数: %d\n", si.procs);
    
    printf("\n");
}

/**
 * 演示基础sysinfo使用方法
 */
int demo_sysinfo_basic() {
    printf("=== 基础sysinfo使用示例 ===\n");
    
    // 显示系统信息
    show_system_info();
    
    // 演示多次查询
    printf("连续查询系统信息:\n");
    for (int i = 0; i < 3; i++) {
        struct sysinfo si;
        
        if (sysinfo(&si) == 0) {
            printf("  第 %d 次查询:\n", i + 1);
            printf("    运行时间: %ld 秒\n", si.uptime);
            printf("    可用内存: %.2f MB\n", 
                   si.freeram * si.mem_unit / (1024.0 * 1024.0));
            printf("    当前进程: %d\n", si.procs);
        } else {
            printf("  第 %d 次查询失败: %s\n", i + 1, strerror(errno));
        }
        
        if (i < 2) sleep(2);  // 间隔查询
    }
    
    return 0;
}

int main() {
    return demo_sysinfo_basic();
}

示例2:内存监控工具

#include <sys/sysinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>

/**
 * 内存使用统计结构
 */
typedef struct {
    unsigned long total_ram_mb;
    unsigned long free_ram_mb;
    unsigned long used_ram_mb;
    unsigned long shared_ram_mb;
    unsigned long buffer_ram_mb;
    unsigned long total_swap_mb;
    unsigned long free_swap_mb;
    unsigned long used_swap_mb;
    double ram_usage_percent;
    double swap_usage_percent;
} memory_stats_t;

/**
 * 获取内存统计信息
 */
int get_memory_stats(memory_stats_t *stats) {
    struct sysinfo si;
    
    if (sysinfo(&si) == -1) {
        return -1;
    }
    
    // 转换为MB
    unsigned long mem_unit = si.mem_unit ? si.mem_unit : 1;
    
    stats->total_ram_mb = si.totalram * mem_unit / (1024 * 1024);
    stats->free_ram_mb = si.freeram * mem_unit / (1024 * 1024);
    stats->used_ram_mb = stats->total_ram_mb - stats->free_ram_mb;
    stats->shared_ram_mb = si.sharedram * mem_unit / (1024 * 1024);
    stats->buffer_ram_mb = si.bufferram * mem_unit / (1024 * 1024);
    stats->total_swap_mb = si.totalswap * mem_unit / (1024 * 1024);
    stats->free_swap_mb = si.freeswap * mem_unit / (1024 * 1024);
    stats->used_swap_mb = stats->total_swap_mb - stats->free_swap_mb;
    
    // 计算使用率
    stats->ram_usage_percent = stats->total_ram_mb > 0 ? 
        (double)stats->used_ram_mb / stats->total_ram_mb * 100 : 0;
    stats->swap_usage_percent = stats->total_swap_mb > 0 ? 
        (double)stats->used_swap_mb / stats->total_swap_mb * 100 : 0;
    
    return 0;
}

/**
 * 显示内存统计信息
 */
void show_memory_stats(const memory_stats_t *stats) {
    printf("内存统计信息:\n");
    printf("  物理内存:\n");
    printf("    总量: %lu MB\n", stats->total_ram_mb);
    printf("    已用: %lu MB (%.1f%%)\n", stats->used_ram_mb, stats->ram_usage_percent);
    printf("    可用: %lu MB\n", stats->free_ram_mb);
    printf("    共享: %lu MB\n", stats->shared_ram_mb);
    printf("    缓冲: %lu MB\n", stats->buffer_ram_mb);
    
    printf("  交换空间:\n");
    printf("    总量: %lu MB\n", stats->total_swap_mb);
    printf("    已用: %lu MB (%.1f%%)\n", stats->used_swap_mb, stats->swap_usage_percent);
    printf("    可用: %lu MB\n", stats->free_swap_mb);
}

/**
 * 内存监控警报
 */
void check_memory_alerts(const memory_stats_t *stats) {
    printf("\n内存警报检查:\n");
    
    // RAM使用率警报
    if (stats->ram_usage_percent > 90) {
        printf("  ⚠ 警告: RAM使用率过高 (%.1f%%)\n", stats->ram_usage_percent);
    } else if (stats->ram_usage_percent > 80) {
        printf("  ℹ 提示: RAM使用率较高 (%.1f%%)\n", stats->ram_usage_percent);
    } else {
        printf("  ✓ RAM使用率正常 (%.1f%%)\n", stats->ram_usage_percent);
    }
    
    // 交换空间使用率警报
    if (stats->swap_usage_percent > 80) {
        printf("  ⚠ 警告: 交换空间使用率过高 (%.1f%%)\n", stats->swap_usage_percent);
    } else if (stats->swap_usage_percent > 50) {
        printf("  ℹ 提示: 交换空间使用率较高 (%.1f%%)\n", stats->swap_usage_percent);
    } else {
        printf("  ✓ 交换空间使用率正常 (%.1f%%)\n", stats->swap_usage_percent);
    }
    
    // 内存不足警报
    if (stats->free_ram_mb < 100) {
        printf("  ⚠ 警告: 可用内存不足 (%lu MB)\n", stats->free_ram_mb);
    }
}

/**
 * 演示内存监控工具
 */
int demo_memory_monitor() {
    memory_stats_t stats;
    
    printf("=== 内存监控工具演示 ===\n");
    
    // 单次内存统计
    printf("1. 当前内存状态:\n");
    if (get_memory_stats(&stats) == 0) {
        show_memory_stats(&stats);
        check_memory_alerts(&stats);
    } else {
        printf("获取内存统计失败: %s\n", strerror(errno));
        return -1;
    }
    
    // 连续监控演示
    printf("\n2. 连续内存监控演示:\n");
    printf("监控5次,每次间隔3秒:\n");
    
    for (int i = 0; i < 5; i++) {
        if (get_memory_stats(&stats) == 0) {
            time_t now = time(NULL);
            char time_str[32];
            strftime(time_str, sizeof(time_str), "%H:%M:%S", localtime(&now));
            
            printf("[%s] 第 %d 次监控:\n", time_str, i + 1);
            printf("  RAM: %lu/%lu MB (%.1f%%)\n",
                   stats.used_ram_mb, stats.total_ram_mb, stats.ram_usage_percent);
            printf("  Swap: %lu/%lu MB (%.1f%%)\n",
                   stats.used_swap_mb, stats.total_swap_mb, stats.swap_usage_percent);
            
            // 检查警报
            if (stats.ram_usage_percent > 90 || stats.swap_usage_percent > 80) {
                printf("  ⚠ 触发内存警报\n");
            }
        } else {
            printf("第 %d 次监控失败: %s\n", i + 1, strerror(errno));
        }
        
        if (i < 4) sleep(3);
    }
    
    // 内存使用趋势分析
    printf("\n3. 内存使用趋势分析:\n");
    printf("历史数据分析:\n");
    
    unsigned long max_ram_usage = 0;
    unsigned long min_ram_usage = (unsigned long)-1;
    double total_ram_usage = 0;
    int sample_count = 0;
    
    for (int i = 0; i < 5; i++) {
        if (get_memory_stats(&stats) == 0) {
            if (stats.used_ram_mb > max_ram_usage) {
                max_ram_usage = stats.used_ram_mb;
            }
            if (stats.used_ram_mb < min_ram_usage) {
                min_ram_usage = stats.used_ram_mb;
            }
            total_ram_usage += stats.ram_usage_percent;
            sample_count++;
        }
        sleep(1);
    }
    
    if (sample_count > 0) {
        printf("  最大RAM使用: %lu MB\n", max_ram_usage);
        printf("  最小RAM使用: %lu MB\n", min_ram_usage);
        printf("  平均RAM使用率: %.1f%%\n", total_ram_usage / sample_count);
    }
    
    return 0;
}

int main() {
    return demo_memory_monitor();
}

示例3:系统负载监控

#include <sys/sysinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>

/**
 * 系统负载统计结构
 */
typedef struct {
    double load_avg_1min;
    double load_avg_5min;
    double load_avg_15min;
    unsigned long uptime_seconds;
    unsigned int process_count;
    time_t boot_time;
} system_load_stats_t;

/**
 * 获取系统负载统计
 */
int get_system_load_stats(system_load_stats_t *stats) {
    struct sysinfo si;
    
    if (sysinfo(&si) == -1) {
        return -1;
    }
    
    // 计算负载平均值
    stats->load_avg_1min = (double)si.loads[0] / (1 << SI_LOAD_SHIFT);
    stats->load_avg_5min = (double)si.loads[1] / (1 << SI_LOAD_SHIFT);
    stats->load_avg_15min = (double)si.loads[2] / (1 << SI_LOAD_SHIFT);
    
    // 系统运行时间
    stats->uptime_seconds = si.uptime;
    stats->boot_time = time(NULL) - si.uptime;
    
    // 进程数
    stats->process_count = si.procs;
    
    return 0;
}

/**
 * 显示系统负载统计
 */
void show_system_load_stats(const system_load_stats_t *stats) {
    char boot_time_str[64];
    char current_time_str[64];
    
    strftime(boot_time_str, sizeof(boot_time_str), "%Y-%m-%d %H:%M:%S", 
             localtime(&stats->boot_time));
    
    printf("系统负载统计:\n");
    printf("  系统启动时间: %s\n", boot_time_str);
    
    unsigned long days = stats->uptime_seconds / 86400;
    unsigned long hours = (stats->uptime_seconds % 86400) / 3600;
    unsigned long minutes = (stats->uptime_seconds % 3600) / 60;
    printf("  系统运行时间: %lu 天 %lu 小时 %lu 分钟\n", days, hours, minutes);
    
    printf("  系统负载平均值:\n");
    printf("    1分钟: %.2f\n", stats->load_avg_1min);
    printf("    5分钟: %.2f\n", stats->load_avg_5min);
    printf("    15分钟: %.2f\n", stats->load_avg_15min);
    
    printf("  当前进程数: %u\n", stats->process_count);
}

/**
 * 分析系统负载状态
 */
void analyze_system_load(const system_load_stats_t *stats, int cpu_count) {
    printf("\n系统负载分析:\n");
    
    // 1分钟负载分析
    double load_per_cpu_1min = stats->load_avg_1min / cpu_count;
    if (load_per_cpu_1min > 1.0) {
        printf("  ⚠ 1分钟负载警告: 每CPU负载 %.2f (> 1.0)\n", load_per_cpu_1min);
    } else if (load_per_cpu_1min > 0.7) {
        printf("  ℹ 1分钟负载提示: 每CPU负载 %.2f (较高)\n", load_per_cpu_1min);
    } else {
        printf("  ✓ 1分钟负载正常: 每CPU负载 %.2f\n", load_per_cpu_1min);
    }
    
    // 5分钟负载分析
    double load_per_cpu_5min = stats->load_avg_5min / cpu_count;
    if (load_per_cpu_5min > 1.0) {
        printf("  ⚠ 5分钟负载警告: 每CPU负载 %.2f (> 1.0)\n", load_per_cpu_5min);
    } else if (load_per_cpu_5min > 0.7) {
        printf("  ℹ 5分钟负载提示: 每CPU负载 %.2f (较高)\n", load_per_cpu_5min);
    } else {
        printf("  ✓ 5分钟负载正常: 每CPU负载 %.2f\n", load_per_cpu_5min);
    }
    
    // 15分钟负载分析
    double load_per_cpu_15min = stats->load_avg_15min / cpu_count;
    if (load_per_cpu_15min > 1.0) {
        printf("  ⚠ 15分钟负载警告: 每CPU负载 %.2f (> 1.0)\n", load_per_cpu_15min);
    } else if (load_per_cpu_15min > 0.7) {
        printf("  ℹ 15分钟负载提示: 每CPU负载 %.2f (较高)\n", load_per_cpu_15min);
    } else {
        printf("  ✓ 15分钟负载正常: 每CPU负载 %.2f\n", load_per_cpu_15min);
    }
    
    // 进程数分析
    if (stats->process_count > 1000) {
        printf("  ⚠ 进程数警告: 当前有 %u 个进程\n", stats->process_count);
    } else if (stats->process_count > 500) {
        printf("  ℹ 进程数提示: 当前有 %u 个进程\n", stats->process_count);
    } else {
        printf("  ✓ 进程数正常: 当前有 %u 个进程\n", stats->process_count);
    }
}

/**
 * 演示系统负载监控
 */
int demo_system_load_monitoring() {
    system_load_stats_t stats;
    int cpu_count = sysconf(_SC_NPROCESSORS_ONLN);
    
    printf("=== 系统负载监控演示 ===\n");
    printf("CPU核心数: %d\n", cpu_count);
    
    // 单次负载统计
    printf("\n1. 当前系统负载状态:\n");
    if (get_system_load_stats(&stats) == 0) {
        show_system_load_stats(&stats);
        analyze_system_load(&stats, cpu_count);
    } else {
        printf("获取系统负载统计失败: %s\n", strerror(errno));
        return -1;
    }
    
    // 连续负载监控
    printf("\n2. 连续负载监控演示:\n");
    printf("监控10次,每次间隔2秒:\n");
    
    double max_load_1min = 0, min_load_1min = 999;
    double total_load_1min = 0;
    int sample_count = 0;
    
    for (int i = 0; i < 10; i++) {
        if (get_system_load_stats(&stats) == 0) {
            time_t now = time(NULL);
            char time_str[32];
            strftime(time_str, sizeof(time_str), "%H:%M:%S", localtime(&now));
            
            printf("[%s] 第 %d 次监控:\n", time_str, i + 1);
            printf("  负载: %.2f %.2f %.2f\n", 
                   stats.load_avg_1min, stats.load_avg_5min, stats.load_avg_15min);
            printf("  进程: %u\n", stats.process_count);
            
            // 统计负载数据
            if (stats.load_avg_1min > max_load_1min) {
                max_load_1min = stats.load_avg_1min;
            }
            if (stats.load_avg_1min < min_load_1min) {
                min_load_1min = stats.load_avg_1min;
            }
            total_load_1min += stats.load_avg_1min;
            sample_count++;
        } else {
            printf("第 %d 次监控失败: %s\n", i + 1, strerror(errno));
        }
        
        if (i < 9) sleep(2);
    }
    
    // 负载趋势分析
    printf("\n3. 负载趋势分析:\n");
    if (sample_count > 0) {
        printf("  1分钟负载范围: %.2f - %.2f\n", min_load_1min, max_load_1min);
        printf("  1分钟负载平均: %.2f\n", total_load_1min / sample_count);
        printf("  负载波动幅度: %.2f\n", max_load_1min - min_load_1min);
    }
    
    // 负载等级判断
    printf("\n4. 负载等级判断:\n");
    if (sample_count > 0) {
        double avg_load = total_load_1min / sample_count;
        double load_per_cpu = avg_load / cpu_count;
        
        if (load_per_cpu > 1.5) {
            printf("  💥 系统负载极重: 每CPU平均负载 %.2f\n", load_per_cpu);
        } else if (load_per_cpu > 1.0) {
            printf("  ⚠ 系统负载较重: 每CPU平均负载 %.2f\n", load_per_cpu);
        } else if (load_per_cpu > 0.7) {
            printf("  ℹ 系统负载中等: 每CPU平均负载 %.2f\n", load_per_cpu);
        } else {
            printf("  ✓ 系统负载轻松: 每CPU平均负载 %.2f\n", load_per_cpu);
        }
    }
    
    return 0;
}

int main() {
    return demo_system_load_monitoring();
}

示例4:基础sysfs使用

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>

/**
 * 读取sysfs文件内容
 */
int read_sysfs_file(const char *path, char *buffer, size_t buffer_size) {
    int fd;
    ssize_t bytes_read;
    
    fd = open(path, O_RDONLY);
    if (fd == -1) {
        return -1;
    }
    
    bytes_read = read(fd, buffer, buffer_size - 1);
    if (bytes_read == -1) {
        close(fd);
        return -1;
    }
    
    buffer[bytes_read] = '\0';
    
    // 移除末尾的换行符
    if (bytes_read > 0 && buffer[bytes_read - 1] == '\n') {
        buffer[bytes_read - 1] = '\0';
    }
    
    close(fd);
    return 0;
}

/**
 * 显示CPU信息
 */
void show_cpu_info() {
    DIR *dir;
    struct dirent *entry;
    char path[256];
    char buffer[256];
    
    printf("=== CPU信息 ===\n");
    
    // 读取CPU基本信息
    if (read_sysfs_file("/sys/devices/system/cpu/online", buffer, sizeof(buffer)) == 0) {
        printf("在线CPU: %s\n", buffer);
    }
    
    if (read_sysfs_file("/sys/devices/system/cpu/offline", buffer, sizeof(buffer)) == 0) {
        if (strlen(buffer) > 0) {
            printf("离线CPU: %s\n", buffer);
        }
    }
    
    // 遍历CPU目录
    dir = opendir("/sys/devices/system/cpu");
    if (dir) {
        int cpu_count = 0;
        while ((entry = readdir(dir)) != NULL) {
            if (strncmp(entry->d_name, "cpu", 3) == 0 && 
                entry->d_name[3] >= '0' && entry->d_name[3] <= '9') {
                cpu_count++;
                
                // 读取CPU频率信息
                snprintf(path, sizeof(path), 
                        "/sys/devices/system/cpu/%s/cpufreq/scaling_cur_freq", 
                        entry->d_name);
                if (read_sysfs_file(path, buffer, sizeof(buffer)) == 0) {
                    long freq_khz = atol(buffer);
                    printf("CPU %s 当前频率: %.2f MHz\n", 
                           entry->d_name, freq_khz / 1000.0);
                }
                
                // 读取CPU在线状态
                snprintf(path, sizeof(path), 
                        "/sys/devices/system/cpu/%s/online", 
                        entry->d_name);
                if (read_sysfs_file(path, buffer, sizeof(buffer)) == 0) {
                    printf("CPU %s 在线状态: %s\n", 
                           entry->d_name, atoi(buffer) ? "在线" : "离线");
                }
            }
        }
        printf("CPU总数: %d\n", cpu_count);
        closedir(dir);
    }
    
    printf("\n");
}

/**
 * 显示内存信息
 */
void show_memory_info() {
    char buffer[256];
    
    printf("=== 内存信息 ===\n");
    
    // 读取内存块信息
    if (read_sysfs_file("/sys/devices/system/memory/block_size_bytes", buffer, sizeof(buffer)) == 0) {
        unsigned long block_size = strtoul(buffer, NULL, 16);
        printf("内存块大小: %lu 字节 (%.2f MB)\n", block_size, block_size / (1024.0 * 1024.0));
    }
    
    // 遍历内存块
    DIR *dir = opendir("/sys/devices/system/memory");
    if (dir) {
        struct dirent *entry;
        int online_count = 0, total_count = 0;
        
        while ((entry = readdir(dir)) != NULL) {
            if (strncmp(entry->d_name, "memory", 6) == 0) {
                total_count++;
                char path[256];
                snprintf(path, sizeof(path), 
                        "/sys/devices/system/memory/%s/state", entry->d_name);
                
                if (read_sysfs_file(path, buffer, sizeof(buffer)) == 0) {
                    if (strcmp(buffer, "online") == 0) {
                        online_count++;
                    }
                }
            }
        }
        
        printf("内存块总数: %d\n", total_count);
        printf("在线内存块: %d\n", online_count);
        printf("离线内存块: %d\n", total_count - online_count);
        
        closedir(dir);
    }
    
    printf("\n");
}

/**
 * 显示块设备信息
 */
void show_block_devices() {
    printf("=== 块设备信息 ===\n");
    
    DIR *dir = opendir("/sys/block");
    if (dir) {
        struct dirent *entry;
        
        printf("块设备列表:\n");
        while ((entry = readdir(dir)) != NULL) {
            if (entry->d_name[0] != '.') {
                char path[256];
                char buffer[256];
                
                // 读取设备大小
                snprintf(path, sizeof(path), "/sys/block/%s/size", entry->d_name);
                if (read_sysfs_file(path, buffer, sizeof(buffer)) == 0) {
                    unsigned long sectors = atol(buffer);
                    double size_gb = sectors * 512.0 / (1024.0 * 1024.0 * 1024.0);
                    printf("  %s: %.2f GB (%lu 扇区)\n", entry->d_name, size_gb, sectors);
                } else {
                    printf("  %s: (无法获取大小)\n", entry->d_name);
                }
                
                // 读取设备类型
                snprintf(path, sizeof(path), "/sys/block/%s/queue/rotational", entry->d_name);
                if (read_sysfs_file(path, buffer, sizeof(buffer)) == 0) {
                    if (atoi(buffer) == 1) {
                        printf("    类型: 机械硬盘\n");
                    } else {
                        printf("    类型: 固态硬盘\n");
                    }
                }
            }
        }
        
        closedir(dir);
    }
    
    printf("\n");
}

/**
 * 演示基础sysfs使用方法
 */
int demo_sysfs_basic() {
    printf("=== 基础sysfs使用示例 ===\n");
    
    // 检查sysfs是否挂载
    if (access("/sys", F_OK) == -1) {
        printf("sysfs未挂载或不可访问\n");
        return -1;
    }
    
    printf("sysfs挂载点: /sys\n");
    
    // 显示CPU信息
    show_cpu_info();
    
    // 显示内存信息
    show_memory_info();
    
    // 显示块设备信息
    show_block_devices();
    
    // 演示读取特定属性
    printf("=== 特定属性读取演示 ===\n");
    
    struct {
        const char *path;
        const char *description;
    } attributes[] = {
        {"/sys/class/dmi/id/product_name", "产品名称"},
        {"/sys/class/dmi/id/product_version", "产品版本"},
        {"/sys/class/dmi/id/bios_vendor", "BIOS厂商"},
        {"/sys/class/dmi/id/bios_version", "BIOS版本"},
        {"/sys/kernel/mm/transparent_hugepage/enabled", "透明大页状态"},
        {NULL, NULL}
    };
    
    for (int i = 0; attributes[i].path; i++) {
        char buffer[256];
        if (read_sysfs_file(attributes[i].path, buffer, sizeof(buffer)) == 0) {
            printf("%s: %s\n", attributes[i].description, buffer);
        } else {
            printf("%s: 无法读取 (%s)\n", attributes[i].description, strerror(errno));
        }
    }
    
    return 0;
}

int main() {
    return demo_sysfs_basic();
}

示例5:综合系统监控工具

#include <sys/sysinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <dirent.h>
#include <fcntl.h>

/**
 * 综合系统信息结构
 */
typedef struct {
    // 系统基本信息
    time_t current_time;
    time_t boot_time;
    unsigned long uptime_seconds;
    
    // 内存信息
    unsigned long total_ram_mb;
    unsigned long free_ram_mb;
    unsigned long used_ram_mb;
    unsigned long total_swap_mb;
    unsigned long free_swap_mb;
    unsigned long used_swap_mb;
    double ram_usage_percent;
    double swap_usage_percent;
    
    // 负载信息
    double load_avg_1min;
    double load_avg_5min;
    double load_avg_15min;
    unsigned int process_count;
    
    // 硬件信息
    int cpu_count;
    int online_cpu_count;
    char hostname[256];
} system_info_t;

/**
 * 获取综合系统信息
 */
int get_system_info(system_info_t *info) {
    struct sysinfo si;
    
    // 获取系统时间
    info->current_time = time(NULL);
    
    // 获取sysinfo信息
    if (sysinfo(&si) == -1) {
        return -1;
    }
    
    // 系统基本信息
    info->uptime_seconds = si.uptime;
    info->boot_time = info->current_time - si.uptime;
    
    // 内存信息
    unsigned long mem_unit = si.mem_unit ? si.mem_unit : 1;
    info->total_ram_mb = si.totalram * mem_unit / (1024 * 1024);
    info->free_ram_mb = si.freeram * mem_unit / (1024 * 1024);
    info->used_ram_mb = info->total_ram_mb - info->free_ram_mb;
    info->total_swap_mb = si.totalswap * mem_unit / (1024 * 1024);
    info->free_swap_mb = si.freeswap * mem_unit / (1024 * 1024);
    info->used_swap_mb = info->total_swap_mb - info->free_swap_mb;
    
    info->ram_usage_percent = info->total_ram_mb > 0 ? 
        (double)info->used_ram_mb / info->total_ram_mb * 100 : 0;
    info->swap_usage_percent = info->total_swap_mb > 0 ? 
        (double)info->used_swap_mb / info->total_swap_mb * 100 : 0;
    
    // 负载信息
    info->load_avg_1min = (double)si.loads[0] / (1 << SI_LOAD_SHIFT);
    info->load_avg_5min = (double)si.loads[1] / (1 << SI_LOAD_SHIFT);
    info->load_avg_15min = (double)si.loads[2] / (1 << SI_LOAD_SHIFT);
    info->process_count = si.procs;
    
    // 硬件信息
    info->cpu_count = sysconf(_SC_NPROCESSORS_ONLN);
    info->online_cpu_count = info->cpu_count;  // 简化处理
    
    // 主机名
    gethostname(info->hostname, sizeof(info->hostname) - 1);
    
    return 0;
}

/**
 * 显示综合系统信息
 */
void show_system_info(const system_info_t *info) {
    char time_str[64];
    char boot_time_str[64];
    
    printf("=========================================\n");
    printf("         系统监控报告\n");
    printf("=========================================\n");
    
    // 时间信息
    strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", localtime(&info->current_time));
    strftime(boot_time_str, sizeof(boot_time_str), "%Y-%m-%d %H:%M:%S", localtime(&info->boot_time));
    
    printf("主机名: %s\n", info->hostname);
    printf("当前时间: %s\n", time_str);
    printf("启动时间: %s\n", boot_time_str);
    
    unsigned long days = info->uptime_seconds / 86400;
    unsigned long hours = (info->uptime_seconds % 86400) / 3600;
    unsigned long minutes = (info->uptime_seconds % 3600) / 60;
    printf("运行时间: %lu 天 %lu 小时 %lu 分钟\n", days, hours, minutes);
    
    // CPU信息
    printf("\nCPU信息:\n");
    printf("  CPU核心数: %d\n", info->cpu_count);
    printf("  在线CPU数: %d\n", info->online_cpu_count);
    
    // 内存信息
    printf("\n内存信息:\n");
    printf("  物理内存: %lu MB / %lu MB (%.1f%%)\n", 
           info->used_ram_mb, info->total_ram_mb, info->ram_usage_percent);
    printf("  交换空间: %lu MB / %lu MB (%.1f%%)\n", 
           info->used_swap_mb, info->total_swap_mb, info->swap_usage_percent);
    
    // 负载信息
    printf("\n系统负载:\n");
    printf("  1分钟平均负载: %.2f\n", info->load_avg_1min);
    printf("  5分钟平均负载: %.2f\n", info->load_avg_5min);
    printf("  15分钟平均负载: %.2f\n", info->load_avg_15min);
    printf("  当前进程数: %u\n", info->process_count);
    
    // 系统健康状态
    printf("\n系统健康状态:\n");
    
    // 内存健康检查
    if (info->ram_usage_percent > 90) {
        printf("  ⚠ 内存使用率过高: %.1f%%\n", info->ram_usage_percent);
    } else if (info->ram_usage_percent > 80) {
        printf("  ℹ 内存使用率较高: %.1f%%\n", info->ram_usage_percent);
    } else {
        printf("  ✓ 内存使用率正常: %.1f%%\n", info->ram_usage_percent);
    }
    
    // 交换空间健康检查
    if (info->swap_usage_percent > 50) {
        printf("  ⚠ 交换空间使用过多: %.1f%%\n", info->swap_usage_percent);
    } else if (info->swap_usage_percent > 20) {
        printf("  ℹ 交换空间使用较多: %.1f%%\n", info->swap_usage_percent);
    } else {
        printf("  ✓ 交换空间使用正常: %.1f%%\n", info->swap_usage_percent);
    }
    
    // 负载健康检查
    double load_per_cpu = info->load_avg_1min / info->cpu_count;
    if (load_per_cpu > 1.5) {
        printf("  ⚠ 系统负载过重: 每CPU负载 %.2f\n", load_per_cpu);
    } else if (load_per_cpu > 1.0) {
        printf("  ℹ 系统负载中等: 每CPU负载 %.2f\n", load_per_cpu);
    } else {
        printf("  ✓ 系统负载正常: 每CPU负载 %.2f\n", load_per_cpu);
    }
    
    printf("=========================================\n\n");
}

/**
 * 实时监控模式
 */
void real_time_monitoring(int duration_seconds, int interval_seconds) {
    system_info_t info;
    time_t start_time = time(NULL);
    time_t current_time;
    int report_count = 0;
    
    printf("=== 实时系统监控 ===\n");
    printf("监控时长: %d 秒\n", duration_seconds);
    printf("监控间隔: %d 秒\n", interval_seconds);
    printf("开始监控...\n\n");
    
    while ((current_time = time(NULL)) - start_time < duration_seconds) {
        if (get_system_info(&info) == 0) {
            report_count++;
            printf("[第 %d 次监控报告] ", report_count);
            show_system_info(&info);
        } else {
            printf("获取系统信息失败: %s\n", strerror(errno));
        }
        
        // 等待下次监控
        sleep(interval_seconds);
    }
    
    printf("监控完成,共生成 %d 份报告\n", report_count);
}

/**
 * 演示综合系统监控工具
 */
int demo_comprehensive_monitor() {
    system_info_t info;
    
    printf("=== 综合系统监控工具演示 ===\n");
    
    // 单次系统信息获取
    printf("1. 当前系统状态:\n");
    if (get_system_info(&info) == 0) {
        show_system_info(&info);
    } else {
        printf("获取系统信息失败: %s\n", strerror(errno));
        return -1;
    }
    
    // 简短监控演示
    printf("2. 简短监控演示 (10秒):\n");
    real_time_monitoring(10, 3);
    
    // 系统信息统计
    printf("3. 系统信息统计:\n");
    
    // 收集统计信息
    unsigned long max_ram_used = 0, min_ram_used = (unsigned long)-1;
    double max_load_1min = 0, min_load_1min = 999;
    double total_ram_usage = 0, total_load_1min = 0;
    int sample_count = 0;
    
    printf("收集统计样本...\n");
    for (int i = 0; i < 5; i++) {
        if (get_system_info(&info) == 0) {
            // 更新最大最小值
            if (info.used_ram_mb > max_ram_used) max_ram_used = info.used_ram_mb;
            if (info.used_ram_mb < min_ram_used) min_ram_used = info.used_ram_mb;
            if (info.load_avg_1min > max_load_1min) max_load_1min = info.load_avg_1min;
            if (info.load_avg_1min < min_load_1min) min_load_1min = info.load_avg_1min;
            
            // 累加统计值
            total_ram_usage += info.ram_usage_percent;
            total_load_1min += info.load_avg_1min;
            sample_count++;
        }
        sleep(1);
    }
    
    // 显示统计结果
    if (sample_count > 0) {
        printf("\n统计结果 (%d 个样本):\n", sample_count);
        printf("  内存使用范围: %lu MB - %lu MB\n", min_ram_used, max_ram_used);
        printf("  内存使用率范围: %.1f%% - %.1f%%\n", 
               min_ram_used * 100.0 / info.total_ram_mb,
               max_ram_used * 100.0 / info.total_ram_mb);
        printf("  负载范围: %.2f - %.2f\n", min_load_1min, max_load_1min);
        printf("  平均内存使用率: %.1f%%\n", total_ram_usage / sample_count);
        printf("  平均1分钟负载: %.2f\n", total_load_1min / sample_count);
    }
    
    // 监控建议
    printf("\n=== 系统监控建议 ===\n");
    printf("1. 定期监控:\n");
    printf("   - 每5分钟检查一次关键指标\n");
    printf("   - 每小时生成详细报告\n");
    printf("   - 每天汇总统计信息\n");
    
    printf("\n2. 警报阈值:\n");
    printf("   - 内存使用率 > 90%% 触发警告\n");
    printf("   - 交换空间使用率 > 50%% 触发警告\n");
    printf("   - 每CPU负载 > 1.5 触发警告\n");
    printf("   - 进程数 > 1000 触发提醒\n");
    
    printf("\n3. 性能优化:\n");
    printf("   - 根据负载趋势调整资源配置\n");
    printf("   - 及时清理内存泄漏进程\n");
    printf("   - 优化高负载应用\n");
    printf("   - 合理配置交换空间\n");
    
    return 0;
}

int main() {
    return demo_comprehensive_monitor();
}

sysfs/sysinfo 使用注意事项

系统要求:

  1. 内核版本: 支持sysfs的Linux内核(2.6+)
  2. 权限要求: 通常不需要特殊权限
  3. 挂载要求: sysfs必须正确挂载(通常在/sys)

sysfs特点:

  1. 虚拟文件系统: 不占用磁盘空间
  2. 动态更新: 实时反映内核状态
  3. 层次结构: 按照设备类型组织
  4. 只读属性: 大部分文件是只读的

sysinfo特点:

  1. 轻量级: 系统调用开销小
  2. 实时性: 提供当前系统状态
  3. 标准化: 跨平台兼容性好
  4. 信息全面: 涵盖主要系统指标

错误处理:

  1. ENOENT: 文件或目录不存在
  2. EACCES: 权限不足
  3. EINVAL: 参数无效
  4. ENOMEM: 内存不足

性能考虑:

  1. 缓存利用: 适当缓存频繁访问的信息
  2. 批量读取: 减少系统调用次数
  3. 异步处理: 避免阻塞主线程
  4. 增量更新: 只更新变化的信息

安全考虑:

  1. 权限检查: 验证访问权限
  2. 输入验证: 验证读取的数据
  3. 资源限制: 避免过度消耗系统资源
  4. 日志记录: 记录重要操作

最佳实践:

  1. 信息整合: 综合多个信息源
  2. 趋势分析: 关注指标变化趋势
  3. 警报机制: 及时发现异常情况
  4. 可视化展示: 直观显示监控结果

sysfs目录结构

主要目录:

/sys/
├── block/          # 块设备信息
├── bus/            # 总线信息
├── class/          # 设备类信息
├── dev/            # 设备信息
├── devices/        # 设备树
├── firmware/       # 固件信息
├── fs/             # 文件系统信息
└── kernel/         # 内核信息

sysinfo结构详解

struct sysinfo:

struct sysinfo {
    long uptime;         // 系统运行时间(秒)
    unsigned long loads[3];  // 1,5,15分钟负载平均值
    unsigned long totalram;  // 总物理内存
    unsigned long freeram;   // 可用物理内存
    unsigned long sharedram; // 共享内存
    unsigned long bufferram; // 缓冲区内存
    unsigned long totalswap; // 总交换空间
    unsigned long freeswap;  // 可用交换空间
    unsigned short procs;    // 当前进程数
    unsigned long totalhigh; // 高端内存总量
    unsigned long freehigh;  // 可用高端内存
    unsigned int mem_unit;   // 内存单位
    char _f[20-2*sizeof(__kernel_ulong_t)-sizeof(__u32)]; // 填充
};

常见使用场景

1. 系统监控:

// 实时监控系统资源使用情况
struct sysinfo si;
sysinfo(&si);
double ram_usage = (si.totalram - si.freeram) * 100.0 / si.totalram;

2. 性能分析:

// 分析系统负载和性能瓶颈
double load_per_cpu = si.loads[0] / (1 << SI_LOAD_SHIFT) / cpu_count;

3. 资源管理:

// 根据系统资源动态调整应用行为
if (si.freeram * si.mem_unit < MIN_MEMORY_THRESHOLD) {
    // 内存不足,采取措施
}

4. 硬件信息查询:

// 通过sysfs查询硬件详细信息
char model[256];
read_sysfs_file("/sys/class/dmi/id/product_name", model, sizeof(model));

总结

sysfs 和 sysinfo 是Linux系统中重要的系统信息访问接口:

sysfs特点:

  1. 结构化信息: 提供详细的硬件和内核对象信息
  2. 动态更新: 实时反映系统状态变化
  3. 标准化接口: 统一的文件系统访问方式
  4. 丰富内容: 涵盖各类系统组件信息

sysinfo特点:

  1. 快速访问: 高效获取系统整体统计信息
  2. 标准API: 跨平台兼容的系统调用
  3. 全面指标: 包含内存、负载、进程等关键信息
  4. 实时监控: 适合构建监控和告警系统

通过合理使用这两个接口,可以构建功能强大的系统监控和管理工具,为系统运维和性能优化提供有力支持。在实际应用中,需要注意错误处理、性能优化和安全考虑等方面的问题。

发表在 linux文章 | 留下评论

syslog系统调用及示例

我们来深入学习 syslog 系统调用(注意:这里的 syslog 不是指用户空间的 syslog() 函数,而是指 Linux 内核提供的一个系统调用,用于从内核空间向用户空间的系统日志守护进程发送消息)

不过,通常当我们谈论在用户空间程序中记录日志时,我们指的是使用标准 C 库提供的 syslog(3) 函数。这个函数是用户空间 API,它在底层可能会使用 syslog 系统调用来与系统日志守护进程(如 rsyslogd 或 syslogd)通信。

让我们先明确一下:

  1. syslog 系统调用:这是内核使用的低级机制。
  2. syslog(3) 用户空间函数:这是程序员在应用程序中记录日志时使用的标准库函数。

由于直接调用 syslog 系统调用在用户空间编程中非常罕见且复杂,我们重点学习更常用、更相关的 syslog(3) 用户空间函数


1. 函数介绍 (针对 syslog(3) 用户空间函数)

在 Linux 系统中,程序需要记录运行状态、错误信息、调试信息等,这些信息对于系统管理员监控系统、诊断问题至关重要。将这些信息直接打印到终端是不合适的,特别是对于后台运行的守护进程(daemons)来说,它们通常没有终端。

syslog(3) 函数提供了一种标准化的方法,让程序可以将日志消息发送到系统的系统日志 (System Log) 设施。这个设施通常由一个守护进程(如 rsyslogd)管理,它负责接收来自内核和各种用户空间程序的日志消息,然后根据配置将它们写入文件(如 /var/log/messages/var/log/syslog)、发送到远程日志服务器,或在终端上显示。

简单来说,syslog(3) 就是让你的程序能够像系统内核或其他服务一样,把“日志”规范地记录到系统日志中,方便集中管理和查看。

典型应用场景

  • 守护进程 (Daemons):如 web 服务器、数据库服务器等后台服务,使用 syslog 记录启动、关闭、错误、警告等信息。
  • 系统工具:许多系统自带的命令行工具使用 syslog 来报告执行状态或错误。
  • 调试和监控:开发人员可以在程序中插入 syslog 调用来记录关键步骤或变量状态,帮助调试。

2. 函数原型

#include <syslog.h> // 包含 syslog 函数声明

// 打开到系统日志的连接
void openlog(const char *ident, int option, int facility);

// 写入一条日志消息
void syslog(int priority, const char *format, ...);

// 在日志消息中包含一组变量参数
void vsyslog(int priority, const char *format, va_list ap);

// 关闭到系统日志的连接
void closelog(void);

// (高级) 设置哪些日志消息会被丢弃
int setlogmask(int maskpri);

3. 功能

  • openlog: 打开一个到系统日志设施的连接(如果尚未打开),并设置一些默认参数,如程序名称、选项和默认的设施类型。
  • syslog: 将一条格式化的消息写入系统日志。
  • vsyslog: 与 syslog 功能相同,但参数列表使用 va_list,通常由其他变参函数调用。
  • closelog: 关闭到系统日志设施的连接。
  • setlogmask: 设置一个掩码,用于过滤掉优先级低于指定级别的日志消息。

4. 参数详解

openlog(const char *ident, int option, int facility)

  • ident:
    • const char * 类型。
    • 指向一个字符串,该字符串将被添加到每条日志消息的开头。通常设置为程序的名称,方便识别日志来源。例如,如果 ident 是 "my_daemon",日志可能显示为 my_daemon[12345]: ...
  • option:
    • int 类型。
    • 一个位掩码,用于指定 openlog 和后续 syslog 调用的各种选项。可以是以下值的按位或 (|) 组合:
      • LOG_CONS: 如果日志消息无法通过网络发送给日志守护进程,则直接写入系统控制台 (/dev/console)。
      • LOG_NDELAY: 立即打开到日志守护进程的连接,而不是等到第一条日志消息被写入时才打开。
      • LOG_NOWAIT: (已废弃,通常被忽略)。
      • LOG_ODELAY: 延迟打开连接,直到第一条日志消息被写入(这是默认行为,与 LOG_NDELAY 相反)。
      • LOG_PERROR: 除了将消息发送到系统日志外,还将消息输出到标准错误 (stderr)。
      • LOG_PID: 在每条日志消息中包含调用进程的 PID。例如:my_daemon[12345]: ...
  • facility:
    • int 类型。
    • 指定程序的类型或消息的来源类别。日志守护进程使用这个信息来决定如何处理消息(例如,存储到哪个文件)。常见的设施类型有:
      • LOG_AUTH: 安全/授权消息。
      • LOG_AUTHPRIV: 私有的安全/授权消息。
      • LOG_CRON: 定时任务守护进程 (cron) 产生的消息。
      • LOG_DAEMON: 系统守护进程产生的消息。
      • LOG_FTP: FTP 守护进程产生的消息。
      • LOG_KERN: 内核产生的消息。
      • LOG_LOCAL0 到 LOG_LOCAL7: 保留给本地使用。
      • LOG_LPR: 行式打印机系统产生的消息。
      • LOG_MAIL: 邮件系统产生的消息。
      • LOG_NEWS: 网络新闻系统产生的消息。
      • LOG_SYSLOG: 由 syslogd 内部产生的消息。
      • LOG_USER: 任意的用户级消息(这是默认值)。
      • LOG_UUCP: UUCP 子系统产生的消息。

syslog(int priority, const char *format, ...)

  • priority:
    • int 类型。
    • 指定消息的优先级(重要性)。它由设施类型严重性级别组成,通过按位或 (|) 操作符组合。
    • 严重性级别 (从高到低)
      • LOG_EMERG: 系统不可用 (Emergency)。
      • LOG_ALERT: 必须立即处理的动作 (Alert)。
      • LOG_CRIT: 严重情况 (Critical)。
      • LOG_ERR: 错误条件 (Error)。
      • LOG_WARNING: 警告条件 (Warning)。
      • LOG_NOTICE: 正常但重要的情况 (Notice)。
      • LOG_INFO: 信息性消息 (Informational)。
      • LOG_DEBUG: 调试级别的消息 (Debug)。
    • 设施类型:如果在此处指定了设施类型(如 LOG_DAEMON | LOG_ERR),它会覆盖 openlog 中设置的默认设施类型。如果未指定(只写级别,如 LOG_ERR),则使用 openlog 中的默认设施。
  • format:
    • const char * 类型。
    • 一个 printf 风格的格式字符串,用于指定日志消息的格式。
  • ...:
    • 可变参数列表,对应 format 字符串中的格式说明符。

closelog(void)

  • 无参数。关闭与系统日志守护进程的连接。这是一个可选调用,因为程序退出时连接会自动关闭。

setlogmask(int maskpri)

  • maskpri:
    • int 类型。
    • 一个位掩码,定义了哪些优先级的消息应该被处理。可以使用 LOG_MASK(priority) 宏来生成针对单个级别的掩码,或使用 LOG_UPTO(priority) 宏来生成包含从 LOG_EMERG 到指定 priority 的所有级别的掩码。
  • 返回值: 返回调用 setlogmask 之前的掩码。

5. 返回值

  • openlogsyslogvsyslogcloselog无返回值
  • setlogmask: 返回调用前的掩码。

6. 示例代码

下面的示例演示了如何使用 syslog(3) 函数族来记录不同类型和级别的日志消息。

#define _GNU_SOURCE // 启用 GNU 扩展
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h> // 包含 syslog 函数
#include <stdarg.h> // 包含 va_list, va_start, va_end
#include <sys/stat.h> // 包含权限常量
#include <fcntl.h>    // 包含 open, O_* flags
#include <errno.h>
#include <string.h>

// 一个模拟的复杂操作函数,使用 syslog 记录其步骤
void complex_operation(int param) {
    syslog(LOG_INFO, "Starting complex operation with param=%d", param);

    if (param < 0) {
        syslog(LOG_WARNING, "Warning: param %d is negative, using absolute value.", param);
        param = abs(param);
    }

    // 模拟一个可能失败的操作
    if (param == 0) {
        syslog(LOG_ERR, "Error: Invalid parameter (zero) for complex operation.");
        return; // 提前返回
    }

    // 模拟工作
    for (int i = 0; i < param; ++i) {
        // 只记录一部分调试信息,避免日志过多
        if (i % (param / 4 + 1) == 0) {
            syslog(LOG_DEBUG, "complex_operation: Processing step %d/%d", i, param);
        }
        // 模拟一些计算或 I/O
        usleep(100000); // 休眠 0.1 秒
    }

    syslog(LOG_INFO, "Finished complex operation successfully with param=%d", param);
}

// 一个使用 va_list 的包装函数示例
void my_log_wrapper(int priority, const char *format, ...) {
    va_list args;
    va_start(args, format);
    // 使用 vsyslog 记录消息
    vsyslog(priority, format, args);
    va_end(args);
}

int main() {
    printf("--- Demonstrating syslog ---\n");
    printf("PID: %d\n", getpid());

    // 1. 打开 syslog 连接
    // ident: "my_app" 会作为日志消息的前缀
    // LOG_PID: 在消息中包含进程 PID
    // LOG_CONS: 如果无法联系 syslogd,则写入控制台
    // LOG_PERROR: 同时将消息输出到 stderr
    // facility: LOG_USER 表示这是一般用户级消息
    openlog("my_app", LOG_PID | LOG_CONS | LOG_PERROR, LOG_USER);

    syslog(LOG_INFO, "Application started.");

    // 2. 演示不同级别的日志
    syslog(LOG_DEBUG, "This is a debug message. You might not see it depending on syslog config.");
    syslog(LOG_INFO, "This is an informational message.");
    syslog(LOG_NOTICE, "This is a notice message.");
    syslog(LOG_WARNING, "This is a warning message.");
    syslog(LOG_ERR, "This is an error message.");
    syslog(LOG_CRIT, "This is a critical message.");
    syslog(LOG_ALERT, "This is an alert message.");
    syslog(LOG_EMERG, "This is an emergency message.");

    // 3. 演示使用设施类型
    syslog(LOG_DAEMON | LOG_INFO, "This message has facility DAEMON.");

    // 4. 演示格式化消息
    int value = 42;
    const char *name = "example";
    syslog(LOG_INFO, "Formatted message: value=%d, name='%s'", value, name);

    // 5. 演示使用 setlogmask 过滤日志
    printf("\n--- Demonstrating setlogmask ---\n");
    // 先记录一条 Debug 消息 (可能看不到,取决于系统默认配置)
    syslog(LOG_DEBUG, "Debug message before setting mask.");

    // 设置掩码,只允许 INFO 级别及以上的消息通过
    // LOG_UPTO(LOG_INFO) 包含 LOG_EMERG, LOG_ALERT, ..., LOG_INFO
    setlogmask(LOG_UPTO(LOG_INFO));

    syslog(LOG_DEBUG, "Debug message after setting mask to UPTO INFO. (Should be masked out)");
    syslog(LOG_INFO, "Info message after setting mask. (Should be visible)");
    syslog(LOG_WARNING, "Warning message after setting mask. (Should be visible)");

    // 6. 调用模拟函数
    printf("\n--- Calling complex_operation ---\n");
    complex_operation(5);
    complex_operation(-3);
    complex_operation(0);

    // 7. 使用自定义包装函数
    printf("\n--- Using custom wrapper function ---\n");
    my_log_wrapper(LOG_INFO, "Message sent via custom wrapper function with value %d.", 100);

    // 8. 关闭 syslog 连接 (可选)
    syslog(LOG_INFO, "Application is shutting down.");
    closelog();

    printf("\n--- Finished ---\n");
    printf("Check your system log files (e.g., /var/log/messages, /var/log/syslog)\n");
    printf("or use 'journalctl' (on systemd systems) to see the logged messages.\n");
    printf("Look for entries prefixed with 'my_app'.\n");

    return 0;
}

7. 编译和运行

# 假设代码保存在 syslog_example.c 中
gcc -o syslog_example syslog_example.c

# 运行程序
./syslog_example

8. 查看日志

程序运行后,日志消息会被发送到系统的日志设施。如何查看取决于你的 Linux 发行版和配置:

  • 传统 syslog 系统
    • 检查 /var/log/messages 或 /var/log/syslog 文件。
    • 使用 tail -f /var/log/messages 实时查看。
  • systemd 系统 (使用 journald):
    • 使用 journalctl 命令:
      • journalctl -f:实时查看所有日志。
      • journalctl -t my_app:查看标签为 my_app 的日志。
      • journalctl -u <service_name>:查看特定服务的日志。

示例 journalctl 输出:

...
Feb 15 10:30:00 hostname my_app[12345]: Application started.
Feb 15 10:30:00 hostname my_app[12345]: This is an informational message.
Feb 15 10:30:00 hostname my_app[12345]: This is a notice message.
Feb 15 10:30:00 hostname my_app[12345]: This is a warning message.
Feb 15 10:30:00 hostname my_app[12345]: This is an error message.
Feb 15 10:30:00 hostname my_app[12345]: This is a critical message.
Feb 15 10:30:00 hostname my_app[12345]: This is an alert message.
Feb 15 10:30:00 hostname my_app[12345]: This is an emergency message.
Feb 15 10:30:00 hostname my_app[12345]: This message has facility DAEMON.
Feb 15 10:30:00 hostname my_app[12345]: Formatted message: value=42, name='example'
Feb 15 10:30:00 hostname my_app[12345]: Info message after setting mask. (Should be visible)
Feb 15 10:30:00 hostname my_app[12345]: Warning message after setting mask. (Should be visible)
Feb 15 10:30:00 hostname my_app[12345]: Starting complex operation with param=5
Feb 15 10:30:01 hostname my_app[12345]: complex_operation: Processing step 0/5
Feb 15 10:30:01 hostname my_app[12345]: complex_operation: Processing step 1/5
Feb 15 10:30:01 hostname my_app[12345]: complex_operation: Processing step 2/5
Feb 15 10:30:01 hostname my_app[12345]: complex_operation: Processing step 3/5
Feb 15 10:30:01 hostname my_app[12345]: complex_operation: Processing step 4/5
Feb 15 10:30:01 hostname my_app[12345]: Finished complex operation successfully with param=5
Feb 15 10:30:01 hostname my_app[12345]: Starting complex operation with param=3
Feb 15 10:30:01 hostname my_app[12345]: Warning: param 3 is negative, using absolute value.
Feb 15 10:30:01 hostname my_app[12345]: complex_operation: Processing step 0/3
Feb 15 10:30:01 hostname my_app[12345]: complex_operation: Processing step 1/3
Feb 15 10:30:01 hostname my_app[12345]: complex_operation: Processing step 2/3
Feb 15 10:30:01 hostname my_app[12345]: Finished complex operation successfully with param=3
Feb 15 10:30:01 hostname my_app[12345]: Starting complex operation with param=0
Feb 15 10:30:01 hostname my_app[12345]: Error: Invalid parameter (zero) for complex operation.
Feb 15 10:30:01 hostname my_app[12345]: Message sent via custom wrapper function with value 100.
Feb 15 10:30:01 hostname my_app[12345]: Application is shutting down.
...

9. 总结

syslog(3) 是 Linux 用户空间程序记录日志的标准且强大的工具。

  • openlog 用于初始化,设置日志来源标识、选项和默认设施。
  • syslog 用于实际写入日志消息,指定优先级和格式化内容。
  • closelog 用于清理(可选)。
  • setlogmask 用于在程序内部动态控制哪些级别的日志被记录。

通过使用 syslog,程序可以将日志集成到系统的统一日志管理框架中,这对于系统管理和故障排查非常有价值。它是编写健壮、可维护的 Linux 应用程序的基础技能之一。

关于真正的 syslog 系统调用

如果你确实需要了解内核空间的 syslog 系统调用(用于 syslogd 守护进程与内核通信,或使用 klogctl 函数从用户空间访问内核日志缓冲区),请告知,我可以提供相关信息。但对于应用程序员来说,syslog(3) 是日常记录日志的正确选择。

发表在 linux文章 | 留下评论

tee系统调用及示例tee

tee函数详解

1. 函数介绍

tee函数是Linux系统中一个非常实用的函数,它的作用就像现实中的”T型管道”一样。想象一下水管系统中的T型接头,水从主管道流入,同时流向两个不同的分支管道。在Linux编程中,tee函数就是这样一个”管道分流器”,它能够将一个管道中的数据同时复制到另一个管道中,而不需要将数据从内核空间复制到用户空间再复制回去。

使用场景:

  • 管道数据的复制和分流
  • 避免不必要的数据拷贝,提高程序性能
  • 实现数据的并行处理
  • 日志记录系统中同时写入多个输出流

2. 函数原型

#include <fcntl.h>
ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags);

3. 功能

tee函数的主要功能是在两个管道描述符之间高效地复制数据。它不经过用户空间,直接在内核中完成数据的复制,这大大提高了效率。这个函数特别适用于需要将数据从一个管道同时发送到多个目的地的场景。

4. 参数

  • fd_in: 源管道文件描述符(输入端)
    • 类型:int
    • 含义:数据来源的管道描述符,必须是管道或套接字
  • fd_out: 目标管道文件描述符(输出端)
    • 类型:int
    • 含义:数据目标的管道描述符,必须是管道或套接字
  • len: 要复制的数据长度
    • 类型:size_t
    • 含义:希望复制的最大字节数
  • flags: 操作标志
    • 类型:unsigned int
    • 含义:控制复制行为的标志位
    • 常用值:
      • SPLICE_F_MOVE:尽可能移动页面而不是复制
      • SPLICE_F_NONBLOCK:非阻塞操作
      • SPLICE_F_MORE:提示还有更多数据要写入
      • SPLICE_F_GIFT:页面是礼品(内核内部使用)

5. 返回值

  • 成功: 返回实际复制的字节数(ssize_t类型)
  • 失败: 返回-1,并设置errno错误码
    • EINVAL:参数无效
    • EBADF:文件描述符无效
    • ESPIPE:文件描述符不是管道
    • ENOMEM:内存不足

6. 相似函数或关联函数

  • splice(): 在文件描述符之间移动数据
  • vmsplice(): 从用户空间缓冲区向管道写入数据
  • read()/write(): 传统的数据读写函数
  • pipe(): 创建管道

7. 示例代码

示例1:基础tee使用 – 简单的数据分流

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

int main() {
    int pipe1[2], pipe2[2];  // 创建两个管道
    char buffer1[100], buffer2[100];
    ssize_t bytes_written, bytes_read;
    
    // 创建管道1
    if (pipe(pipe1) == -1) {
        perror("创建管道1失败");
        exit(EXIT_FAILURE);
    }
    
    // 创建管道2
    if (pipe(pipe2) == -1) {
        perror("创建管道2失败");
        exit(EXIT_FAILURE);
    }
    
    // 向管道1写入数据
    const char* message = "Hello, tee function!";
    write(pipe1[1], message, strlen(message));
    
    // 使用tee函数将管道1的数据复制到管道2
    // 注意:tee不消耗数据,数据仍保留在源管道中
    bytes_written = tee(pipe1[0], pipe2[1], strlen(message), SPLICE_F_NONBLOCK);
    
    if (bytes_written == -1) {
        perror("tee函数执行失败");
        exit(EXIT_FAILURE);
    }
    
    printf("tee函数复制了 %zd 字节的数据\n", bytes_written);
    
    // 从管道1读取数据(验证数据仍然存在)
    bytes_read = read(pipe1[0], buffer1, sizeof(buffer1) - 1);
    if (bytes_read > 0) {
        buffer1[bytes_read] = '\0';
        printf("从管道1读取: %s\n", buffer1);
    }
    
    // 从管道2读取数据(验证数据已成功复制)
    bytes_read = read(pipe2[0], buffer2, sizeof(buffer2) - 1);
    if (bytes_read > 0) {
        buffer2[bytes_read] = '\0';
        printf("从管道2读取: %s\n", buffer2);
    }
    
    // 关闭所有文件描述符
    close(pipe1[0]);
    close(pipe1[1]);
    close(pipe2[0]);
    close(pipe2[1]);
    
    return 0;
}

示例2:tee实现日志同时输出到文件和终端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

int main() {
    int log_pipe[2], stdout_pipe[2];
    int log_file;
    ssize_t bytes_copied;
    const char* log_message = "这是一个重要的日志信息\n";
    
    // 创建管道用于日志处理
    if (pipe(log_pipe) == -1) {
        perror("创建日志管道失败");
        exit(EXIT_FAILURE);
    }
    
    // 创建管道用于标准输出
    if (pipe(stdout_pipe) == -1) {
        perror("创建标准输出管道失败");
        exit(EXIT_FAILURE);
    }
    
    // 打开日志文件
    log_file = open("app.log", O_WRONLY | O_CREAT | O_APPEND, 0644);
    if (log_file == -1) {
        perror("打开日志文件失败");
        exit(EXIT_FAILURE);
    }
    
    // 向日志管道写入消息
    write(log_pipe[1], log_message, strlen(log_message));
    
    // 使用tee将日志同时复制到标准输出管道和日志文件
    // 第一次tee:复制到标准输出管道
    bytes_copied = tee(log_pipe[0], stdout_pipe[1], strlen(log_message), SPLICE_F_MORE);
    if (bytes_copied == -1) {
        perror("tee复制到标准输出失败");
        exit(EXIT_FAILURE);
    }
    
    // 第二次tee:复制到日志文件(使用splice,因为文件不是管道)
    // 注意:tee只能用于管道之间,文件需要使用splice
    bytes_copied = splice(log_pipe[0], NULL, log_file, NULL, strlen(log_message), SPLICE_F_MOVE);
    if (bytes_copied == -1) {
        perror("splice复制到日志文件失败");
        exit(EXIT_FAILURE);
    }
    
    // 从标准输出管道读取并显示
    char output_buffer[256];
    ssize_t bytes_read = read(stdout_pipe[0], output_buffer, sizeof(output_buffer) - 1);
    if (bytes_read > 0) {
        output_buffer[bytes_read] = '\0';
        printf("终端输出: %s", output_buffer);
    }
    
    printf("日志已同时写入文件和终端\n");
    
    // 清理资源
    close(log_pipe[0]);
    close(log_pipe[1]);
    close(stdout_pipe[0]);
    close(stdout_pipe[1]);
    close(log_file);
    
    return 0;
}

示例3:tee与管道链结合使用

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>

int main() {
    int pipe1[2], pipe2[2], pipe3[2];
    pid_t pid1, pid2;
    
    // 创建三个管道
    if (pipe(pipe1) == -1 || pipe(pipe2) == -1 || pipe(pipe3) == -1) {
        perror("创建管道失败");
        exit(EXIT_FAILURE);
    }
    
    // 创建第一个子进程 - 数据生产者
    pid1 = fork();
    if (pid1 == 0) {
        // 子进程1:向管道1写入数据
        close(pipe1[0]); // 关闭读端
        const char* data = "数据流: 1 2 3 4 5\n";
        write(pipe1[1], data, strlen(data));
        close(pipe1[1]);
        exit(0);
    }
    
    // 创建第二个子进程 - 数据处理和分流
    pid2 = fork();
    if (pid2 == 0) {
        // 子进程2:使用tee分流数据
        close(pipe1[1]); // 关闭管道1的写端
        close(pipe2[0]); // 关闭管道2的读端
        close(pipe3[0]); // 关闭管道3的读端
        
        // 使用tee将管道1的数据同时复制到管道2和管道3
        ssize_t copied = tee(pipe1[0], pipe2[1], 1024, SPLICE_F_MORE);
        if (copied > 0) {
            // 再次tee复制到第三个管道
            tee(pipe1[0], pipe3[1], 1024, 0);
        }
        
        close(pipe1[0]);
        close(pipe2[1]);
        close(pipe3[1]);
        exit(0);
    }
    
    // 父进程:读取分流后的数据
    close(pipe1[0]); close(pipe1[1]); // 父进程不需要管道1
    close(pipe2[1]); // 父进程不需要管道2的写端
    close(pipe3[1]); // 父进程不需要管道3的写端
    
    // 等待子进程完成
    waitpid(pid1, NULL, 0);
    waitpid(pid2, NULL, 0);
    
    // 读取管道2的数据
    char buffer2[256];
    ssize_t bytes2 = read(pipe2[0], buffer2, sizeof(buffer2) - 1);
    if (bytes2 > 0) {
        buffer2[bytes2] = '\0';
        printf("处理器1收到: %s", buffer2);
    }
    
    // 读取管道3的数据
    char buffer3[256];
    ssize_t bytes3 = read(pipe3[0], buffer3, sizeof(buffer3) - 1);
    if (bytes3 > 0) {
        buffer3[bytes3] = '\0';
        printf("处理器2收到: %s", buffer3);
    }
    
    // 清理资源
    close(pipe2[0]);
    close(pipe3[0]);
    
    return 0;
}

编译和运行

# 编译示例1
gcc -o tee_example1 tee_example1.c
./tee_example1

# 编译示例2
gcc -o tee_example2 tee_example2.c
./tee_example2

# 编译示例3
gcc -o tee_example3 tee_example3.c
./tee_example3

通过这些示例,你可以看到tee函数在数据分流、日志处理和管道链中的强大应用。记住,tee函数的关键优势是避免了用户空间和内核空间之间的数据拷贝,这在处理大量数据时能显著提高性能。

发表在 linux文章 | 留下评论

tgkill系统调用及示例

tgkill函数详解

1. 函数介绍

tgkill函数是Linux系统中一个精确的进程控制函数,它的名字来源于”Thread Group Kill”(线程组杀死)。在Linux中,每个进程实际上是一个线程组,主线程的ID就是进程ID。tgkill允许你精确地向指定进程组中的特定线程发送信号。

可以把tgkill想象成一个”精确制导导弹”,它不仅能指定攻击哪个”军队”(进程组),还能精确指定攻击该军队中的哪个”士兵”(特定线程)。相比之下,传统的kill函数更像是”地毯式轰炸”,可能会影响整个进程组。

使用场景:

  • 多线程程序中精确控制特定线程
  • 线程调试和测试
  • 实现线程间通信机制
  • 精确的线程终止控制

2. 函数原型

#include <sys/syscall.h>
#include <signal.h>

long syscall(SYS_tgkill, int tgid, int tid, int sig);
// 或者使用封装函数(如果系统提供)
int tgkill(int tgid, int tid, int sig);

注意:tgkill不是标准C库函数,通常需要通过系统调用来使用。

3. 功能

tgkill函数的主要功能是向指定的线程组中的特定线程发送信号。它提供了比传统kill函数更高的精确度:

  • tgid(Thread Group ID):线程组ID,通常是进程ID
  • tid(Thread ID):线程ID,指定线程组中的具体线程
  • sig:要发送的信号

4. 参数

  • tgid: 线程组ID(Thread Group ID)
    • 类型:int
    • 含义:目标线程所属的线程组ID,通常等于进程ID(PID)
  • tid: 线程ID(Thread ID)
    • 类型:int
    • 含义:线程组中具体线程的ID,必须是该线程组中的有效线程
  • sig: 信号编号
    • 类型:int
    • 含义:要发送给目标线程的信号,如SIGTERM、SIGKILL、SIGUSR1等

5. 返回值

  • 成功: 返回0
  • 失败: 返回-1,并设置errno错误码
    • EINVAL:信号编号无效
    • ESRCH:找不到指定的线程组或线程
    • EPERM:没有权限向目标线程发送信号
    • EAGAIN:系统资源不足

6. 相似函数或关联函数

  • kill(): 向进程发送信号
  • pthread_kill(): 向POSIX线程发送信号
  • raise(): 向当前进程发送信号
  • signal()/sigaction(): 设置信号处理函数
  • getpid(): 获取当前进程ID
  • gettid(): 获取当前线程ID

7. 示例代码

示例1:基础tgkill使用 – 精确控制线程

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <signal.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>

// 获取线程ID的辅助函数
pid_t gettid(void) {
    return syscall(SYS_gettid);
}

// 线程处理函数
void signal_handler(int sig) {
    printf("线程 %d 收到信号 %d (%s)\n", gettid(), sig, strsignal(sig));
}

// 工作线程函数
void* worker_thread(void* arg) {
    int thread_num = *(int*)arg;
    pid_t tid = gettid();
    
    printf("工作线程 %d 启动,线程ID: %d\n", thread_num, tid);
    
    // 设置信号处理函数
    signal(SIGUSR1, signal_handler);
    signal(SIGTERM, signal_handler);
    
    // 线程主循环
    while(1) {
        printf("线程 %d 正在工作...\n", tid);
        sleep(2);
    }
    
    return NULL;
}

// tgkill系统调用封装
int tgkill(int tgid, int tid, int sig) {
    return syscall(SYS_tgkill, tgid, tid, sig);
}

int main() {
    pthread_t threads[3];
    int thread_nums[3] = {1, 2, 3};
    pid_t main_tid = gettid();
    pid_t pids[3];
    int i;
    
    printf("主线程ID: %d, 进程ID: %d\n", main_tid, getpid());
    
    // 创建多个工作线程
    for(i = 0; i < 3; i++) {
        if(pthread_create(&threads[i], NULL, worker_thread, &thread_nums[i]) != 0) {
            perror("创建线程失败");
            exit(EXIT_FAILURE);
        }
        // 给线程一些时间启动
        sleep(1);
        pids[i] = gettid(); // 这里简化处理,实际应该从线程中获取
    }
    
    // 让线程运行一段时间
    sleep(3);
    
    // 向特定线程发送自定义信号
    printf("\n=== 向线程发送SIGUSR1信号 ===\n");
    if(tgkill(getpid(), pids[0], SIGUSR1) == 0) {
        printf("成功向线程 %d 发送 SIGUSR1 信号\n", pids[0]);
    } else {
        perror("tgkill发送信号失败");
    }
    
    sleep(2);
    
    // 向另一个线程发送终止信号
    printf("\n=== 向线程发送SIGTERM信号 ===\n");
    if(tgkill(getpid(), pids[1], SIGTERM) == 0) {
        printf("成功向线程 %d 发送 SIGTERM 信号\n", pids[1]);
    } else {
        perror("tgkill发送信号失败");
    }
    
    sleep(2);
    
    // 演示错误处理
    printf("\n=== 演示错误处理 ===\n");
    if(tgkill(999999, 999999, SIGTERM) == -1) {
        printf("向不存在的线程发送信号失败(预期行为): %s\n", strerror(errno));
    }
    
    // 等待几秒观察结果
    sleep(3);
    
    // 强制终止所有线程
    printf("\n=== 终止所有线程 ===\n");
    for(i = 0; i < 3; i++) {
        tgkill(getpid(), pids[i], SIGKILL);
    }
    
    return 0;
}

示例2:线程监控和管理

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <signal.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <time.h>

#define MAX_THREADS 5

// 线程信息结构体
typedef struct {
    pthread_t thread;
    pid_t tid;
    int id;
    int running;
    time_t start_time;
} thread_info_t;

thread_info_t threads[MAX_THREADS];
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

// 获取线程ID
pid_t gettid(void) {
    return syscall(SYS_gettid);
}

// 信号处理函数
void signal_handler(int sig) {
    pid_t tid = gettid();
    printf("[信号处理] 线程 %d 收到信号: %s\n", tid, strsignal(sig));
    
    if(sig == SIGUSR1) {
        printf("[信号处理] 线程 %d 将暂停工作\n", tid);
        sleep(5);
        printf("[信号处理] 线程 %d 恢复工作\n", tid);
    } else if(sig == SIGTERM) {
        printf("[信号处理] 线程 %d 准备退出\n", tid);
        pthread_exit(NULL);
    }
}

// 工作线程函数
void* worker_thread(void* arg) {
    thread_info_t* info = (thread_info_t*)arg;
    info->tid = gettid();
    info->running = 1;
    info->start_time = time(NULL);
    
    printf("工作线程 %d 启动,系统线程ID: %d\n", info->id, info->tid);
    
    // 设置信号处理
    signal(SIGUSR1, signal_handler);
    signal(SIGTERM, signal_handler);
    signal(SIGINT, signal_handler);
    
    // 工作循环
    while(info->running) {
        printf("线程 %d 正在处理任务...\n", info->tid);
        sleep(3);
    }
    
    printf("线程 %d 正常退出\n", info->tid);
    return NULL;
}

// tgkill封装函数
int tgkill(int tgid, int tid, int sig) {
    return syscall(SYS_tgkill, tgid, tid, sig);
}

// 显示所有线程状态
void show_thread_status() {
    printf("\n=== 线程状态 ===\n");
    pthread_mutex_lock(&mutex);
    for(int i = 0; i < MAX_THREADS; i++) {
        if(threads[i].tid != 0) {
            time_t uptime = time(NULL) - threads[i].start_time;
            printf("线程 %d: TID=%d, 状态=%s, 运行时间=%lds\n", 
                   threads[i].id, threads[i].tid,
                   threads[i].running ? "运行中" : "已停止",
                   uptime);
        }
    }
    pthread_mutex_unlock(&mutex);
    printf("================\n\n");
}

int main() {
    int i;
    
    printf("主线程监控程序启动,PID: %d\n", getpid());
    
    // 初始化线程信息
    memset(threads, 0, sizeof(threads));
    
    // 创建工作线程
    for(i = 0; i < MAX_THREADS; i++) {
        threads[i].id = i + 1;
        if(pthread_create(&threads[i].thread, NULL, worker_thread, &threads[i]) != 0) {
            perror("创建线程失败");
            exit(EXIT_FAILURE);
        }
        printf("创建线程 %d\n", i + 1);
    }
    
    // 给线程一些启动时间
    sleep(2);
    
    // 显示初始状态
    show_thread_status();
    
    // 演示各种tgkill操作
    printf("=== 演示tgkill操作 ===\n");
    
    // 向第一个线程发送暂停信号
    if(tgkill(getpid(), threads[0].tid, SIGUSR1) == 0) {
        printf("向线程 %d 发送暂停信号\n", threads[0].tid);
    }
    
    sleep(1);
    
    // 向第三个线程发送终止信号
    if(tgkill(getpid(), threads[2].tid, SIGTERM) == 0) {
        printf("向线程 %d 发送终止信号\n", threads[2].tid);
        pthread_mutex_lock(&mutex);
        threads[2].running = 0;
        pthread_mutex_unlock(&mutex);
    }
    
    sleep(2);
    show_thread_status();
    
    // 向所有线程发送中断信号
    printf("向所有线程发送中断信号...\n");
    pthread_mutex_lock(&mutex);
    for(i = 0; i < MAX_THREADS; i++) {
        if(threads[i].tid != 0 && threads[i].running) {
            if(tgkill(getpid(), threads[i].tid, SIGINT) == 0) {
                printf("向线程 %d 发送中断信号\n", threads[i].tid);
            }
        }
    }
    pthread_mutex_unlock(&mutex);
    
    sleep(2);
    
    // 等待所有线程结束
    for(i = 0; i < MAX_THREADS; i++) {
        if(threads[i].tid != 0) {
            pthread_join(threads[i].thread, NULL);
            printf("线程 %d 已结束\n", threads[i].id);
        }
    }
    
    printf("所有线程已安全退出,程序结束\n");
    return 0;
}

示例3:错误处理和权限检查

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <signal.h>
#include <errno.h>
#include <string.h>

// tgkill封装函数
int tgkill(int tgid, int tid, int sig) {
    return syscall(SYS_tgkill, tgid, tid, sig);
}

// 错误处理函数
void handle_error(const char* operation) {
    switch(errno) {
        case EINVAL:
            printf("%s 失败: 无效的信号编号\n", operation);
            break;
        case ESRCH:
            printf("%s 失败: 找不到指定的线程或进程\n", operation);
            break;
        case EPERM:
            printf("%s 失败: 权限不足\n", operation);
            break;
        default:
            printf("%s 失败: %s\n", operation, strerror(errno));
            break;
    }
}

int main() {
    pid_t my_pid = getpid();
    pid_t my_tid = syscall(SYS_gettid);
    
    printf("当前进程信息:\n");
    printf("  进程ID (PID): %d\n", my_pid);
    printf("  线程ID (TID): %d\n", my_tid);
    printf("  用户ID (UID): %d\n", getuid());
    printf("  有效用户ID (EUID): %d\n", geteuid());
    printf("\n");
    
    // 测试1: 向自己发送信号(应该成功)
    printf("=== 测试1: 向当前线程发送信号 ===\n");
    if(tgkill(my_pid, my_tid, SIGUSR1) == 0) {
        printf("✓ 成功向当前线程发送信号\n");
    } else {
        handle_error("向当前线程发送信号");
    }
    
    // 测试2: 使用无效信号编号
    printf("\n=== 测试2: 使用无效信号编号 ===\n");
    if(tgkill(my_pid, my_tid, 999) == -1) {
        handle_error("使用无效信号");
    }
    
    // 测试3: 向不存在的进程发送信号
    printf("\n=== 测试3: 向不存在的进程发送信号 ===\n");
    if(tgkill(999999, 999999, SIGTERM) == -1) {
        handle_error("向不存在的进程发送信号");
    }
    
    // 测试4: 向不存在的线程发送信号
    printf("\n=== 测试4: 向不存在的线程发送信号 ===\n");
    if(tgkill(my_pid, 999999, SIGTERM) == -1) {
        handle_error("向不存在的线程发送信号");
    }
    
    // 测试5: 发送各种标准信号
    printf("\n=== 测试5: 发送标准信号 ===\n");
    int signals[] = {SIGHUP, SIGINT, SIGQUIT, SIGUSR1, SIGUSR2};
    const char* signal_names[] = {"SIGHUP", "SIGINT", "SIGQUIT", "SIGUSR1", "SIGUSR2"};
    int num_signals = sizeof(signals) / sizeof(signals[0]);
    
    for(int i = 0; i < num_signals; i++) {
        printf("发送 %s (信号 %d)... ", signal_names[i], signals[i]);
        if(tgkill(my_pid, my_tid, signals[i]) == 0) {
            printf("成功\n");
        } else {
            printf("失败\n");
        }
    }
    
    printf("\n=== 完成所有测试 ===\n");
    printf("注意: 某些信号可能被系统忽略或有特殊处理\n");
    
    return 0;
}

编译和运行

# 编译示例1(需要链接pthread库)
gcc -o tgkill_example1 tgkill_example1.c -lpthread
./tgkill_example1

# 编译示例2
gcc -o tgkill_example2 tgkill_example2.c -lpthread
./tgkill_example2

# 编译示例3
gcc -o tgkill_example3 tgkill_example3.c
./tgkill_example3

重要注意事项

  1. 权限要求: tgkill需要适当的权限才能向目标线程发送信号
  2. 线程ID获取: 需要在线程内部调用syscall(SYS_gettid)获取真实的线程ID
  3. 错误处理: 必须检查返回值并适当处理错误
  4. 信号安全: 在信号处理函数中只能调用异步信号安全的函数
  5. 跨平台兼容性: tgkill是Linux特有的系统调用,不适用于其他操作系统

通过这些示例,你可以理解tgkill在精确线程控制方面的强大功能,它为多线程程序提供了精细化的控制能力。

发表在 linux文章 | 留下评论

timerfd_create, timerfd_gettime, timerfd_settime系统调用及示例

好的,我们来深入学习 timerfd 相关的系统调用 (timerfd_createtimerfd_gettimetimerfd_settime)

1. 函数介绍

在 Linux 系统编程中,处理定时事件是一个常见需求。传统的定时方法包括:

  1. alarm() 和 setitimer() + 信号 (SIGALRM):当定时器到期时,内核会发送一个信号给进程。这属于异步通知方式。信号处理函数有诸多限制(只能调用异步安全函数),并且容易引入竞态条件。
  2. sleep() 系列函数:让进程休眠指定时间。这会阻塞进程,不够灵活。

timerfd 是 Linux 2.6.25 引入的一种基于文件描述符 (File Descriptor) 的定时器接口。它巧妙地将定时器“变成”了一个可以像文件一样操作的描述符。

  • 你调用 timerfd_create() 创建一个定时器,它会返回一个文件描述符
  • 你调用 timerfd_settime() 来启动或修改这个定时器的超时时间和间隔。
  • 当定时器到期时,这个文件描述符就会变为可读状态。
  • 你可以在程序的主循环中使用 read()poll()select() 或 epoll_wait() 等 I/O 多路复用函数来监听这个文件描述符。当 read() 成功时,就意味着定时器超时了。

简单来说,timerfd 就是把定时器“变成”了可以像文件一样读取的数据流,让你可以用处理文件 I/O 或网络 I/O 的方式来处理定时事件。

典型应用场景

  • 事件驱动服务器:将定时器集成到 epoll/poll 的主循环中,统一处理网络 I/O 和定时事件。
  • 避免信号处理的复杂性:用同步的 read() 替代异步的信号处理函数。
  • 精确控制定时:可以创建一次性定时器或周期性定时器。

2. 函数原型

#include <sys/timerfd.h> // 包含 timerfd 相关函数和结构体

// 创建一个定时器并返回文件描述符
int timerfd_create(int clockid, int flags);

// 获取定时器的当前设置
int timerfd_gettime(int fd, struct itimerspec *curr_value);

// 启动或重新设置定时器
int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);

3. 功能

  • timerfd_create: 创建一个新的定时器对象,并返回一个与之关联的文件描述符。
  • timerfd_gettime: 查询指定定时器文件描述符的当前定时设置(下次超时时间和间隔)。
  • timerfd_settime: 启动、停止或修改指定定时器文件描述符的定时设置。

4. 参数详解

timerfd_create(int clockid, int flags)

  • clockid:
    • int 类型。
    • 指定定时器基于哪个时钟源。常用的有:
      • CLOCK_REALTIME: 系统实时时钟,表示从 Unix 纪元(1970-01-01 00:00:00 UTC)开始的时间。如果系统时间被手动修改,这个时钟会受到影响。
      • CLOCK_MONOTONIC: 单调时钟,表示从某个未指定起点开始的时间,不会受到系统时间调整的影响,是测量间隔的首选。
  • flags:
    • int 类型。
    • 用于修改定时器行为的标志位。常用的有:
      • TFD_CLOEXEC: 在执行 exec() 系列函数时自动关闭该文件描述符。
      • TFD_NONBLOCK: 使 read() 操作变为非阻塞模式。如果当前没有定时器到期事件可读,read() 会立即返回 -1 并设置 errno 为 EAGAIN 或 EWOULDBLOCK
  • 返回值:
    • 成功: 返回一个有效的定时器文件描述符(非负整数)。
    • 失败: 返回 -1,并设置 errno

timerfd_gettime(int fd, struct itimerspec *curr_value)

  • fd:
    • int 类型。
    • 一个有效的定时器文件描述符。
  • curr_value:
    • struct itimerspec * 类型。
    • 一个指向 struct itimerspec 结构体的指针。函数调用成功后,会将定时器的当前设置填充到这个结构体中。
  • 返回值:
    • 成功: 返回 0。
    • 失败: 返回 -1,并设置 errno

timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value)

  • fd:
    • int 类型。
    • 一个有效的定时器文件描述符。
  • flags:
    • int 类型。
    • 控制 new_value 中时间的解释方式。主要标志:
      • 0 (默认): new_value->it_value 指定的是从当前时间点开始的相对超时时间。
      • TFD_TIMER_ABSTIMEnew_value->it_value 指定的是一个绝对的超时时间点(基于创建定时器时指定的 clockid)。
  • new_value:
    • const struct itimerspec * 类型。
    • 指向一个 struct itimerspec 结构体,定义了新的定时器设置。
  • old_value:
    • struct itimerspec * 类型。
    • 如果不为 NULL,函数调用成功后,会将定时器在设置前的旧设置填充到这个结构体中。如果不需要旧值,可以传 NULL
  • 返回值:
    • 成功: 返回 0。
    • 失败: 返回 -1,并设置 errno

struct itimerspec 结构体

这个结构体用于定义定时器的时间设置:

struct timespec {
    time_t tv_sec;  /* 秒 */
    long   tv_nsec; /* 纳秒 */
};

struct itimerspec {
    struct timespec it_interval; /* 定时器的时间间隔 (周期性定时器) */
    struct timespec it_value;    /* 首次超时的相对/绝对时间 */
};
  • it_value: 指定首次超时的时间。
    • 如果 tv_sec 和 tv_nsec 都为 0,则定时器被停止
    • 如果非 0,则指定定时器下次到期的时间。
  • it_interval: 指定定时器到期后,重复的间隔时间。
    • 如果 tv_sec 和 tv_nsec 都为 0,则定时器是一次性的
    • 如果非 0,则定时器在首次到期后,会按此间隔周期性地重复到期。

5. 如何使用定时器文件描述符

  1. 创建: 使用 timerfd_create() 创建定时器,获得 fd
  2. 设置: 使用 timerfd_settime() 配置定时器的超时和间隔。
  3. 等待: 在主循环中,使用 poll()select()epoll_wait() 或直接 read() (如果设置为阻塞) 来等待 fd 变为可读。
  4. 读取: 当 fd 可读时,调用 read(fd, &expirations, sizeof(expirations))read() 会成功,并且读取到一个 uint64_t 类型的整数,表示自上次 read() 以来定时器到期的次数
  5. 查询/修改: 可以随时使用 timerfd_gettime() 查询当前设置,或再次调用 timerfd_settime() 修改设置。

6. 错误码 (errno)

这些函数共享一些常见的错误码:

  • EINVAL: 参数无效(例如 clockid 无效,flags 无效,itimerspec 字段值无效)。
  • EMFILE: 进程已打开的文件描述符数量达到上限 (RLIMIT_NOFILE)。
  • ENFILE: 系统已打开的文件描述符数量达到上限。
  • ENOMEM: 内核内存不足。
  • EBADFfd 不是有效的文件描述符。
  • EFAULTnew_valueold_value, 或 curr_value 指向无效内存。

7. 相似函数或关联函数

  • alarm / setitimer: 传统的基于信号的定时器。
  • pollselectepoll_wait: I/O 多路复用函数,可以监听 timerfd 文件描述符的可读事件。
  • read: 用于从 timerfd 中读取到期次数。
  • close: 关闭 timerfd 文件描述符。
  • clock_gettime / clock_settime: 获取和设置不同类型的系统时钟。
  • POSIX 定时器 (timer_createtimer_settimetimer_gettime): 另一套定时器 API,也使用信号通知。

8. 示例代码

下面的示例演示了如何使用 timerfd 创建一次性定时器和周期性定时器,并将其集成到 poll 系统调用中。

#define _GNU_SOURCE // 启用 GNU 扩展
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/timerfd.h> // timerfd 相关
#include <poll.h>       // poll 相关
#include <string.h>
#include <errno.h>
#include <inttypes.h>   // 包含 PRIu64 宏,用于 printf uint64_t

// 辅助函数:打印 itimerspec 结构
void print_itimerspec(const char* prefix, const struct itimerspec *ts) {
    printf("%s: ", prefix);
    if (ts->it_value.tv_sec == 0 && ts->it_value.tv_nsec == 0) {
        printf("Timer is STOPPED\n");
    } else {
        printf("Next expiration in %ld.%09ld seconds\n", ts->it_value.tv_sec, ts->it_value.tv_nsec);
    }
    printf("     Interval: %ld.%09ld seconds\n", ts->it_interval.tv_sec, ts->it_interval.tv_nsec);
}

int main() {
    int tfd_one_shot, tfd_periodic;
    struct itimerspec one_shot_time, periodic_time;
    struct pollfd fds[3]; // 监听两个 timerfd 和 标准输入
    uint64_t expirations;
    ssize_t s;

    printf("--- Demonstrating timerfd ---\n");
    printf("PID: %d\n", getpid());

    // 1. 创建两个 timerfd
    // 一次性定时器,基于单调时钟
    tfd_one_shot = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
    if (tfd_one_shot == -1) {
        perror("timerfd_create one-shot");
        exit(EXIT_FAILURE);
    }
    printf("Created one-shot timerfd: %d\n", tfd_one_shot);

    // 周期性定时器,基于单调时钟,非阻塞
    tfd_periodic = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
    if (tfd_periodic == -1) {
        perror("timerfd_create periodic");
        close(tfd_one_shot);
        exit(EXIT_FAILURE);
    }
    printf("Created periodic timerfd: %d (non-blocking)\n", tfd_periodic);

    // 2. 设置一次性定时器:3秒后到期
    one_shot_time.it_value.tv_sec = 3;     // 3 秒后
    one_shot_time.it_value.tv_nsec = 0;
    one_shot_time.it_interval.tv_sec = 0;  // 一次性,不重复
    one_shot_time.it_interval.tv_nsec = 0;
    if (timerfd_settime(tfd_one_shot, 0, &one_shot_time, NULL) == -1) {
        perror("timerfd_settime one-shot");
        close(tfd_one_shot);
        close(tfd_periodic);
        exit(EXIT_FAILURE);
    }
    printf("Set one-shot timer to expire in 3 seconds.\n");

    // 3. 设置周期性定时器:立即启动,每 2 秒重复一次
    periodic_time.it_value.tv_sec = 0;     // 立即启动
    periodic_time.it_value.tv_nsec = 1;    // 纳秒设为1,确保立即触发第一次
    periodic_time.it_interval.tv_sec = 2;  // 每 2 秒重复
    periodic_time.it_interval.tv_nsec = 0;
    if (timerfd_settime(tfd_periodic, 0, &periodic_time, NULL) == -1) {
        perror("timerfd_settime periodic");
        close(tfd_one_shot);
        close(tfd_periodic);
        exit(EXIT_FAILURE);
    }
    printf("Set periodic timer to start immediately and repeat every 2 seconds.\n");

    // 4. 查询并打印初始定时器状态
    struct itimerspec curr_time;
    if (timerfd_gettime(tfd_one_shot, &curr_time) == 0) {
        print_itimerspec("One-shot timer initial state", &curr_time);
    }
    if (timerfd_gettime(tfd_periodic, &curr_time) == 0) {
        print_itimerspec("Periodic timer initial state", &curr_time);
    }

    // 5. 设置 poll 的文件描述符数组
    fds[0].fd = tfd_one_shot;
    fds[0].events = POLLIN;
    fds[1].fd = tfd_periodic;
    fds[1].events = POLLIN;
    fds[2].fd = STDIN_FILENO;
    fds[2].events = POLLIN;

    printf("\nEntering main loop. Waiting for timers or input (type 'quit' to exit)...\n");

    // 6. 主循环:使用 poll 等待事件
    int one_shot_done = 0;
    while (!one_shot_done) { // 循环直到一次性定时器完成
        int poll_num = poll(fds, 3, -1); // -1 表示无限期等待
        if (poll_num == -1) {
            if (errno == EINTR) {
                continue; // poll 被信号中断,继续等待
            } else {
                perror("poll");
                break;
            }
        }

        if (poll_num > 0) {
            // 检查一次性定时器
            if (fds[0].revents & POLLIN) {
                s = read(tfd_one_shot, &expirations, sizeof(expirations));
                if (s == sizeof(expirations)) {
                    printf("\n[ONE-SHOT TIMER] Expired! Expirations counted: %" PRIu64 "\n", expirations);
                    one_shot_done = 1; // 设置标志退出循环

                    // 再次查询状态,确认已停止
                    if (timerfd_gettime(tfd_one_shot, &curr_time) == 0) {
                        print_itimerspec("One-shot timer final state", &curr_time);
                    }
                } else {
                    perror("read one-shot timerfd");
                }
            }

            // 检查周期性定时器
            if (fds[1].revents & POLLIN) {
                s = read(tfd_periodic, &expirations, sizeof(expirations));
                if (s == sizeof(expirations)) {
                    printf("\n[PERIODIC TIMER] Expired! Expirations counted: %" PRIu64 "\n", expirations);
                    // 可以在这里处理周期性任务
                } else if (s == -1) {
                    if (errno != EAGAIN && errno != EWOULDBLOCK) {
                         perror("read periodic timerfd"); // 非 EAGAIN 的错误
                    } // EAGAIN/EWOULDBLOCK 是正常的,因为我们设置了 NONBLOCK
                }
            }

            // 检查标准输入
            if (fds[2].revents & POLLIN) {
                char buf[16];
                ssize_t nread = read(STDIN_FILENO, buf, sizeof(buf) - 1);
                if (nread > 0) {
                    buf[nread] = '\0';
                    printf("Read from stdin: %s", buf);
                    if (strncmp(buf, "quit\n", 5) == 0) {
                        printf("Exiting due to 'quit' command.\n");
                        break;
                    }
                } else if (nread == 0) {
                    printf("EOF on stdin (Ctrl+D). Exiting.\n");
                    break;
                }
            }
        }
    }

    // 7. 清理资源
    printf("\nClosing timerfds...\n");
    close(tfd_one_shot);
    close(tfd_periodic);
    printf("Program finished.\n");

    return 0;
}

9. 编译和运行

# 假设代码保存在 timerfd_example.c 中
gcc -o timerfd_example timerfd_example.c

# 运行程序
./timerfd_example
# 程序会启动定时器,并在终端输出信息。
# 等待 3 秒后一次性定时器触发。
# 每 2 秒周期性定时器会触发。
# 输入 'quit' 并回车可以退出。

10. 预期输出

--- Demonstrating timerfd ---
PID: 12345
Created one-shot timerfd: 3
Created periodic timerfd: 4 (non-blocking)
Set one-shot timer to expire in 3 seconds.
Set periodic timer to start immediately and repeat every 2 seconds.
One-shot timer initial state: Next expiration in 2.999999000 seconds
     Interval: 0.000000000 seconds
Periodic timer initial state: Next expiration in 0.000000001 seconds
     Interval: 2.000000000 seconds

Entering main loop. Waiting for timers or input (type 'quit' to exit)...

[PERIODIC TIMER] Expired! Expirations counted: 1

[PERIODIC TIMER] Expired! Expirations counted: 1

[ONE-SHOT TIMER] Expired! Expirations counted: 1
One-shot timer final state: Timer is STOPPED

[PERIODIC TIMER] Expired! Expirations counted: 1
...
# (继续每2秒打印,直到输入 'quit')
Read from stdin: quit
Exiting due to 'quit' command.

Closing timerfds...
Program finished.

11. 总结

timerfd 提供了一种现代化、同步化、且易于与事件驱动模型集成的定时器机制。

  • 核心优势:将定时事件转换为文件描述符的可读事件,可以方便地融入 poll/select/epoll 等 I/O 多路复用框架。
  • 避免信号:解决了传统信号定时器的异步处理复杂性和竞态条件问题。
  • 灵活配置:通过 timerfd_settime 可以轻松创建一次性或周期性定时器,并支持相对和绝对时间。
  • 信息丰富read() 返回的到期次数 (uint64_t) 可以帮助处理定时器“堆积”(例如程序忙于处理其他任务导致多次到期)的情况。

掌握 timerfd 对于编写高性能、事件驱动的 Linux 应用程序(尤其是服务器)非常有帮助。

https://app-blog.csdn.net/csdn/aiChatNew

1. 函数介绍

2. 函数原型

3. 功能

4. 参数详解

timerfd_create(int clockid, int flags)

timerfd_gettime(int fd, struct itimerspec *curr_value)

timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value)

struct itimerspec 结构体

5. 如何使用定时器文件描述符

6. 错误码 (errno)

7. 相似函数或关联函数

8. 示例代码

9. 编译和运行

10. 预期输出

11. 总结

Markdown 9106 字数 374 行数 当前行 1, 当前列 85

HTML 8433 字数 295 段落

保存草稿 发布文章

发表在 linux文章 | 留下评论

times系统调用及示例

times 函数详解

1. 函数介绍

times 是POSIX标准函数,用于获取进程及其子进程的CPU时间统计信息。它返回自系统启动以来进程在用户态和内核态消耗的CPU时间,以及所有已终止子进程的CPU时间统计。这对于性能分析、资源监控和系统管理非常有用。

2. 函数原型

#include <sys/times.h>
clock_t times(struct tms *buf);

3. 功能

times 函数获取当前进程的CPU时间使用情况,包括用户态CPU时间、系统态CPU时间,以及所有已终止子进程的相应时间。这些信息对于进程性能分析和资源管理至关重要。

4. 参数

  • *struct tms buf: 指向tms结构体的指针,用于存储时间统计信息

5. 返回值

  • 成功: 返回自系统启动以来的滴答数(ticks)
  • 失败: 返回(clock_t)-1,并设置errno

6. 相似函数,或关联函数

  • clock: 获取进程CPU时间
  • getrusage: 获取资源使用情况
  • time/clock_gettime: 获取系统时间
  • wait/waitpid: 等待子进程并获取其资源使用情况

7. 示例代码

示例1:基础times使用

#include <sys/times.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>

/**
 * 显示CPU时间统计
 */
void show_cpu_times(const struct tms *tms_buf, clock_t ticks) {
    long clk_tck = sysconf(_SC_CLK_TCK);  // 获取每秒滴答数
    
    printf("CPU时间统计:\n");
    printf("  用户态时间: %jd 滴答 (%.3f 秒)\n", 
           (intmax_t)tms_buf->tms_utime, 
           (double)tms_buf->tms_utime / clk_tck);
    printf("  系统态时间: %jd 滴答 (%.3f 秒)\n", 
           (intmax_t)tms_buf->tms_stime, 
           (double)tms_buf->tms_stime / clk_tck);
    printf("  子进程用户态时间: %jd 滴答 (%.3f 秒)\n", 
           (intmax_t)tms_buf->tms_cutime, 
           (double)tms_buf->tms_cutime / clk_tck);
    printf("  子进程系统态时间: %jd 滴答 (%.3f 秒)\n", 
           (intmax_t)tms_buf->tms_cstime, 
           (double)tms_buf->tms_cstime / clk_tck);
    printf("  总滴答数: %jd\n", (intmax_t)ticks);
    printf("  系统滴答率: %ld 滴答/秒\n", clk_tck);
}

/**
 * 演示基础times使用方法
 */
int demo_times_basic() {
    struct tms tms_start, tms_end;
    clock_t start_ticks, end_ticks;
    long clk_tck;
    
    printf("=== 基础times使用示例 ===\n");
    
    // 获取系统滴答率
    clk_tck = sysconf(_SC_CLK_TCK);
    if (clk_tck == -1) {
        perror("获取系统滴答率失败");
        return -1;
    }
    
    printf("系统滴答率: %ld 滴答/秒\n", clk_tck);
    
    // 获取初始时间统计
    start_ticks = times(&tms_start);
    if (start_ticks == (clock_t)-1) {
        perror("获取初始时间统计失败");
        return -1;
    }
    
    printf("\n1. 初始时间统计:\n");
    show_cpu_times(&tms_start, start_ticks);
    
    // 执行一些CPU密集型操作
    printf("\n2. 执行CPU密集型操作...\n");
    
    volatile long sum = 0;
    for (long i = 0; i < 100000000; i++) {
        sum += i * i;
        if (i % 20000000 == 0) {
            printf("  进度: %ld%%\n", i / 1000000);
        }
    }
    
    printf("  计算结果: %ld\n", sum);
    
    // 获取结束时间统计
    end_ticks = times(&tms_end);
    if (end_ticks == (clock_t)-1) {
        perror("获取结束时间统计失败");
        return -1;
    }
    
    printf("\n3. 结束时间统计:\n");
    show_cpu_times(&tms_end, end_ticks);
    
    // 计算时间差
    printf("\n4. 时间差统计:\n");
    clock_t ticks_diff = end_ticks - start_ticks;
    clock_t utime_diff = tms_end.tms_utime - tms_start.tms_utime;
    clock_t stime_diff = tms_end.tms_stime - tms_start.tms_stime;
    
    printf("  消耗滴答数: %jd\n", (intmax_t)ticks_diff);
    printf("  用户态时间差: %jd 滴答 (%.3f 秒)\n", 
           (intmax_t)utime_diff, (double)utime_diff / clk_tck);
    printf("  系统态时间差: %jd 滴答 (%.3f 秒)\n", 
           (intmax_t)stime_diff, (double)stime_diff / clk_tck);
    printf("  总CPU时间: %.3f 秒\n", 
           (double)(utime_diff + stime_diff) / clk_tck);
    
    // 计算CPU使用率
    if (ticks_diff > 0) {
        double cpu_utilization = (double)(utime_diff + stime_diff) / ticks_diff * 100;
        printf("  CPU使用率: %.2f%%\n", cpu_utilization);
    }
    
    return 0;
}

int main() {
    return demo_times_basic();
}

示例2:父子进程CPU时间统计

#include <sys/times.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <time.h>

/**
 * 子进程工作函数
 */
void child_work(int child_id, int work_duration) {
    printf("子进程 %d (PID: %d) 启动\n", child_id, getpid());
    
    struct tms tms_start, tms_current;
    clock_t start_ticks, current_ticks;
    long clk_tck = sysconf(_SC_CLK_TCK);
    
    start_ticks = times(&tms_start);
    
    // 执行工作
    volatile long sum = 0;
    time_t start_time = time(NULL);
    
    while (difftime(time(NULL), start_time) < work_duration) {
        // CPU密集型工作
        for (int i = 0; i < 1000000; i++) {
            sum += i;
        }
        
        // 定期报告进度
        if (difftime(time(NULL), start_time) > 0 && 
            fmod(difftime(time(NULL), start_time), 1.0) < 0.1) {
            current_ticks = times(&tms_current);
            if (current_ticks != (clock_t)-1) {
                clock_t utime_diff = tms_current.tms_utime - tms_start.tms_utime;
                printf("  子进程 %d: 用户态时间 %.3f 秒\n", 
                       child_id, (double)utime_diff / clk_tck);
            }
        }
    }
    
    printf("子进程 %d 完成,计算结果: %ld\n", child_id, sum);
    exit(child_id);
}

/**
 * 演示父子进程CPU时间统计
 */
int demo_parent_child_times() {
    struct tms tms_before, tms_after;
    clock_t ticks_before, ticks_after;
    pid_t children[3];
    int child_count = 3;
    long clk_tck = sysconf(_SC_CLK_TCK);
    
    printf("=== 父子进程CPU时间统计演示 ===\n");
    printf("系统滴答率: %ld 滴答/秒\n", clk_tck);
    
    // 获取父进程初始时间统计
    ticks_before = times(&tms_before);
    if (ticks_before == (clock_t)-1) {
        perror("获取初始时间统计失败");
        return -1;
    }
    
    printf("\n1. 父进程初始时间统计:\n");
    printf("  父进程用户态时间: %.3f 秒\n", 
           (double)tms_before.tms_utime / clk_tck);
    printf("  父进程系统态时间: %.3f 秒\n", 
           (double)tms_before.tms_stime / clk_tck);
    printf("  子进程累计时间: %.3f 秒 (用户态) + %.3f 秒 (系统态)\n",
           (double)tms_before.tms_cutime / clk_tck,
           (double)tms_before.tms_cstime / clk_tck);
    
    // 创建子进程
    printf("\n2. 创建子进程:\n");
    for (int i = 0; i < child_count; i++) {
        children[i] = fork();
        if (children[i] == 0) {
            // 子进程
            child_work(i + 1, 3 + i);  // 不同持续时间
        } else if (children[i] > 0) {
            printf("  创建子进程 %d: PID=%d\n", i + 1, children[i]);
        } else {
            perror("创建子进程失败");
            // 清理已创建的子进程
            for (int j = 0; j < i; j++) {
                kill(children[j], SIGKILL);
            }
            return -1;
        }
    }
    
    // 父进程等待子进程完成
    printf("\n3. 父进程等待子进程完成:\n");
    int completed_children = 0;
    
    while (completed_children < child_count) {
        int status;
        pid_t finished_pid = wait(&status);
        if (finished_pid > 0) {
            completed_children++;
            printf("  子进程 %d (PID=%d) 已完成,退出状态: %d\n", 
                   WEXITSTATUS(status), finished_pid, WEXITSTATUS(status));
        } else if (finished_pid == -1) {
            if (errno != EINTR) {
                perror("等待子进程失败");
                break;
            }
        }
    }
    
    // 获取父进程结束时间统计
    ticks_after = times(&tms_after);
    if (ticks_after == (clock_t)-1) {
        perror("获取结束时间统计失败");
        return -1;
    }
    
    printf("\n4. 父进程结束时间统计:\n");
    printf("  父进程用户态时间: %.3f 秒\n", 
           (double)tms_after.tms_utime / clk_tck);
    printf("  父进程系统态时间: %.3f 秒\n", 
           (double)tms_after.tms_stime / clk_tck);
    printf("  子进程累计时间: %.3f 秒 (用户态) + %.3f 秒 (系统态)\n",
           (double)tms_after.tms_cutime / clk_tck,
           (double)tms_after.tms_cstime / clk_tck);
    
    // 计算差异
    printf("\n5. 父子进程时间差异分析:\n");
    clock_t parent_utime_diff = tms_after.tms_utime - tms_before.tms_utime;
    clock_t parent_stime_diff = tms_after.tms_stime - tms_before.tms_stime;
    clock_t children_utime_diff = tms_after.tms_cutime - tms_before.tms_cutime;
    clock_t children_stime_diff = tms_after.tms_cstime - tms_before.tms_cstime;
    
    printf("  父进程CPU时间: %.3f 秒 (用户态) + %.3f 秒 (系统态) = %.3f 秒\n",
           (double)parent_utime_diff / clk_tck,
           (double)parent_stime_diff / clk_tck,
           (double)(parent_utime_diff + parent_stime_diff) / clk_tck);
    
    printf("  子进程CPU时间: %.3f 秒 (用户态) + %.3f 秒 (系统态) = %.3f 秒\n",
           (double)children_utime_diff / clk_tck,
           (double)children_stime_diff / clk_tck,
           (double)(children_utime_diff + children_stime_diff) / clk_tck);
    
    printf("  总CPU时间: %.3f 秒\n",
           (double)(parent_utime_diff + parent_stime_diff + 
                   children_utime_diff + children_stime_diff) / clk_tck);
    
    // 总体统计
    clock_t total_ticks = ticks_after - ticks_before;
    if (total_ticks > 0) {
        double total_cpu_time = (double)(parent_utime_diff + parent_stime_diff + 
                                       children_utime_diff + children_stime_diff) / clk_tck;
        double wall_clock_time = (double)total_ticks / clk_tck;
        double cpu_utilization = total_cpu_time / wall_clock_time * 100;
        
        printf("\n6. 总体性能统计:\n");
        printf("  墙钟时间: %.3f 秒\n", wall_clock_time);
        printf("  CPU时间: %.3f 秒\n", total_cpu_time);
        printf("  CPU利用率: %.2f%%\n", cpu_utilization);
        printf("  并行效率: %.2f%%\n", cpu_utilization / child_count);
    }
    
    return 0;
}

int main() {
    return demo_parent_child_times();
}

示例3:性能分析工具

#include <sys/times.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <time.h>
#include <math.h>

/**
 * 性能分析结果结构
 */
typedef struct {
    clock_t start_ticks;
    clock_t end_ticks;
    struct tms start_tms;
    struct tms end_tms;
    double wall_time_seconds;
    double cpu_time_seconds;
    double user_time_seconds;
    double system_time_seconds;
    double children_user_time_seconds;
    double children_system_time_seconds;
    double cpu_utilization_percent;
} performance_analysis_t;

/**
 * 初始化性能分析
 */
int init_performance_analysis(performance_analysis_t *analysis) {
    analysis->start_ticks = times(&analysis->start_tms);
    if (analysis->start_ticks == (clock_t)-1) {
        perror("初始化性能分析失败");
        return -1;
    }
    
    return 0;
}

/**
 * 完成性能分析
 */
int finish_performance_analysis(performance_analysis_t *analysis) {
    long clk_tck = sysconf(_SC_CLK_TCK);
    
    analysis->end_ticks = times(&analysis->end_tms);
    if (analysis->end_ticks == (clock_t)-1) {
        perror("完成性能分析失败");
        return -1;
    }
    
    // 计算各种时间统计
    clock_t ticks_diff = analysis->end_ticks - analysis->start_ticks;
    clock_t utime_diff = analysis->end_tms.tms_utime - analysis->start_tms.tms_utime;
    clock_t stime_diff = analysis->end_tms.tms_stime - analysis->start_tms.tms_stime;
    clock_t cutime_diff = analysis->end_tms.tms_cutime - analysis->start_tms.tms_cutime;
    clock_t cstime_diff = analysis->end_tms.tms_cstime - analysis->start_tms.tms_cstime;
    
    analysis->wall_time_seconds = (double)ticks_diff / clk_tck;
    analysis->user_time_seconds = (double)utime_diff / clk_tck;
    analysis->system_time_seconds = (double)stime_diff / clk_tck;
    analysis->children_user_time_seconds = (double)cutime_diff / clk_tck;
    analysis->children_system_time_seconds = (double)cstime_diff / clk_tck;
    analysis->cpu_time_seconds = analysis->user_time_seconds + analysis->system_time_seconds +
                                 analysis->children_user_time_seconds + analysis->children_system_time_seconds;
    
    if (ticks_diff > 0) {
        analysis->cpu_utilization_percent = (analysis->cpu_time_seconds / 
                                           analysis->wall_time_seconds) * 100;
    } else {
        analysis->cpu_utilization_percent = 0.0;
    }
    
    return 0;
}

/**
 * 显示性能分析结果
 */
void show_performance_analysis(const performance_analysis_t *analysis) {
    printf("=== 性能分析报告 ===\n");
    printf("时间统计:\n");
    printf("  墙钟时间: %.3f 秒\n", analysis->wall_time_seconds);
    printf("  用户态时间: %.3f 秒\n", analysis->user_time_seconds);
    printf("  系统态时间: %.3f 秒\n", analysis->system_time_seconds);
    printf("  子进程用户态时间: %.3f 秒\n", analysis->children_user_time_seconds);
    printf("  子进程系统态时间: %.3f 秒\n", analysis->children_system_time_seconds);
    printf("  总CPU时间: %.3f 秒\n", analysis->cpu_time_seconds);
    printf("  CPU利用率: %.2f%%\n", analysis->cpu_utilization_percent);
    
    // 详细分解
    printf("\n详细分解:\n");
    if (analysis->wall_time_seconds > 0) {
        printf("  用户态占比: %.1f%%\n", 
               (analysis->user_time_seconds / analysis->wall_time_seconds) * 100);
        printf("  系统态占比: %.1f%%\n", 
               (analysis->system_time_seconds / analysis->wall_time_seconds) * 100);
        printf("  子进程用户态占比: %.1f%%\n", 
               (analysis->children_user_time_seconds / analysis->wall_time_seconds) * 100);
        printf("  子进程系统态占比: %.1f%%\n", 
               (analysis->children_system_time_seconds / analysis->wall_time_seconds) * 100);
    }
}

/**
 * CPU密集型测试函数
 */
void cpu_intensive_test(const char *test_name, int iterations) {
    printf("执行CPU密集型测试: %s (%d 次迭代)\n", test_name, iterations);
    
    volatile double result = 0.0;
    for (int i = 0; i < iterations; i++) {
        // 执行数学计算
        for (int j = 1; j <= 1000; j++) {
            result += sin(j * 0.001) * cos(j * 0.001);
        }
        
        // 定期显示进度
        if (i > 0 && i % (iterations / 10) == 0) {
            printf("  %s 进度: %d%%\n", test_name, (i * 100) / iterations);
        }
    }
    
    printf("  %s 完成,结果: %.6f\n", test_name, result);
}

/**
 * I/O密集型测试函数
 */
void io_intensive_test(const char *test_name, int file_count) {
    printf("执行I/O密集型测试: %s (%d 个文件)\n", test_name, file_count);
    
    for (int i = 0; i < file_count; i++) {
        char filename[64];
        snprintf(filename, sizeof(filename), "/tmp/io_test_%s_%d.tmp", test_name, i);
        
        // 创建并写入文件
        FILE *fp = fopen(filename, "w");
        if (fp) {
            for (int j = 0; j < 1000; j++) {
                fprintf(fp, "测试数据行 %d.%d\n", i, j);
            }
            fclose(fp);
        }
        
        // 读取文件
        fp = fopen(filename, "r");
        if (fp) {
            char buffer[256];
            while (fgets(buffer, sizeof(buffer), fp)) {
                // 简单处理读取的数据
                volatile int dummy = strlen(buffer);
            }
            fclose(fp);
        }
        
        // 删除临时文件
        unlink(filename);
        
        if (i > 0 && i % (file_count / 10) == 0) {
            printf("  %s 进度: %d%%\n", test_name, (i * 100) / file_count);
        }
    }
    
    printf("  %s 完成\n", test_name);
}

/**
 * 演示性能分析工具
 */
int demo_performance_analyzer() {
    performance_analysis_t analysis;
    long clk_tck = sysconf(_SC_CLK_TCK);
    
    printf("=== 性能分析工具演示 ===\n");
    printf("系统滴答率: %ld 滴答/秒\n", clk_tck);
    
    // 初始化性能分析
    if (init_performance_analysis(&analysis) != 0) {
        return -1;
    }
    
    printf("\n开始性能测试...\n");
    
    // 测试1: CPU密集型操作
    printf("\n1. CPU密集型测试:\n");
    cpu_intensive_test("CPU测试1", 5000);
    
    // 测试2: I/O密集型操作
    printf("\n2. I/O密集型测试:\n");
    io_intensive_test("I/O测试1", 50);
    
    // 测试3: 混合操作
    printf("\n3. 混合操作测试:\n");
    for (int i = 0; i < 5; i++) {
        cpu_intensive_test("混合CPU测试", 1000);
        io_intensive_test("混合I/O测试", 10);
    }
    
    // 完成性能分析
    if (finish_performance_analysis(&analysis) != 0) {
        return -1;
    }
    
    // 显示分析结果
    printf("\n");
    show_performance_analysis(&analysis);
    
    // 详细性能建议
    printf("\n=== 性能优化建议 ===\n");
    
    if (analysis.cpu_utilization_percent > 80) {
        printf("📌 CPU使用率很高 (%.1f%%),可能存在CPU瓶颈\n", 
               analysis.cpu_utilization_percent);
        printf("   建议:\n");
        printf("   - 考虑算法优化\n");
        printf("   - 检查是否存在无限循环\n");
        printf("   - 考虑并行处理\n");
    } else if (analysis.cpu_utilization_percent > 50) {
        printf("ℹ CPU使用率中等 (%.1f%%)\n", analysis.cpu_utilization_percent);
        printf("   建议:\n");
        printf("   - 监控CPU使用趋势\n");
        printf("   - 优化热点代码\n");
    } else {
        printf("✓ CPU使用率正常 (%.1f%%)\n", analysis.cpu_utilization_percent);
    }
    
    // I/O性能分析
    double io_ratio = (analysis.children_user_time_seconds + 
                      analysis.children_system_time_seconds) / 
                     analysis.wall_time_seconds;
    if (io_ratio > 0.3) {
        printf("\n📌 I/O操作占比较高 (%.1f%%)\n", io_ratio * 100);
        printf("   建议:\n");
        printf("   - 考虑异步I/O操作\n");
        printf("   - 优化文件访问模式\n");
        printf("   - 使用缓冲减少I/O次数\n");
    }
    
    // 并行效率分析
    if (analysis.children_user_time_seconds + analysis.children_system_time_seconds > 0) {
        printf("\n📊 子进程性能分析:\n");
        printf("   子进程总CPU时间: %.3f 秒\n", 
               analysis.children_user_time_seconds + analysis.children_system_time_seconds);
        printf("   平均每个子进程CPU时间: %.3f 秒\n",
               (analysis.children_user_time_seconds + analysis.children_system_time_seconds) / 2);
    }
    
    return 0;
}

int main() {
    return demo_performance_analyzer();
}

示例4:实时CPU监控

#include <sys/times.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <time.h>
#include <signal.h>

/**
 * CPU监控数据结构
 */
typedef struct {
    time_t timestamp;
    clock_t ticks;
    struct tms tms_data;
    double cpu_usage_percent;
    double user_cpu_percent;
    double system_cpu_percent;
    double children_cpu_percent;
} cpu_monitor_data_t;

/**
 * CPU监控器结构
 */
typedef struct {
    cpu_monitor_data_t history[100];  // 历史数据
    int history_count;
    int max_history;
    long clk_tck;
    volatile int monitoring;
    pid_t target_pid;  // 监控特定进程(0表示当前进程)
} cpu_monitor_t;

/**
 * 初始化CPU监控器
 */
int init_cpu_monitor(cpu_monitor_t *monitor, pid_t target_pid) {
    memset(monitor, 0, sizeof(cpu_monitor_t));
    monitor->max_history = 100;
    monitor->clk_tck = sysconf(_SC_CLK_TCK);
    monitor->target_pid = target_pid;
    monitor->monitoring = 1;
    
    if (monitor->clk_tck == -1) {
        perror("获取系统滴答率失败");
        return -1;
    }
    
    printf("CPU监控器初始化完成\n");
    printf("  监控目标: %s\n", target_pid ? "特定进程" : "当前进程");
    printf("  系统滴答率: %ld 滴答/秒\n", monitor->clk_tck);
    
    return 0;
}

/**
 * 收集CPU使用数据
 */
int collect_cpu_data(cpu_monitor_t *monitor) {
    if (monitor->history_count >= monitor->max_history) {
        // 循环覆盖旧数据
        memmove(&monitor->history[0], &monitor->history[1], 
                sizeof(cpu_monitor_data_t) * (monitor->max_history - 1));
        monitor->history_count = monitor->max_history - 1;
    }
    
    cpu_monitor_data_t *current = &monitor->history[monitor->history_count];
    
    current->timestamp = time(NULL);
    current->ticks = times(&current->tms_data);
    
    if (current->ticks == (clock_t)-1) {
        perror("获取CPU时间失败");
        return -1;
    }
    
    // 计算CPU使用率(与前一个采样点比较)
    if (monitor->history_count > 0) {
        cpu_monitor_data_t *previous = &monitor->history[monitor->history_count - 1];
        clock_t ticks_diff = current->ticks - previous->ticks;
        
        if (ticks_diff > 0) {
            clock_t utime_diff = current->tms_data.tms_utime - previous->tms_data.tms_utime;
            clock_t stime_diff = current->tms_data.tms_stime - previous->tms_data.tms_stime;
            clock_t cutime_diff = current->tms_data.tms_cutime - previous->tms_data.tms_cutime;
            clock_t cstime_diff = current->tms_data.tms_cstime - previous->tms_data.tms_cstime;
            
            current->user_cpu_percent = (double)utime_diff / ticks_diff * 100;
            current->system_cpu_percent = (double)stime_diff / ticks_diff * 100;
            current->children_cpu_percent = (double)(cutime_diff + cstime_diff) / ticks_diff * 100;
            current->cpu_usage_percent = current->user_cpu_percent + 
                                        current->system_cpu_percent + 
                                        current->children_cpu_percent;
        } else {
            current->cpu_usage_percent = 0.0;
            current->user_cpu_percent = 0.0;
            current->system_cpu_percent = 0.0;
            current->children_cpu_percent = 0.0;
        }
    } else {
        // 第一次采样,无法计算使用率
        current->cpu_usage_percent = 0.0;
        current->user_cpu_percent = 0.0;
        current->system_cpu_percent = 0.0;
        current->children_cpu_percent = 0.0;
    }
    
    monitor->history_count++;
    return 0;
}

/**
 * 显示CPU监控数据
 */
void show_cpu_monitor_data(const cpu_monitor_t *monitor) {
    if (monitor->history_count == 0) {
        printf("暂无监控数据\n");
        return;
    }
    
    const cpu_monitor_data_t *latest = &monitor->history[monitor->history_count - 1];
    
    printf("=== CPU监控数据 ===\n");
    printf("采样时间: %s", ctime(&latest->timestamp));
    printf("CPU使用率: %.2f%%\n", latest->cpu_usage_percent);
    printf("  用户态: %.2f%%\n", latest->user_cpu_percent);
    printf("  系统态: %.2f%%\n", latest->system_cpu_percent);
    printf("  子进程: %.2f%%\n", latest->children_cpu_percent);
    
    // 显示详细时间信息
    printf("详细时间信息:\n");
    printf("  用户态时间: %.3f 秒\n", 
           (double)latest->tms_data.tms_utime / monitor->clk_tck);
    printf("  系统态时间: %.3f 秒\n", 
           (double)latest->tms_data.tms_stime / monitor->clk_tck);
    printf("  子进程用户态时间: %.3f 秒\n", 
           (double)latest->tms_data.tms_cutime / monitor->clk_tck);
    printf("  子进程系统态时间: %.3f 秒\n", 
           (double)latest->tms_data.tms_cstime / monitor->clk_tck);
}

/**
 * 显示历史趋势
 */
void show_cpu_trend(const cpu_monitor_t *monitor) {
    if (monitor->history_count < 2) {
        printf("数据不足,无法显示趋势\n");
        return;
    }
    
    printf("=== CPU使用率趋势 ===\n");
    printf("%-20s %-8s %-8s %-8s %-8s\n", 
           "时间", "总CPU%", "用户%", "系统%", "子进程%");
    printf("%-20s %-8s %-8s %-8s %-8s\n", 
           "----", "------", "----", "----", "-----");
    
    // 显示最近10个采样点
    int start_index = (monitor->history_count > 10) ? 
                     monitor->history_count - 10 : 0;
    
    for (int i = start_index; i < monitor->history_count; i++) {
        const cpu_monitor_data_t *data = &monitor->history[i];
        char time_str[20];
        strftime(time_str, sizeof(time_str), "%H:%M:%S", localtime(&data->timestamp));
        
        printf("%-20s %-8.1f %-8.1f %-8.1f %-8.1f\n",
               time_str,
               data->cpu_usage_percent,
               data->user_cpu_percent,
               data->system_cpu_percent,
               data->children_cpu_percent);
    }
}

/**
 * 模拟被监控的工作进程
 */
void work_process(int work_id) {
    printf("工作进程 %d (PID: %d) 启动\n", work_id, getpid());
    
    // 执行不同类型的工作
    for (int cycle = 0; cycle < 5; cycle++) {
        printf("工作进程 %d: 执行周期 %d\n", work_id, cycle + 1);
        
        // CPU密集型工作
        volatile long sum = 0;
        for (long i = 0; i < 50000000; i++) {
            sum += i;
        }
        printf("  CPU工作完成,结果: %ld\n", sum);
        
        // I/O工作
        char filename[64];
        snprintf(filename, sizeof(filename), "/tmp/work_%d_cycle_%d.tmp", work_id, cycle);
        FILE *fp = fopen(filename, "w");
        if (fp) {
            for (int i = 0; i < 10000; i++) {
                fprintf(fp, "工作数据 %d.%d\n", cycle, i);
            }
            fclose(fp);
            unlink(filename);
        }
        
        sleep(2);  // 休息一下
    }
    
    printf("工作进程 %d 完成\n", work_id);
    exit(work_id);
}

/**
 * 演示实时CPU监控
 */
int demo_real_time_cpu_monitoring() {
    cpu_monitor_t monitor;
    pid_t worker_pids[2];
    int worker_count = 2;
    
    printf("=== 实时CPU监控演示 ===\n");
    
    // 初始化监控器
    if (init_cpu_monitor(&monitor, 0) != 0) {
        return -1;
    }
    
    // 创建工作进程
    printf("创建 %d 个工作进程:\n", worker_count);
    for (int i = 0; i < worker_count; i++) {
        worker_pids[i] = fork();
        if (worker_pids[i] == 0) {
            work_process(i + 1);
        } else if (worker_pids[i] > 0) {
            printf("  工作进程 %d: PID=%d\n", i + 1, worker_pids[i]);
        } else {
            perror("创建工作进程失败");
            for (int j = 0; j < i; j++) {
                kill(worker_pids[j], SIGKILL);
            }
            return -1;
        }
    }
    
    // 开始监控
    printf("\n开始实时监控 (30秒):\n");
    time_t start_time = time(NULL);
    
    while (difftime(time(NULL), start_time) < 30) {
        // 收集CPU数据
        if (collect_cpu_data(&monitor) != 0) {
            printf("收集CPU数据失败\n");
            break;
        }
        
        // 每5秒显示一次详细信息
        if (((int)difftime(time(NULL), start_time)) % 5 == 0) {
            show_cpu_monitor_data(&monitor);
            
            // 每15秒显示一次趋势
            if (((int)difftime(time(NULL), start_time)) % 15 == 0) {
                show_cpu_trend(&monitor);
            }
            
            printf("---\n");
        }
        
        // 短暂休眠
        usleep(500000);  // 500ms
    }
    
    // 等待工作进程完成
    printf("等待工作进程完成:\n");
    for (int i = 0; i < worker_count; i++) {
        int status;
        pid_t finished_pid = waitpid(worker_pids[i], &status, 0);
        if (finished_pid > 0) {
            printf("  工作进程 %d (PID=%d) 已完成,退出状态: %d\n", 
                   WEXITSTATUS(status), finished_pid, WEXITSTATUS(status));
        }
    }
    
    // 显示最终监控结果
    printf("\n=== 最终监控结果 ===\n");
    
    // 收集最终数据
    collect_cpu_data(&monitor);
    show_cpu_monitor_data(&monitor);
    
    // 显示完整趋势
    printf("\n完整CPU使用率趋势:\n");
    show_cpu_trend(&monitor);
    
    // 统计分析
    if (monitor.history_count > 1) {
        double max_cpu = 0, min_cpu = 100, avg_cpu = 0;
        double total_cpu = 0;
        int valid_samples = 0;
        
        for (int i = 1; i < monitor.history_count; i++) {
            double cpu_usage = monitor.history[i].cpu_usage_percent;
            if (cpu_usage >= 0) {  // 有效数据
                if (cpu_usage > max_cpu) max_cpu = cpu_usage;
                if (cpu_usage < min_cpu) min_cpu = cpu_usage;
                total_cpu += cpu_usage;
                valid_samples++;
            }
        }
        
        if (valid_samples > 0) {
            avg_cpu = total_cpu / valid_samples;
            
            printf("\n=== 统计分析 ===\n");
            printf("CPU使用率统计:\n");
            printf("  最高使用率: %.2f%%\n", max_cpu);
            printf("  最低使用率: %.2f%%\n", min_cpu);
            printf("  平均使用率: %.2f%%\n", avg_cpu);
            
            if (avg_cpu > 80) {
                printf("  🚨 警告: 平均CPU使用率过高\n");
            } else if (avg_cpu > 60) {
                printf("  ⚠ 提醒: CPU使用率较高\n");
            } else {
                printf("  ✓ CPU使用率正常\n");
            }
        }
    }
    
    return 0;
}

int main() {
    return demo_real_time_cpu_monitoring();
}

示例5:进程资源使用统计

#include <sys/times.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <time.h>
#include <sys/resource.h>

/**
 * 进程资源使用统计结构
 */
typedef struct {
    pid_t pid;
    char process_name[256];
    struct tms start_tms;
    struct tms end_tms;
    clock_t start_ticks;
    clock_t end_ticks;
    time_t start_time;
    time_t end_time;
    
    // CPU时间统计
    double user_time_seconds;
    double system_time_seconds;
    double children_user_time_seconds;
    double children_system_time_seconds;
    double total_cpu_time_seconds;
    double wall_clock_time_seconds;
    double cpu_utilization_percent;
    
    // 系统资源统计
    long max_rss_kb;      // 最大驻留集大小
    long page_faults;     // 页面错误次数
    long voluntary_ctxt_switches;   // 自愿上下文切换
    long nonvoluntary_ctxt_switches; // 非自愿上下文切换
} process_resource_stats_t;

/**
 * 初始化进程资源统计
 */
int init_process_resource_stats(process_resource_stats_t *stats, pid_t target_pid) {
    memset(stats, 0, sizeof(process_resource_stats_t));
    
    stats->pid = target_pid ? target_pid : getpid();
    stats->start_time = time(NULL);
    stats->start_ticks = times(&stats->start_tms);
    
    if (stats->start_ticks == (clock_t)-1) {
        perror("初始化进程资源统计失败");
        return -1;
    }
    
    // 获取进程名称
    char comm_path[256];
    snprintf(comm_path, sizeof(comm_path), "/proc/%d/comm", stats->pid);
    
    FILE *fp = fopen(comm_path, "r");
    if (fp) {
        if (fgets(stats->process_name, sizeof(stats->process_name), fp)) {
            // 移除换行符
            char *newline = strchr(stats->process_name, '\n');
            if (newline) *newline = '\0';
        }
        fclose(fp);
    } else {
        snprintf(stats->process_name, sizeof(stats->process_name), "process_%d", stats->pid);
    }
    
    printf("进程资源统计初始化:\n");
    printf("  进程ID: %d\n", stats->pid);
    printf("  进程名称: %s\n", stats->process_name);
    printf("  启动时间: %s", ctime(&stats->start_time));
    
    return 0;
}

/**
 * 完成进程资源统计
 */
int finish_process_resource_stats(process_resource_stats_t *stats) {
    long clk_tck = sysconf(_SC_CLK_TCK);
    
    stats->end_time = time(NULL);
    stats->end_ticks = times(&stats->end_tms);
    
    if (stats->end_ticks == (clock_t)-1) {
        perror("完成进程资源统计失败");
        return -1;
    }
    
    // 计算CPU时间
    clock_t utime_diff = stats->end_tms.tms_utime - stats->start_tms.tms_utime;
    clock_t stime_diff = stats->end_tms.tms_stime - stats->start_tms.tms_stime;
    clock_t cutime_diff = stats->end_tms.tms_cutime - stats->start_tms.tms_cutime;
    clock_t cstime_diff = stats->end_tms.tms_cstime - stats->start_tms.tms_cstime;
    
    stats->user_time_seconds = (double)utime_diff / clk_tck;
    stats->system_time_seconds = (double)stime_diff / clk_tck;
    stats->children_user_time_seconds = (double)cutime_diff / clk_tck;
    stats->children_system_time_seconds = (double)cstime_diff / clk_tck;
    stats->total_cpu_time_seconds = stats->user_time_seconds + 
                                   stats->system_time_seconds +
                                   stats->children_user_time_seconds + 
                                   stats->children_system_time_seconds;
    stats->wall_clock_time_seconds = (double)(stats->end_ticks - stats->start_ticks) / clk_tck;
    
    if (stats->wall_clock_time_seconds > 0) {
        stats->cpu_utilization_percent = (stats->total_cpu_time_seconds / 
                                         stats->wall_clock_time_seconds) * 100;
    }
    
    // 获取系统资源使用情况
    struct rusage usage;
    if (getrusage(RUSAGE_SELF, &usage) == 0) {
        stats->max_rss_kb = usage.ru_maxrss;  // 在Linux上以KB为单位
        stats->page_faults = usage.ru_majflt;
        stats->voluntary_ctxt_switches = usage.ru_nvcsw;
        stats->nonvoluntary_ctxt_switches = usage.ru_nivcsw;
    }
    
    return 0;
}

/**
 * 显示进程资源统计
 */
void show_process_resource_stats(const process_resource_stats_t *stats) {
    printf("=== 进程资源使用统计 ===\n");
    printf("进程信息:\n");
    printf("  进程ID: %d\n", stats->pid);
    printf("  进程名称: %s\n", stats->process_name);
    printf("  运行时间: %s 到 %s", 
           ctime(&stats->start_time), ctime(&stats->end_time));
    
    printf("\nCPU时间统计:\n");
    printf("  用户态时间: %.3f 秒\n", stats->user_time_seconds);
    printf("  系统态时间: %.3f 秒\n", stats->system_time_seconds);
    printf("  子进程用户态时间: %.3f 秒\n", stats->children_user_time_seconds);
    printf("  子进程系统态时间: %.3f 秒\n", stats->children_system_time_seconds);
    printf("  总CPU时间: %.3f 秒\n", stats->total_cpu_time_seconds);
    printf("  墙钟时间: %.3f 秒\n", stats->wall_clock_time_seconds);
    printf("  CPU利用率: %.2f%%\n", stats->cpu_utilization_percent);
    
    printf("\n系统资源统计:\n");
    printf("  最大内存使用: %ld KB (%.2f MB)\n", 
           stats->max_rss_kb, stats->max_rss_kb / 1024.0);
    printf("  主要页面错误: %ld\n", stats->page_faults);
    printf("  自愿上下文切换: %ld\n", stats->voluntary_ctxt_switches);
    printf("  非自愿上下文切换: %ld\n", stats->nonvoluntary_ctxt_switches);
    printf("  总上下文切换: %ld\n", stats->voluntary_ctxt_switches + 
           stats->nonvoluntary_ctxt_switches);
}

/**
 * 模拟不同类型的工作负载
 */
void simulate_workload(int workload_type, int intensity) {
    switch (workload_type) {
        case 1:  // CPU密集型
            printf("执行CPU密集型工作 (强度: %d)\n", intensity);
            volatile long sum = 0;
            for (long i = 0; i < intensity * 10000000L; i++) {
                sum += i * i;
            }
            printf("  CPU工作完成,结果: %ld\n", sum);
            break;
            
        case 2:  // I/O密集型
            printf("执行I/O密集型工作 (强度: %d)\n", intensity);
            for (int i = 0; i < intensity * 100; i++) {
                char filename[64];
                snprintf(filename, sizeof(filename), "/tmp/io_work_%d_%d.tmp", getpid(), i);
                
                FILE *fp = fopen(filename, "w");
                if (fp) {
                    for (int j = 0; j < 1000; j++) {
                        fprintf(fp, "测试数据 %d.%d\n", i, j);
                    }
                    fclose(fp);
                    unlink(filename);
                }
            }
            printf("  I/O工作完成\n");
            break;
            
        case 3:  // 内存密集型
            printf("执行内存密集型工作 (强度: %d)\n", intensity);
            size_t alloc_size = intensity * 1024 * 1024;  // MB
            char *buffer = malloc(alloc_size);
            if (buffer) {
                // 填充内存
                memset(buffer, 0xAA, alloc_size);
                // 访问内存
                volatile long checksum = 0;
                for (size_t i = 0; i < alloc_size; i += 1024) {
                    checksum += buffer[i];
                }
                printf("  内存工作完成,校验和: %ld\n", checksum);
                free(buffer);
            } else {
                printf("  内存分配失败\n");
            }
            break;
            
        default:
            printf("未知工作负载类型: %d\n", workload_type);
            break;
    }
}

/**
 * 演示进程资源使用统计
 */
int demo_process_resource_statistics() {
    process_resource_stats_t stats;
    long clk_tck = sysconf(_SC_CLK_TCK);
    
    printf("=== 进程资源使用统计演示 ===\n");
    printf("系统滴答率: %ld 滴答/秒\n", clk_tck);
    
    // 初始化统计
    if (init_process_resource_stats(&stats, 0) != 0) {
        return -1;
    }
    
    printf("\n开始执行不同类型的工作负载:\n");
    
    // 工作负载1: CPU密集型
    printf("\n1. CPU密集型工作:\n");
    simulate_workload(1, 5);
    
    // 工作负载2: I/O密集型
    printf("\n2. I/O密集型工作:\n");
    simulate_workload(2, 3);
    
    // 工作负载3: 内存密集型
    printf("\n3. 内存密集型工作:\n");
    simulate_workload(3, 100);
    
    // 混合工作负载
    printf("\n4. 混合工作负载:\n");
    for (int i = 0; i < 3; i++) {
        simulate_workload(1, 2);  // 轻量CPU工作
        simulate_workload(2, 1);   // 轻量I/O工作
        sleep(1);
    }
    
    // 完成统计
    if (finish_process_resource_stats(&stats) != 0) {
        return -1;
    }
    
    // 显示统计结果
    printf("\n");
    show_process_resource_stats(&stats);
    
    // 详细分析
    printf("\n=== 详细分析 ===\n");
    
    // CPU使用分析
    printf("CPU使用分析:\n");
    if (stats.cpu_utilization_percent > 80) {
        printf("  🚨 CPU使用率极高 (%.1f%%)\n", stats.cpu_utilization_percent);
        printf("     建议检查是否存在无限循环或算法效率问题\n");
    } else if (stats.cpu_utilization_percent > 50) {
        printf("  ⚠ CPU使用率较高 (%.1f%%)\n", stats.cpu_utilization_percent);
        printf("     建议优化热点代码\n");
    } else {
        printf("  ✓ CPU使用率正常 (%.1f%%)\n", stats.cpu_utilization_percent);
    }
    
    // 内存使用分析
    printf("\n内存使用分析:\n");
    if (stats.max_rss_kb > 1024 * 1024) {  // 超过1GB
        printf("  🚨 内存使用量巨大: %.2f GB\n", stats.max_rss_kb / (1024.0 * 1024.0));
        printf("     建议检查是否存在内存泄漏\n");
    } else if (stats.max_rss_kb > 100 * 1024) {  // 超过100MB
        printf("  ℹ 内存使用量较大: %.2f MB\n", stats.max_rss_kb / 1024.0);
        printf("     建议监控内存增长趋势\n");
    } else {
        printf("  ✓ 内存使用量正常: %.2f MB\n", stats.max_rss_kb / 1024.0);
    }
    
    // I/O效率分析
    printf("\nI/O效率分析:\n");
    double io_efficiency = (double)stats.voluntary_ctxt_switches / 
                          (stats.voluntary_ctxt_switches + stats.nonvoluntary_ctxt_switches + 1);
    if (io_efficiency > 0.8) {
        printf("  ✓ I/O效率良好 (%.1f%% 自愿切换)\n", io_efficiency * 100);
        printf("     表明程序很好地配合了I/O操作\n");
    } else {
        printf("  ℹ I/O效率一般 (%.1f%% 自愿切换)\n", io_efficiency * 100);
        printf("     可以考虑优化阻塞I/O操作\n");
    }
    
    // 性能建议
    printf("\n=== 性能优化建议 ===\n");
    printf("1. CPU优化:\n");
    printf("   - 分析CPU密集型代码段\n");
    printf("   - 考虑并行处理或算法优化\n");
    printf("   - 使用性能分析工具定位热点\n");
    
    printf("\n2. 内存优化:\n");
    printf("   - 监控内存使用峰值\n");
    printf("   - 及时释放不用的内存\n");
    printf("   - 考虑内存池或对象复用\n");
    
    printf("\n3. I/O优化:\n");
    printf("   - 使用缓冲减少I/O次数\n");
    printf("   - 考虑异步I/O操作\n");
    printf("   - 优化文件访问模式\n");
    
    return 0;
}

int main() {
    return demo_process_resource_statistics();
}

times 使用注意事项

系统要求:

  1. POSIX兼容: 支持POSIX标准的系统
  2. 权限要求: 通常不需要特殊权限
  3. 架构支持: 支持所有主流架构

精度考虑:

  1. 滴答精度: 受系统滴答率限制(通常100Hz)
  2. 累积误差: 长时间监控可能累积误差
  3. 采样频率: 高频采样可能影响被监控进程

错误处理:

  1. 返回值检查: 必须检查返回值和errno
  2. 系统资源: 可能在资源不足时失败
  3. 进程状态: 目标进程终止时的行为

性能考虑:

  1. 系统调用开销: 频繁调用有性能开销
  2. 内存使用: 历史数据存储需要内存
  3. 监控开销: 监控本身会消耗少量CPU资源

安全考虑:

  1. 权限检查: 监控其他进程需要适当权限
  2. 资源限制: 避免过度消耗系统资源
  3. 数据保护: 监控数据的隐私和安全

最佳实践:

  1. 合理采样: 选择适当的采样间隔
  2. 错误处理: 妥善处理各种错误情况
  3. 资源管理: 及时清理监控资源
  4. 数据持久化: 重要监控数据应持久化存储
  5. 报警机制: 设置合理的报警阈值

times函数特点

优点:

  1. 标准兼容: POSIX标准函数,广泛支持
  2. 信息全面: 提供完整的CPU时间统计
  3. 性能良好: 系统调用开销小
  4. 易于使用: API简单直观

限制:

  1. 精度限制: 受系统滴答率限制
  2. 实时性: 不是实时监控的最佳选择
  3. 跨进程: 监控其他进程需要权限
  4. 历史数据: 只能获取累积统计,不能获取瞬时值

相关函数对比

times vs clock:

// times(): 获取进程CPU时间统计
struct tms tms_buf;
clock_t ticks = times(&tms_buf);

// clock(): 获取进程CPU时间
clock_t cpu_time = clock();

times vs getrusage:

// times(): 基础CPU时间统计
struct tms tms_buf;
times(&tms_buf);

// getrusage(): 详细的资源使用情况
struct rusage usage;
getrusage(RUSAGE_SELF, &usage);

常见使用场景

1. 性能分析:

// 分析程序CPU使用情况
struct tms start_tms, end_tms;
clock_t start_ticks = times(&start_tms);
// 执行程序
clock_t end_ticks = times(&end_tms);
// 计算CPU时间差

2. 资源监控:

// 监控进程资源使用
while (monitoring) {
    struct tms current_tms;
    clock_t current_ticks = times(&current_tms);
    // 分析CPU使用率
    sleep(1);
}

3. 系统管理:

// 系统负载监控
struct tms system_tms;
clock_t system_ticks = times(&system_tms);
// 分析系统整体性能

总结

times 函数是Linux系统中重要的进程资源监控工具,提供了:

  1. 全面的CPU时间统计: 包括用户态、系统态和子进程时间
  2. 标准兼容性: 符合POSIX标准,广泛支持
  3. 简单易用: API设计直观,易于集成
  4. 性能监控: 适用于各种性能分析场景

通过合理使用 times 函数,可以构建功能强大的性能监控和分析工具。在实际应用中,需要注意精度限制、错误处理和性能影响等问题,选择合适的监控策略和采样频率。

https://app-blog.csdn.net/csdn/aiChatNew

times 函数详解

1. 函数介绍

2. 函数原型

3. 功能

4. 参数

5. 返回值

6. 相似函数,或关联函数

7. 示例代码

示例1:基础times使用

示例2:父子进程CPU时间统计

示例3:性能分析工具

示例4:实时CPU监控

示例5:进程资源使用统计

times 使用注意事项

系统要求:

精度考虑:

错误处理:

性能考虑:

安全考虑:

最佳实践:

times函数特点

优点:

限制:

相关函数对比

times vs clock:

times vs getrusage:

常见使用场景

1. 性能分析:

2. 资源监控:

3. 系统管理:

总结

Markdown 27496 字数 1357 行数 当前行 1, 当前列 0

HTML 27105 字数 1120 段落

保存草稿 发布文章

发表在 linux文章 | 留下评论

tkill系统调用及示例

我们来深入学习 tkill 系统调用

1. 函数介绍

在 Linux 系统中,信号(Signal)是一种进程间通信(IPC)机制,允许一个进程(或内核)向另一个进程发送简短的异步通知。例如,当你在终端按下 Ctrl+C 时,系统会向当前前台进程发送 SIGINT 信号。

发送信号最常用的方法是 kill() 系统调用。kill(pid_t pid, int sig) 可以向指定的进程 ID (PID) 发送信号。

然而,在 Linux(特别是引入了线程之后),情况变得稍微复杂了一些。一个进程 (Process) 可以包含多个线程 (Threads)。这些线程共享同一个 PID,但每个线程都有自己的唯一线程 ID (TID)。在 Linux 内核的实现中,每个线程实际上都被视为一个独立的“任务”(task)。

这就引出了一个问题:如果我想向一个特定的线程发送信号,而不是整个进程,该怎么办?使用 kill() 只能指定 PID,内核会选择该进程中的某个线程来接收信号,但我们无法精确控制是哪一个。

tkill (Thread Kill) 系统调用就是为了解决这个问题而设计的。它的作用是向指定的线程 ID (TID) 发送信号

简单来说,tkill 就是 kill 的“精确打击”版本,它让你可以指定向哪个“小兵”(线程)开炮,而不是向整个“军队”(进程)开炮。

重要提示

  • tkill 是一个非常底层的系统调用。
  • 对于大多数应用程序开发,不推荐直接使用 tkill
  • POSIX 标准提供了更安全、更可移植的线程信号发送方法:pthread_kill()。这是你应该优先考虑使用的函数。
  • tkill 主要由线程库(如 NPTL)或高级调试/管理工具在内部使用。

2. 函数原型

// 标准 C 库通常不提供直接包装,需要通过 syscall 调用
#include <sys/syscall.h> // 包含系统调用号 SYS_tkill
#include <unistd.h>      // 包含 syscall 函数
#include <signal.h>      // 包含信号常量

long syscall(SYS_tkill, int tid, int sig);
// 注意:现代 glibc (2.30+) 提供了 tgkill,tkill 可能被弃用或不直接暴露

注意:用户空间标准 C 库通常不直接提供 tkill() 函数。你需要使用 syscall() 函数来直接调用系统调用号 SYS_tkill

3. 功能

向指定线程 ID (tid) 的线程发送指定的信号 (sig)。

4. 参数

  • tid:
    • int 类型。
    • 目标线程在内核中的唯一 ID (Thread ID)。这不是 POSIX 线程库 (pthread_t) 的 ID,而是内核级别的 TID。可以通过 syscall(SYS_gettid) 获取当前线程的 TID。
  • sig:
    • int 类型。
    • 要发送的信号编号,例如 SIGTERMSIGUSR1SIGSTOP 等。注意,SIGKILL 和 SIGSTOP 不能被捕获或忽略,但可以发送。

5. 返回值

  • 成功: 返回 0。
  • 失败: 返回 -1,并设置全局变量 errno 来指示具体的错误原因。

6. 错误码 (errno)

  • EINVALsig 参数无效(不是一个有效的信号号)。
  • EPERM: 调用者权限不足,无法向目标线程发送信号(例如,普通用户试图向 root 用户的线程发送信号)。
  • ESRCH: 找不到 tid 指定的线程。

7. 相似函数或关联函数

  • kill: 向指定进程 ID (PID) 发送信号。内核会选择进程中的一个线程来接收信号。
  • pthread_kill: POSIX 标准函数,用于向指定的 pthread_t 线程发送信号。这是用户空间多线程程序推荐使用的方法。
  • tgkill: 一个更安全的系统调用,用于向指定线程组 ID 和线程 ID 的线程发送信号 (tgkill(tgid, tid, sig))。它提供了额外的检查以避免向错误的线程发送信号。pthread_kill 在底层通常会调用 tgkill
  • gettid: 获取当前线程的内核 TID。需要通过 syscall(SYS_gettid) 调用。
  • pthread_self: 获取当前线程的 POSIX 线程 ID (pthread_t)。

8. 为什么 tkill 不推荐直接使用?

  1. 易错性:直接使用内核 TID (tid) 容易出错。如果 tid 恰好与系统中另一个不相关的进程的 PID 相同,tkill 可能会错误地向那个进程发送信号。
  2. 竞态条件:线程 ID 是可复用的。一个线程退出后,它的 TID 可能会被分配给一个新创建的、完全不相关的线程。如果在 ID 复用之间调用 tkill,可能会发送给错误的线程。
  3. 可移植性tkill 是 Linux 特有的系统调用。使用 POSIX 标准的 pthread_kill 可以让你的代码更容易移植到其他支持 POSIX 线程的系统上。
  4. 更好的抽象pthread_kill 工作在 POSIX 线程模型层面,更符合应用程序员的思维。

9. 示例代码

由于直接使用 tkill 比较危险且不推荐,下面的示例将演示:

  1. 如何获取线程的内核 TID。
  2. 如何(不推荐地)使用 tkill
  3. 如何(推荐地)使用 pthread_kill

警告:此示例用于教学目的,展示 tkill 的用法。在实际编程中,请使用 pthread_kill

#define _GNU_SOURCE // 启用 GNU 扩展以使用 syscall(SYS_gettid)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>   // POSIX 线程
#include <signal.h>    // 信号
#include <sys/syscall.h> // syscall, SYS_tkill, SYS_gettid
#include <errno.h>
#include <string.h>

// 全局变量用于线程间通信
volatile sig_atomic_t signal_received = 0;

// 信号处理函数
void signal_handler(int sig) {
    printf("Thread (TID %ld) received signal %d\n", syscall(SYS_gettid), sig);
    signal_received = 1;
}

// 线程函数
void* worker_thread(void *arg) {
    long thread_num = (long)arg;
    pid_t my_tid = syscall(SYS_gettid); // 获取内核 TID

    printf("Worker thread %ld started. Kernel TID: %d\n", thread_num, my_tid);

    // 设置信号处理掩码,解除对 SIGUSR1 的阻塞
    // (假设主线程已经阻塞了它)
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGUSR1);
    pthread_sigmask(SIG_UNBLOCK, &set, NULL);

    // 等待信号
    while (!signal_received) {
        printf("Worker thread %ld (TID %d) waiting for signal...\n", thread_num, my_tid);
        sleep(1);
    }

    printf("Worker thread %ld (TID %d) finishing.\n", thread_num, my_tid);
    return NULL;
}

int main() {
    pthread_t thread1, thread2;
    pid_t main_tid = syscall(SYS_gettid); // 获取主线程的 TID
    struct sigaction sa;

    printf("--- Demonstrating tkill vs pthread_kill ---\n");
    printf("Main thread PID: %d, Kernel TID: %d\n", getpid(), main_tid);

    // 1. 设置信号处理函数 (主线程)
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = signal_handler;
    sigemptyset(&sa.sa_mask);
    if (sigaction(SIGUSR1, &sa, NULL) == -1) {
        perror("sigaction");
        exit(EXIT_FAILURE);
    }

    // 2. 阻塞 SIGUSR1 在主线程,稍后在线程中解除阻塞
    sigset_t block_set;
    sigemptyset(&block_set);
    sigaddset(&block_set, SIGUSR1);
    sigprocmask(SIG_BLOCK, &block_set, NULL);

    // 3. 创建工作线程
    if (pthread_create(&thread1, NULL, worker_thread, (void*)1) != 0 ||
        pthread_create(&thread2, NULL, worker_thread, (void*)2) != 0) {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }

    sleep(2); // 等待线程启动并打印 TID

    // --- 方法 1: 使用不推荐的 tkill ---
    printf("\n--- Using tkill (NOT RECOMMENDED) ---\n");
    // 假设我们 somehow 知道了 thread1 的内核 TID (这在现实中很难安全地做到)
    // 这里我们简化处理,直接使用
    pid_t worker1_tid = syscall(SYS_gettid); // 这是错误的!这是主线程的 TID
    // 为了演示,我们假设我们有一个正确的方法获取了 worker1 的 TID
    // 比如通过全局变量或线程自己报告
    // 让我们让 worker_thread 报告它的 TID
    // (在实际代码中,你需要一个安全的机制在线程间传递 TID)
    // 这里我们伪造一个值来演示,实际使用非常危险
    // pid_t fake_worker_tid = 99999; // 假设的 TID
    // printf("Attempting to send SIGUSR1 to worker thread using tkill(TID=%d)...\n", fake_worker_tid);
    // long result = syscall(SYS_tkill, fake_worker_tid, SIGUSR1);
    // if (result == -1) {
    //     perror("tkill");
    // } else {
    //     printf("tkill succeeded.\n");
    // }

    // 由于获取其他线程 TID 的安全方法复杂,我们跳过直接 tkill 演示
    // 而是重点演示推荐的方法

    // --- 方法 2: 使用推荐的 pthread_kill ---
    printf("\n--- Using pthread_kill (RECOMMENDED) ---\n");
    printf("Sending SIGUSR1 to worker thread 1 using pthread_kill()...\n");
    if (pthread_kill(thread1, SIGUSR1) != 0) {
        perror("pthread_kill thread1");
    } else {
        printf("pthread_kill(thread1, SIGUSR1) succeeded.\n");
    }

    sleep(2); // 等待线程处理信号

    printf("Sending SIGUSR1 to worker thread 2 using pthread_kill()...\n");
    if (pthread_kill(thread2, SIGUSR1) != 0) {
        perror("pthread_kill thread2");
    } else {
        printf("pthread_kill(thread2, SIGUSR1) succeeded.\n");
    }

    // 5. 等待线程结束
    if (pthread_join(thread1, NULL) != 0 ||
        pthread_join(thread2, NULL) != 0) {
        perror("pthread_join");
    }

    printf("\nMain thread finished.\n");
    return 0;
}

改进版示例:安全地在线程间传递 TID 并演示 tkill(仅供学习)

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <sys/syscall.h>
#include <errno.h>
#include <stdatomic.h>

// 使用原子变量安全地在线程间共享 TID
_Atomic pid_t worker_tid = 0;

volatile sig_atomic_t worker_signal_received = 0;

void worker_signal_handler(int sig) {
    pid_t tid = syscall(SYS_gettid);
    printf("Worker (TID %d) received signal %d\n", tid, sig);
    worker_signal_received = 1;
}

void* worker_thread_v2(void *arg) {
    pid_t my_tid = syscall(SYS_gettid);
    printf("Worker thread started. Kernel TID: %d\n", my_tid);

    // 安全地将 TID 报告给主线程
    atomic_store(&worker_tid, my_tid);

    // 设置信号处理
    struct sigaction sa;
    sa.sa_handler = worker_signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    if (sigaction(SIGUSR2, &sa, NULL) == -1) { // 使用不同的信号以区分
        perror("sigaction worker");
        return NULL;
    }

    while (!worker_signal_received) {
        sleep(1);
    }
    printf("Worker (TID %d) exiting.\n", my_tid);
    return NULL;
}

int main() {
    pthread_t thread;
    pid_t main_tid = syscall(SYS_gettid);

    printf("--- Demonstrating tkill (Learning Purpose Only) ---\n");
    printf("Main thread PID: %d, Kernel TID: %d\n", getpid(), main_tid);

    if (pthread_create(&thread, NULL, worker_thread_v2, NULL) != 0) {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }

    // 等待 worker 线程报告其 TID
    pid_t target_tid;
    while ((target_tid = atomic_load(&worker_tid)) == 0) {
        usleep(100000); // 0.1 秒
    }
    printf("Main thread got worker TID: %d\n", target_tid);

    sleep(1); // 确保 worker 设置了信号处理函数

    // --- 现在演示 tkill (仅供学习,实际不推荐) ---
    printf("\n--- Using tkill (FOR LEARNING ONLY) ---\n");
    printf("Sending SIGUSR2 to worker thread (TID %d) using tkill()...\n", target_tid);
    long result = syscall(SYS_tkill, target_tid, SIGUSR2);
    if (result == -1) {
        perror("tkill");
        printf("This might fail due to permissions or the TID being invalid/reused.\n");
    } else {
        printf("tkill() returned 0 (success).\n");
    }

    pthread_join(thread, NULL);
    printf("Main thread finished.\n");
    return 0;
}

10. 编译和运行

# 假设代码保存在 tkill_example.c 中
# 需要链接 pthread 库
gcc -o tkill_example tkill_example.c -lpthread

# 运行程序
./tkill_example

11. 预期输出 (第二个示例)

--- Demonstrating tkill (Learning Purpose Only) ---
Main thread PID: 12345, Kernel TID: 12345
Worker thread started. Kernel TID: 12346
Main thread got worker TID: 12346

--- Using tkill (FOR LEARNING ONLY) ---
Sending SIGUSR2 to worker thread (TID 12346) using tkill()...
Worker (TID 12346) received signal 12
tkill() returned 0 (success).
Worker (TID 12346) exiting.
Main thread finished.

12. 总结

tkill 是一个底层的 Linux 系统调用,用于向指定的内核线程 ID (TID) 发送信号。

  • 优点:提供了向特定线程发送信号的能力。
  • 缺点/风险
    • 易于误用,可能导致向错误的进程/线程发送信号。
    • 存在竞态条件(TID 复用)。
    • 不是 POSIX 标准,可移植性差。
  • 推荐做法:在用户空间多线程程序中,应始终优先使用标准的 pthread_kill() 函数来向特定线程发送信号。它更安全、更可靠、更具可移植性。
  • tkill 的用途:主要供系统级程序、线程库实现或调试工具内部使用。

对于 Linux 编程新手,请记住:能用 pthread_kill 就别用 tkill

https://app-blog.csdn.net/csdn/aiChatNew

1. 函数介绍

2. 函数原型

3. 功能

4. 参数

5. 返回值

6. 错误码 (errno)

7. 相似函数或关联函数

8. 为什么 tkill 不推荐直接使用?

9. 示例代码

10. 编译和运行

11. 预期输出 (第二个示例)

12. 总结

Markdown 7782 字数 338 行数 当前行 1, 当前列 20 文章已保存22:13:08

HTML 7390 字数 253 段落

保存草稿 发布文章

发表在 linux文章 | 留下评论