正确使用 realloc 方法

Publish: September 5, 2016 Category: C/C++ No Comments

1. realloc第一种行为引发的bug

void *ptr = realloc(ptr, new_size);
if (!ptr) {
    错误处理
}

这里就引出了一个内存泄露的问题,当realloc分配失败的时候,会返回NULL。但是参数中的ptr的内存是没有被释放的。如果直接将realloc的返回值赋给ptr。那么当申请内存失败时,就会造成ptr原来指向的内存丢失,造成泄露。


2. 正确的处理应该是这样

void *new_ptr = realloc(ptr, new_size);
if (!new_ptr) {
    错误处理。
}
ptr = new_ptr


C 遍历目录文件

Publish: September 2, 2016 Category: C/C++ No Comments

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>


int trave_dir(char *path)
{
    DIR *pdir;
    struct dirent *pent;
    struct stat sb; 
    if ((pdir = opendir(path)) == NULL) {
        return -1; 
    }   
    
    while ((pent = readdir(pdir)) != NULL) {
        // 去掉(. .. 隐藏文件)
        if (strncmp(pent->d_name, ".", 1) == 0) continue;
    
        char new_path[BUF_SIZE] = {0};
        snprintf(new_path, sizeof(new_path), "%s/%s", path, pent->d_name);
        printf("%s\n", new_path);
        if (stat(new_path, &sb) >= 0 && S_ISDIR(sb.st_mode)) {
            trave_dir(new_path);
        }   
    }   
    closedir(pdir);
    return 0;
}


int main(int argc, char **argv)
{
    trave_dir(argv[1]);

    return 0;
}


编译运行:

# gcc -g -o test test.c
# ./test .


Linux 解决调用函数超时方法

Publish: August 2, 2016 Category: C/C++ No Comments

当我们需要一个别人写的一个函数(可能是库里的),但是该函数可以会执行很长的时间,我们需要对其做超时控制,最简单的方法是使用alarm + signal的处理方式,但是在信号的处理函数中能做的比较少,只能做一些简单的处理,这里提供另外一种方法:

原理:使用线程的 pthread_cond_timedwait() 方法,主线程创建子线程并使用 pthread_cond_timedwait()方法等待,子线程里调用可能超时的函数:

#include <time.h>
#include <pthread.h>
#include <stdio.h>
/* for ETIMEDOUT */
#include <errno.h>
#include <string.h>
pthread_mutex_t calculating = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t done = PTHREAD_COND_INITIALIZER;
void *expensive_call(void *data)
{
        int oldtype;
        /* allow the thread to be killed at any time */
        pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);
        /* ... calculations and expensive io here, for example:
         * infinitely loop
         */
        int i = 0;
        for (;;) {
            printf("%d\n", i); 
            sleep(1);
            i++;
        }   
        /* wake up the caller if we've completed in time */
        pthread_cond_signal(&done);
        return NULL;
}

/* note: this is not thread safe as it uses a global condition/mutex */
int do_or_timeout(struct timespec *max_wait)
{
        struct timespec abs_time;
        pthread_t tid;
        int err;
        pthread_mutex_lock(&calculating);
        /* pthread cond_timedwait expects an absolute time to wait until */
        clock_gettime(CLOCK_REALTIME, &abs_time);
        abs_time.tv_sec += max_wait->tv_sec;
        abs_time.tv_nsec += max_wait->tv_nsec;
        pthread_create(&tid, NULL, expensive_call, NULL);
        /* pthread_cond_timedwait can return spuriously: this should
         * be in a loop for production code
         */
        err = pthread_cond_timedwait(&done, &calculating, &abs_time);
        if (err == ETIMEDOUT)
                fprintf(stderr, "%s: calculation timed out\n", __func__);
        if (!err)
                pthread_mutex_unlock(&calculating);
        return err;
}

int main()
{
        struct timespec max_wait;
        memset(&max_wait, 0, sizeof(max_wait));
        /* wait at most 2 seconds */
        max_wait.tv_sec = 5;
        do_or_timeout(&max_wait);
        printf("Finish\n");
        return 0;
}


编译与执行:

$ gcc -g -o test test.c -pthread -lrt
$ ./test


Linux网络编程---IO超时控制

Publish: July 28, 2016 Category: C/C++ No Comments

   对于网络IO,我们一般情况下都需要超时机制来避免进行操作的进程被handle住,经典的做法就是采用select+非阻塞IO进行判断,select在超时时间内判断是否可以读写操作,然后采用非堵塞读写,不过一般实现的时候读操作不需要设置为非堵塞,读操作只有在没有数据的 时候才会阻塞,select的判断成功说明存在数据,所以即使是阻塞读在这种情况下也是可以做到非阻塞的效果,就没有必要设置成非阻塞的情况了.


下面是针对网络中FD的读、写超时的做法:

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


#ifndef O_NONBLOCK
#define O_NONBLOCK O_NDELAY
#endif

// 设为非阻塞
int ndelay_on(int fd)
{
    return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
}
// 设为阻塞
int ndelay_off(int fd)
{
    return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
}


// return:
// -1: system fail
// -2: timeout
// >0: read length
int read_nb_timeout(int fd, char *buf, int len, int timeout)
{
    fd_set rfds;
    struct timeval tv;
    tv.tv_sec = timeout;
    tv.tv_usec = 0;
    FD_ZERO(&rfds);
    FD_SET(fd, &rfds);
    if (select(fd + 1, &rfds, (fd_set *) 0, (fd_set *) 0, &tv) == -1) {
        //  select fail
        printf("select fail:%s\n", strerror(errno));
        return -1;
    }
    if (FD_ISSET(fd, &rfds)) {
        return read(fd, buf, len);
    }
    printf("read data from remote host timeout[%d]\n", timeout);
    return -2;
}


