linux 应用开发笔记

系统信息和系统资源

1、系统信息函数

1. umane
获取当前操作系统内核名称和信息
操作系统:
主机名:
内核版本:
发行版本:
硬件构架:

在这里插入图片描述

2. sysinfo 系统统计信息
struct sysinfo {  
      long uptime;          /* 启动到现在经过的时间 */  
      unsigned long loads[3];    
      /* 1, 5, and 15 minute load averages */  
      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(long)-sizeof(int)];   
      /* libc5的补丁  
}; 
3. sysconf()

获取系统配置信息,页大小 主机名的长度、进程可打开的最大文件数,每个人用用户最大并发的进程数

时间

1. GMT 全球时间的中心点
UTC:比GMT时间精确,
date - u 查看 UTC时间
 在linux 系统中,时区的文件在。/usr/share/zoneinfo/目录下

在这里插入图片描述
系统的本地的时区由/etc/locatime,通常链接在/usr/share/zoneinfo/的某个文件下
在这里插入图片描述
修改本地时区信息,删除旧链接,新建链接
在这里插入图片描述

2.时间函数
time:s
gettimeofday:us
3. 时间转换函数
   ctime:本地时间    =====    将time_t 时间转换本地时间
   ctime_r:本地时间  =====    将time_t 时间转换本地时间
   localtime:本地时间 将time_t 、gettimeofday时间转换本地时间
   locattime_r:本地时间
   gmtime:是UTC时间
   mktime:和localtime 相反,将时间转为1997到现在总s数
   _r:指该函数可重入,推荐使用可重入   
   此处可能不准
   asctime:将 strcut tm分解时间转换为固定格式的字符
   strftime:可以根据自己的喜好转换为格式化字符串


   settimeofday  :设置本地时间

在这里插入图片描述

4.进程时间

用户CPU时间:进程在用户态运行的时间
系统CPU时间:进程在内核空间运行的时间
一般来说,进程时间 :等于这两者之和,也就是总的CPU时间

  times:获取当前进程时间
  clock:获得总的进程时间,但是想要或得秒数,记得除以CLOCKS_PER_SEG

在这里插入图片描述

5.内存分配
posix_memalign()、aligned_alloc()、memglign()、valloc()、pvalloc(),  
分配对齐内存

6.信号

信号是异步,软件模拟硬件中断,信号本质上是int类型的数字编号,是从1开始的
1. 可靠性信号和不可靠信号 、 实时信号和非实时信号
信号值小于34的是不可靠信号,
不可靠信号:进程对信号的错误处理以及信号丢失
可靠信号:34及之后的,支持排队、不会丢失,信号发送函数sigqueue(),信号绑定函数sigaction()。   
2.非实时信号:都是不可靠的信号,不支持排队
   实时信号支持排队,是可靠信号,
    实时信号是从时间关系上分类
使用 kill -l 查看

在这里插入图片描述

7. signal 函数

在这里插入图片描述

8. sigactiona()

在这里插入图片描述

  ***圈起来的这两个成员互斥***,
  			都是信号处理函数,sa_sigaction 提供更多参数,能获取更多信息,  
   			通过signal_t 获取的,
  sa_mask:在进程执行信号处理函数时,先将这组信号添加到进程的信号掩码字段,当进程执行处理完毕后,在恢复信号掩码,将这个信号掩码字段删除。
  sa_restorer:过时了
  sa_flags: 信号的处理过程:
    可以设置为不阻塞相同的信号,
    子进程不转为僵尸进程
    执行完信号处理函数,将信号改为默认处理函数等等

在这里插入图片描述
例子
在这里插入图片描述

9. kill 传递信号给其他进程

在这里插入图片描述
在这里插入图片描述

10. raise 信号可以传递给自身

在这里插入图片描述

11. 定时器 alarm 、pause

在这里插入图片描述
在这里插入图片描述

12. 信号集函数

在这里插入图片描述
sigismember : 测试信号是否在信号集中

每一信号都有相应的字符串描述信息,存在与sys_siglist,但是推荐使用 strsignal()函数
在这里插入图片描述
strsignal()
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

13 获取等待信号

sigismember
在这里插入图片描述

实时信号 是队列化管理

 某信号多次发送给另一个进程,则会多次传递,
 对于标准信号正在等待进程,即使多次发送,信号也传递一次
 信号编号越小,优先级越高

在这里插入图片描述
在这里插入图片描述

进程

每个进程都有自己的一组的环境变量,名称=值的集合,
env 可以查看 shell 环境变量
添加环境变量export
删除环境变量
在这里插入图片描述
在程序中获取进程变量
在这里插入图片描述

putenv 添加一个环境变量、setenv 向当前环境变量添加一个新的环境变量修  
改现有的环境变量的值,
推荐使用 setenv 
unsetenv 移除name = value,的value。
environ = NULL;移除所有环境变量

注意
在这里插入图片描述
正文段 初始化数据段 未初始化数据段
在这里插入图片描述

栈向下生长、推向上生长
vfork和fork的区别

在这里插入图片描述
execve函数和exec库
在这里插入图片描述

线程

线程tid,打印 %lu
在这里插入图片描述

         2、 等待指定线程退出,获取退出状态void *tret

在这里插入图片描述
注意!!!!!!!

主线程发送取消线程信号,但是副线程创建时设置为不可取消状态,
pthread_setcancelstate:能不能取消状态
pthread_setcanceltype:取消状态的处理,收到取消请求,会被挂起,直到可以取消,或者任意时间点取消

在这里插入图片描述
取消点
在这里插入图片描述
假设一个线程没有取消点,则可以通过pthread_testcancel 产生取消点
在这里插入图片描述

分离线程

pthread_detach
在这里插入图片描述

不可逆的:

在这里插入图片描述
可重入函数
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

初始化一次

pthread_once,对于重入函数来说,里面包含数据初始化的代码,只想执行一次,则通过该函数,但具体执行的是那个线程就由内核调度。

在这里插入图片描述
在这里插入图片描述

互斥锁只需要初始化一次,记得销毁互斥锁

在这里插入图片描述

尝试加锁

在这里插入图片描述

void *thread_func(void *arg)
{
    printf("thread_func %lu\n", pthread_self());
    int ret;
    int cycles = *(int *)arg;
    int l_count = 0;

    for (size_t i = 0; i < cycles; i++)
    {
        while (pthread_mutex_trylock(&mutex)) ; 
     /*如果尝试加锁,如果没有获取到锁,加锁返回0,  有锁非0值的错误码   因为失败的尝试可能会消耗大量的 CPU 
    时间 需要精确控制线程的等待和唤醒顺序,使用标准的pthread_mutex_lock更容易实现这种控制,  
    pthread_mutex_trylockpthread_mutex_trylock需要开发者显式处理失败的情况,这可能会导致代码更加复杂,容易出错*/
    
        // pthread_mutex_lock(&mutex);   
        /*给线程加锁 当无法获取锁时,线程会等待,不会浪费 CPU 时间,因此在等待锁的时候不会占用额外的系统资源。  
当无法获取锁时,会阻塞线程*/
        l_count = g_count;
        l_count++;
        g_count = l_count;
        // g_count++;
        pthread_mutex_unlock(&mutex);
    }
    // printf("thread_func %lu\n", pthread_self());
    return (void *)0;
}

