信号安全函数

进程捕获到信号并对其进行处理时,进程正在执行的正常指令序列就被信号处理程序临时中断,它首先执行该信号处理程序中的指令。如果从信号处理程序中返回,则继续执行在捕获到信号时进程正在执行的正常的指令序列。有如下三类函数不能在信号处理程序中调用(非信号安全函数)
a)已知它们使用静态数据结构(如getpwnam函数)
b)它们调用malloc或free函数
c)标准I/O函数,标准I/O库的很多实现都以不可重入的方式使用全局数据结构。这里需要特别说明一点,书上或网上一些例子,信号处理函数中调用了printf,这里仅仅为了直观说明程序的运行,printf不能在信号处理函数中调用。

度量C程序执行时间

Execution time of C program
项目里有一些调试日志,统计执行数据库插入等耗时操作的时间用于提升性能。统计时间程序真可谓是五花八门。我个人认为比较好的写法就是如下这种形式(跨平台):

clock_t begin = clock();

/* here, do your time-consuming job */

clock_t end = clock();
double time_spent = (double)(end - begin) / CLOCKS_PER_SEC;

pthread_cond_timedwait使用误区

上周定位一个同事转手过来的异常问题,说程序无法正常工作,非常奇怪。由于没有现场环境,只能去review代码,发现这个模块居然将gettimeofday和pthread_cond_timewait一起使用。gettimeofday的一般用法如下:

#include <time.h>
#include <sys/time.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char **argv)
{
  if (argc < 2)
    {
      printf("USAGE: %s loop-iterations\n", argv[0]);
      return 1;
    }

  int iterations = atoi(argv[1]);

  struct timeval start, end;

  gettimeofday(&start, NULL);

  for (int i = 0; i < iterations; i++)
  {
  }

  gettimeofday(&end, NULL);

  printf("%ld\n", ((end.tv_sec * 1000000 + end.tv_usec)
          - (start.tv_sec * 1000000 + start.tv_usec)));

  return 0;
}

gettimeofday与pthread_cond_timewait一起使用的方式:

void mywait(int timeInMs)
{
    struct timespec timeToWait;
    struct timeval now;
    int rt;

    gettimeofday(&now,NULL);

    timeToWait.tv_sec = now.tv_sec+5;
    timeToWait.tv_nsec = (now.tv_usec+1000UL*timeInMs)*1000UL;

    pthread_mutex_lock(&fakeMutex);
    rt = pthread_cond_timedwait(&fakeCond, &fakeMutex, &timeToWait);
    pthread_mutex_unlock(&fakeMutex);
    printf("\nDone\n");
}

但是上面这种写法存在一个问题:如果修改了系统时间会导致等待时间异常,正确的方法是使用相对时间,一个可以运行的例子如下:

// gcc -o pwait pwait.cpp -lpthread
#include <stdio.h>
#include <pthread.h>
#include <time.h>

typedef struct mutex_cond
{
    pthread_condattr_t cattr;
    pthread_mutex_t i_mutex;
    pthread_cond_t i_cv;
    void* i_sigevent;
}mutex_cond_t;
 
int main()
{
    mutex_cond_t mcond;
    int ret = pthread_condattr_init(&(mcond.cattr));
    if (ret != 0)
    {
        return (1);
    }
    mcond.i_sigevent = NULL;
    ret = pthread_mutex_init ( &(mcond.i_mutex), NULL);
    ret = pthread_condattr_setclock(&(mcond.cattr), CLOCK_MONOTONIC);
    ret = pthread_cond_init(&(mcond.i_cv), &(mcond.cattr));

    struct timespec tv;
    while(1)
    {
        clock_gettime(CLOCK_MONOTONIC, &tv);
    printf("%lld\n", (long long) tv.tv_sec);
    tv.tv_sec += 20;// 设置20秒后没收到事件超时返回
    ret = pthread_cond_timedwait(&(mcond.i_cv), &(mcond.i_mutex), &tv);
    }

    return 0;
}

这里函数pthread_condattr_setclock通过CLOCK_MONOTONIC设置成相对时间,不会依赖系统时间的变化,clock_gettime这里获取到的时间应该是系统启动时间,可以通过uptime验证。

[root c++]#./a.out
6700
[root c++]#uptime
 00:24:52 up  1:51,  1 user,  load average: 0.01, 0.02, 0.00

fopen函数

在项目代码里看到如下类似代码:

if ( access(filename, F_OK) != 0 )
{
    int fd = open(filename, O_CREAT|O_RDWR, 0666);
    if ( fd == -1 )
    {
        //error handle
        return -1;
    }
    close(fd);
    (void)chmod(filename, 0666);
}

这种写法存在问题,多个进程同时执行到access,如果文件不存在都会走到下面的逻辑,无法做到原子操作,APUE第三章里介绍了一种判断文件是否存在的原子操作:fopen中添加O_EXCL。
其实上面的代码可以改写成:

    int fd = open(filename, O_CREAT|O_RDWR|O_EXCL, 0666);
   //错误码不是文件不存在,错误处理  
   if (EEXIST != errno && fd == -1 )
    {
        //error handle
        return -1;
    }
    close(fd);
    (void)chmod(filename, 0666);

文件系统只读

开发虚拟机输入cp pc之后,记不住名字使用tab看下有哪些pc开头的文件,结果提示如下错误:
cp pc-bash: cannot create temp file for here-document: Read-only file system
使用mount看了下
/dev/sda1 on / type ext4 (ro,relatime,errors=remount-ro,data=ordered)
这里尝试使用:
mount -o remount,rw /dev/sdb1
仍然提示文件系统只读,使用fsck /dev/sda1可以修复磁盘

[root ~]#fsck /dev/sda1
fsck from util-linux 2.31.1
e2fsck 1.44.1 (24-Mar-2018)
/dev/sda1 contains a file system with errors, check forced.
Pass 1: Checking inodes, blocks, and sizes
Deleted inode 3538955 has zero dtime.  Fix<y>? yes
Inodes that were part of a corrupted orphan linked list found.  Fix<y>? yes
Inode 3538963 was part of the orphaned inode list.  FIXED.
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
Free blocks count wrong (12502342, counted=12538642).
Fix<y>? yes
Inode bitmap differences:  -3538955 -3538963
Fix<y>? yes
Free inodes count wrong for group #432 (5813, counted=5815).
Fix<y>? yes
Free inodes count wrong (3746983, counted=3747057).
Fix<y>? yes

/dev/sda1: ***** FILE SYSTEM WAS MODIFIED *****
/dev/sda1: ***** REBOOT SYSTEM *****
/dev/sda1: 447247/4194304 files (0.1% non-contiguous), 4238062/16776704 blocks

reboot后接着重新以读写方式挂载磁盘/dev/sda1即可修复该问题。