// return:
// -1: system fail
// -2: timeout
// >0: written length
int write_nb_timeout(int fd, char *buf, int len, int timeout)
{
    if (ndelay_on(fd) == -1) {
        printf("set nonblock fail:%s\n", strerror(errno));
        return -1;
    }
    int wrote_len = 0;
    char *pbuf = buf;
    int nwrite = 0;
    int n = len;
    fd_set wfds;
    struct timeval tv;
    while (n > 0) {
        tv.tv_sec = timeout;
        tv.tv_usec = 0;
        FD_ZERO(&wfds);
        FD_SET(fd, &wfds);
        int ret = select(fd + 1, (fd_set *) 0, &wfds, (fd_set *) 0, &tv);
        if ( ret == -1 ) {
            //  select fail
            printf("select fail:%s\n", strerror(errno));
            ndelay_off(fd);
            return -1;
        } else if ( ret == 0 ) {
            //  timeout
            printf("write data to remote host timeout[%d]:%s\n", timeout, strerror(errno));
            ndelay_off(fd);
            return -2;
        } else {
            if (FD_ISSET(fd, &wfds)) {
                nwrite = write(fd, pbuf + wrote_len, n);
                printf("write len:%d/%d [%s]\n", nwrite, n, pbuf + wrote_len);
                if ( (nwrite == -1) && ((errno != EAGAIN) || (errno != EINTR)) ) {
                    printf("write to remote host fail:[%d]%s\n", errno, strerror(errno));
                    ndelay_off(fd);
                    return -1;
                } else if ( nwrite == 0 ) {
                    printf("remote host closed, but i don't send data [%d] to finished\n", n);
                    ndelay_off(fd);
                    return -1;
                } else {
                    wrote_len += nwrite;
                    n -= nwrite;
                }
            }
        }
    }
    ndelay_off(fd);
    return wrote_len;
}


// 测试从标准输入里读
int main(int argc, char **argv)
{
    char buf[1024] = {0};
    int r = 0;
    while (1) {
        r = read_nb_timeout(1, buf, sizeof(buf), 5);
        if (r <= 0) {
            time_t now;
            time(&now);
            struct tm *p = localtime(&now);
            printf("%d-%d-%d %d:%d:%d Error: read timeout\n", (1900+p->tm_year), (1+p->tm_mon), p->tm_mday, p->tm_hour, p->tm_min, p->tm_s
ec);
        } else {
            printf(">>> %s\n", buf);
            memset(buf, 0, sizeof(buf));
        }
    }
    return 0;
}


执行后,如果5秒内不写数据到标准输入则报错,写入后就再计算下个5秒

linux 系统服务启动脚本

Publish: July 26, 2016 Category: Shell No Comments

一个dt程序的服务启动脚本例子:

编辑脚本: /etc/init.d/dt

############################## dt SCRIPT BEGIN
#!/bin/sh
# dt
#
# processname: /usr/home/kyosold/dt
# pidfile:     /var/run/dt.pid

[ -f /etc/rc.d/init.d/functions ] && . /etc/rc.d/init.d/functions

PIDFILE=/var/run/dt.pid

RETVAL=0    #使用变量作为判断和关联上下文的载体

start() {
    echo -n $"Starting dt: "
    if [ ! -f $PIDFILE]; then
        /usr/home/songjian/dt >/dev/null 2>&1
        RETVAL=$?
        echo
        if [ $RETVAL -eq 0 ]; then
            action "启动 dt:" /bin/true
    
            # 从ps -ef进程列表中查询 dt 进程号,并写入 /var/run/dt.pid 文件中
            pid=$(ps -ef | grep dt | grep -v grep | awk '{print $2}')
            echo $pid > $PIDFILE
        else
            action "启动 dt:" /bin/false
        fi
    
        return $RETVAL
    else
        echo
        action "启动 dt:" /bin/false
        
        return 1
    fi
}

stop() {
    echo -n $"Stopping dt:"
    if [ -f $PIDFILE]; then
        kill `cat /var/run/dt.pid`
        RETVAL=$?
        echo
        if [ $RETVAL -eq 0 ]; then
            action "停止 dt:" /bin/true
    
            # 删除/var/run/dt.pid文件
            rm -f $PIDFILE
        else
            action "启动 dt:" /bin/false
        fi
    
        return $RETVAL
    else
        echo
        action "启动 dt:" /bin/false
        
        return 1
    fi
}

case "$1" in
    start)
        start
        ;;  
    stop)
        stop
        ;;  
    restart)
        sh $0 stop
        sleep 2
        sh $0 start
        ;;
    *)
        echo "Format error!"
        echo $"Usage: $0 {start|stop|restart|"
        exit 1
        ;;
esac
exit $RETVAL
############################## SCRIPT END

修改文件的权限:

chmod 755 /etc/init.d/dt


执行:

service dt start
service dt stop
service dt restart


注意

        由于使用该脚本后,程序默认路径为根(/)路径了,所以程序里有用到相对路径的话,需要做chdir,可以采用以下方法:

        由于service脚本启动命令是全路径的,所以使用dirname获取到目录,并chdir:

    char *dname = dirname(argv[0]);
    log_debug("get current path[%s]", dname);
    if (chdir(dname) == -1) {
        log_error("chdir path[%s] fail:[%d]%s", dname, errno, strerror(errno));
        exit(1);
    }