如何避免死锁

最简单的方式就是定义互斥锁的层级关系,当多个线程对一组互斥锁操作时,总是应该按照相同的顺序对该组互斥锁进行锁定

互斥锁的四种类型

!!! 以下都是宏定义 大写字母,本人对大写不敏感

1、pthread_mutex_normal:标准的互斥锁,不做任何互斥锁的检查和死锁状态检测
2、pthread_mutex_errorcheck: 同一线程重复试图的二次加锁,
							 线程对其他线程的互斥锁进行解锁,
							 线程对未锁定的锁进行解锁
3、pthread_mutex_recursive:允许同一线程在互斥锁解锁前,进行多次加锁,并记录加锁次数,被称为递归互斥锁,  
所以加锁次数和解锁次数一定相同
4、pthread_mutex_default:提供互斥锁的默认的行为和属性,在arg为NULL时,或者使用宏定义初始化锁时,都是该类型,
保留锁的最大灵活性。

获取和修改互斥锁的属性

在这里插入图片描述

pthread_mutex_t mutex;
pthread_mutexattr_t attr;
/* 初始化互斥锁属性对象 */
pthread_mutexattr_init(&attr);
/* 将类型属性设置为 PTHREAD_MUTEX_NORMAL */
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
/* 初始化互斥锁 */
pthread_mutex_init(&mutex, &attr);
......
/* 使用完之后 */
pthread_mutexattr_destroy(&attr);
pthread_mutex_destroy(&mutex);
#include <stdio.h>
#include <pthread.h>

int main() {
    pthread_t tid[2];
    int ret;
    int type;
    pthread_mutexattr_t attr; // 声明互斥锁属性对象
    pthread_mutex_t mutex;    // 声明互斥锁

    // 初始化互斥锁属性对象
    pthread_mutexattr_init(&attr);
    // 设置互斥锁属性为递归锁类型
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);

    // 使用互斥锁属性对象初始化互斥锁
    pthread_mutex_init(&mutex, &attr);

    // 获取设置后的锁属性类型
    pthread_mutexattr_gettype(&attr, &type);
    printf("Lock type = %d\n", type); // 打印锁的属性类型

    // 销毁互斥锁和互斥锁属性对象
    pthread_mutex_destroy(&mutex);
    pthread_mutexattr_destroy(&attr);

    return 0;
}

