系统信息和系统资源
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 环境变量
添加环境变量
删除环境变量
在程序中获取进程变量
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