结果:

root@answer:/home/linux/code/app/test# ./build/main.exe 
lock type = 1

条件变量(会阻塞线程)记得销毁

 条件变量直到条件满足,它和互斥锁搭配使用
对于初始化与销毁操作,有以下问题需要注意:
⚫ 在使用条件变量之前必须对条件变量进行初始化操作,使用 PTHREAD_COND_INITIALIZER 宏或
者函数 pthread_cond_init()都行;
⚫ 对已经初始化的条件变量再次进行初始化,将可能会导致未定义行为;
⚫ 对没有进行初始化的条件变量进行销毁,也将可能会导致未定义行为;
⚫ 对某个条件变量而言,仅当没有任何线程等待它时,将其销毁才是最安全的;
⚫ 经 pthread_cond_destroy()销毁的条件变量, 可以再次调用 pthread_cond_init()对其进行重新初始化。
 条件变量 主要是发送信号和等待
 等待:是等通知
 发送信号L:是共享变量发生改变,则发送信号,唤醒线程
函数 pthread_cond_signal()pthread_cond_broadcast()均可向指定的条件变量发送信号,通知一个或多
个处于等待状态的线程。调用 pthread_cond_wait()函数是线程阻塞,直到收到条件变量的通知

pthread_cond_signal()和 pthread_cond_broadcast()的区别在于:二者对阻塞于 pthread_cond_wait()的多个
线程对应的处理方式不同,
pthread_cond_signal()函数至少能唤醒一个线程,但函数 pthread_cond_signal()会更为高效,因为它只需确保至少唤醒一个线程即可,所以如果我们的程序当中,只有一个处于等待状态的线程,使用 pthread_cond_signal()更好

 而 pthread_cond_broadcast()函数则能唤醒所有线程。
 使用 pthread_cond_broadcast()函数总能产生正确的结果,唤醒所有等待状态的线程,

注意:

  //  !!!!!!! sleep(3);// 休眠是在解锁之后进行的,所以副线程可能在   
  休眠期间尝试获取锁,但由于主线程仍在休眠,无法释放锁,因此副线程无法获得执行机会
void pthread_cond_test()
{
    pthread_t tid;
    int ret;
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
    ret = pthread_create(&tid, NULL, thread_func, NULL);
    if (ret)
    {
        perror("pthread create failed");
    }
    while (1)
    {
        pthread_mutex_lock(&mutex);
        g_count++;
        //  !!!!!!! sleep(3);// 休眠是在解锁之后进行的,所以副线程可能在休眠期间尝试获取锁,但由于主线程仍在休眠,无法释放锁,因此副线程无法获得执行机会
        printf("Main: Added num, count = %d\n", g_count);
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&cond);
        sleep(3);
    }
    pthread_join(tid, NULL);
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
}
void *thread_func(void *arg)
{
#if 0
    printf("thread_func %lu\n", pthread_self());
    int ret;
    int cycles = *(int *)arg;
    int l_count = 0;

    for (size_t i = 0; i < cycles; i++)
    {
        while (pthread_mutex_trylock(&mutex))
            ; // 如果尝试加锁,如果没有获取到锁,加锁返回0,有锁非0值的错误码   因为失败的尝试可能会消耗大量的 CPU 时间 需要精确控制线程的等待和唤醒顺序,使用标准的pthread_mutex_lock更容易实现这种控制。pthread_mutex_trylockpthread_mutex_trylock需要开发者显式处理失败的情况,这可能会导致代码更加复杂,容易出错

        // pthread_mutex_lock(&mutex);   //给线程加锁 当无法获取锁时,线程会等待,不会浪费 CPU 时间,因此在等待锁的时候不会占用额外的系统资源。当无法获取锁时,会阻塞线程
        l_count = g_count;
        l_count++;
        g_count = l_count;
        // g_count++;
        pthread_mutex_unlock(&mutex);
    }
    // printf("thread_func %lu\n", pthread_self());
#endif
    // pthread_mutex_lock(&mutex);
    for (;;)
    {
        pthread_mutex_lock(&mutex);
        while (g_count == 0)
        {
            pthread_cond_wait(&cond, &mutex);
        }
        g_count--;
        printf("Thread: Remaining count = %d\n", g_count);
        pthread_mutex_unlock(&mutex);
    }
    // pthread_mutex_unlock(&mutex);

    return (void *)0;
}

结果:

Main: Added num, count = 1
Thread: Remaining count = 0
Main: Added num, count = 1
Thread: Remaining count = 0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值