精华内容
下载资源
问答
  • 2.调用单个文件处理函数,由输入的参数将全路径拼接,然后利用循环体,将linux命令结果输出至C语言函数。 编后记:在开发中遇到问题和想法,如果有导师,一定交流,可以指引自己少发牢骚!与其处于纠结,不如请教...
    人,一定要有勇气和踏实的态度面对改变和选择!开发,零经验起步!
    文件行数比对函数:
    输入参数:接口文件绝对路径,日期(格式YYYYMMDD:20150716)
    根据路径和日期,分别获取这add、del、upd这3个文件的行数,然后与周秀读取rec20150716.chk中文件的条数做对比。
    如果相同返回 RET_SUCCESS,如果不相同返回RET_FAIL,并打印出以下信息。
    任务分析:
    1.如果输入为目录路径,需要遍历读取目录下的每个文件,提取“日期”关键字打开并读取文件。考虑了链表、打开目录等函数、建立文件结构体、strcmp对比名称时利用输入参数作为变量···然而。。。真正的解决思路确实这样的:
    正是因为目录下文件名是很规范的 rec+20150915+num+add这样的形式,可以考虑
    1.目录下全部文件名读取,赋值给文件路径变量。
    2.调用单个文件处理函数,由输入的参数将全路径拼接,然后利用循环体,将linux命令结果输出至C语言函数。
    开发提示1:
        char FILE_PATH_1[200];
        memset(FILE_PATH_1, 0x00, sizeof(FILE_PATH_1));
        strcpy(FILE_PATH_1,"/settle/stl_cdr/inter123");//此目录是AIX小机上的一个目录
    开发提示2:
    (Check_LineCount(&FILE_PATH_1···//目录放到这个变量
    开发提示3:
     char add_file[200];
     sprintf(add_file, "%s/rec%s.num.add", FILE_PATH_1,filedate);//重点读一下,全路径拼接
        *add_chk=Get_File_Count(&add_file);
    开发提示4://求取目录下单独文件的行数 函数Get_File_Count
    int Get_File_Count(char *FILE_PATH_All)
    {   int  num_chk=0;
        char cmd[200];
        FILE *fp;
        sprintf(cmd, "cat %s | wc -l", FILE_PATH_All);
        if((fp = popen(cmd, "r")) == NULL)
            return -1;
        fscanf(fp, "%d", &num_chk);
        pclose(fp);//popen和pclose我并不了解,但是这样做就ok
    ···
    5.编写makefile文件,执行make工具
    文件行数比对函数:建立check_400_file.pc文件,编写Check_LineCount()函数,处理目录下所有文件的行数,输入参数为目录路径,通过sprintf()拼接全路径,调用Get_File_Count()处理单个文件行数,采用了"cat %s | wc -l"命令。获取后,主函数继续调用之前chk文件的函数,进行结果判断。
    测试结果:能够获取目录下包含特定日期关键字的文件,求取行数并输出至主函数。调用findcount函数也正常输出至主函数,进行判断。
    编后记:在开发中遇到问题和想法,如果有导师,一定交流下,可以指引自己少发牢骚!与其处于纠结,不如请教他人!
    谢谢关注!
    展开全文
  • 在一些程序中,我们需要每隔一段时间执行一个函数。例如每2s,5s,10s分别执行不同的函数。如果有多个定时器,实现这个功能就很简单,只需分别定时2s,5s,10s即可。但是Linux中只允许一个进程中有一个定时器,...

    在一些程序中,我们需要每隔一段时间执行一个函数。例如每2s5s10s分别执行不同的函数。如果有多个定时器,实现这个功能就很简单,只需分别定时2s5s10s即可。但是Linux中只允许一个进程中有一个定时器,怎么办呢?可以用以下的方法实现。

    首先使用setitimer函数注册一个1s定时器one_timer,因为1s可以作为被2s5s10s整除的单位时间。Setitimer定时时间到达以后会产生SGIALRM信号,设置SGIALRM的处理函数为multi_timer_manage,定时时间一到便会执行multi_timer_manage函数。

    定义一个timers结构体,存储两个变量。一个是定时时间,值为想要的时间除以单位时间;另一个是定时时间到时执行的函数。创建timers结构体two_timer, five_timer, ten_timer,并填写定时时间和需要执行的函数。

    multi_timer_manage为实现多个定时器功能的核心部分,在multi_timer_manage中分别将one_timer, two_timer, ten_timer对应的定时时间减一,若为零,则执行对应的函数,并将定时时间复位到初始值重新开始定时。这样,就实现了多个定时器的功能,详细代码如下:

     

    #include<stdio.h>

    #include<stdlib.h>

    #include<unistd.h>

    #include<sys/types.h>

    #include<sys/stat.h>

    #include<fcntl.h>

    #include<ctype.h>

    #include<string.h>

    #include<sys/time.h>

    #include<signal.h>

     

    struct itimerval one_timer;

    struct timers

    {

    int interval; //定时时间

    void(*handler)(); //处理函数

    };

    struct timers two_timer;

    struct timers five_timer;

    struct timers ten_timer;

     

    void multi_timer_manage()

    {

    printf("\n---");

    two_timer.interval--;

    if(two_timer.interval==0)

    {

    two_timer.interval=2;

    two_timer.handler();

    }

    five_timer.interval--;

    if(five_timer.interval==0)

    {

    five_timer.interval=5;

    five_timer.handler();

    }

    ten_timer.interval--;

    if(ten_timer.interval==0)

    {

    ten_timer.interval=10;

    ten_timer.handler();

    }

    }

     

    void two_output()

    {

    printf("2 ");

    }

     

    void five_output()

    {

    printf("5 ");

    }

     

    void ten_output()

    {

    printf("10 ");

    }

     

    void init()

    {

    one_timer.it_interval.tv_sec=1; //设置单位定时器定时时间

    one_timer.it_value.tv_sec=1; //设置单位定时器初始值

    setitimer(ITIMER_REAL,&one_timer,NULL); //初始化单位定时器

    signal(SIGALRM,multi_timer_manage); //指定单位定时器定时时间到时执行的函数

    two_timer.interval=2;

    two_timer.handler=two_output;

    five_timer.interval=5;

    five_timer.handler=five_output;

    ten_timer.interval=10;

    ten_timer.handler=ten_output;

    }

     

    int main()

    {

    init();

    while(1);

    }

     

    执行结果如下:

    ---

    ---2 

    ---

    ---2 

    ---5 

    ---2 

    ---

    ---2 

    ---

    ---2 5 10 

    ---

    ---2 

    ---

    ---2 

    ---5 

    ---2 

    ---

    ---2 

    ---

    ---2 5 10 

    ......

    可见,每秒都输出---,每隔2秒输出2,每隔5秒输出5,每隔10秒输出10

     

    展开全文
  • 第1章 字符测试函数 第2章 数据转换函数 第3章 内存配置函数 第4章 时间函数 第5章 字符串处理函数 第6章 数学计算函数 ...pdf文档,400页,函数大全,每个函数后面都有范例! 是学习linux程序的必备工具!
  • Linux下多个进程或线程同时对一个文件进行写操作和access函数 Linux下多个进程或线程同时对一个文件进行写操作,如何解决冲突? 使用flock(锁定文件或解除锁定),简单可行! 先介绍一下flock函数吧 头文件...
    Linux下多个进程或线程同时对一个文件进行写操作和access函数


    Linux下多个进程或线程同时对一个文件进行写操作,如何解决冲突?
    使用flock(锁定文件或解除锁定),简单可行!
    先介绍一下flock函数吧
    头文件 #include<sys/file.h>
    定义函数 int flock(int fd,int operation);
    函数说明 flock()会依参数operation所指定的方式对参数fd所指的文件做各种锁定或解除锁定的动作。此函数只能锁定整个文件,无法锁定文件的某一区域。
    参数
    operation有下列四种情况:
    LOCK_SH 建立共享锁定。多个进程可同时对同一个文件作共享锁定。
    LOCK_EX 建立互斥锁定。一个文件同时只有一个互斥锁定。
    LOCK_UN 解除文件锁定状态。
    LOCK_NB 无法建立锁定时,此操作可不被阻断,马上返回进程。通常与LOCK_SH或LOCK_EX 做OR(|)组合。
    单一文件无法同时建立共享锁定和互斥锁定,而当使用dup()或fork()时文件描述词不会继承此种锁定。
    返回值 返回0表示成功,若有错误则返回-1,错误代码存于errno。
    上代码!

    #include <sys/file.h> //flock header file
    #include <unistd.h> //ftruncate header file
    FILE *fin;
    //读写打开一个文本文件,允许读和写
    fin = fopen(szFile, "rt+");
    if (!fin)
    {
    cout << "Fail to open the file: " << szFile << endl;
    return;
    }
    //建立排他锁,阻塞方式
    if ( (flock(fileno(fin), LOCK_EX)) < 0 )
    {
    cout << "Fail to lock the file: " << szFile << endl;
    return;
    }
    本程序需要每次清空后重写
    ftruncate(fileno(fin), 0); //清空文件
    //写文件
    ......
    //解除锁
    if ( (flock(fileno(fin), LOCK_UN)) < 0 )
    {
    cout << "Fail to unlock the file: " << szFile << endl;
    return;
    }
    fclose(fin);




    linux C之access函数 



    access():判断是否具有存取文件的权限
    相关函数
        stat,open,chmod,chown,setuid,setgid
    表头文件
        #include<unistd.h>
    定义函数
        int access(const char * pathname, int mode);

    函数说明
        access()会检查是否可以读/写某一已存在的文件。参数mode有几种情况组合, R_OK,W_OK,X_OK 和F_OK。R_OK,W_OK与X_OK用来检查文件是否具有读取、写入和执行的权限。F_OK则是用来判断该文件是否存在。由于access()只作权限的核查,并不理会文件形态或文件内容,因此,如果一目录表示为“可写入”,表示可以在该目录中建立新文件等操作,而非意味此目录可以被当做文件处理。例如,你会发现DOS的文件都具有“可执行”权限,但用execve()执行时则会失败。
    返回值
        若所有欲查核的权限都通过了检查则返回0值,表示成功,只要有一权限被禁止则返回-1。
    错误代码
        EACCESS 参数pathname 所指定的文件不符合所要求测试的权限。
        EROFS 欲测试写入权限的文件存在于只读文件系统内。
        EFAULT 参数pathname指针超出可存取内存空间。
        EINVAL 参数mode 不正确。
        ENAMETOOLONG 参数pathname太长。
        ENOTDIR 参数pathname为一目录。
        ENOMEM 核心内存不足    
        ELOOP 参数pathname有过多符号连接问题。
        EIO I/O 存取错误。
    附加说明
        使用access()作用户认证方面的判断要特别小心,例如在access()后再做open()的空文件可能会造成系统安全上的问题。

    范例

    #include<unistd.h>
    int main()
    {
        if (access(“/etc/passwd”,R_OK) = =0)
            printf(“/etc/passwd can be read\n”);
    }
    执行
    /etc/passwd can be read 




    展开全文
  • 这对图形界面的程序尤其有意义,当一操作耗时很长时,整个系统都会等待这操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用线程技术,将耗时长的操作(time consuming)置于一新的线程,可以避免这种...

    转载自:https://blog.csdn.net/orzorz/article/details/5439780
    1、概述
    多线程程序作为一种多任务、并发的工作方式,有以下的优点:

    1. 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。
    2. 使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。
    3. 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。
      Linux下最常用的是遵循POSIX标准的pthread线程库。pthread的实现是通过系统调用clone()这一Linux特有的系统调用来实现。
      2、线程创建与结束
      1)pthread_t
      线程的标识符类型,pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义:
    typedef unsigned long int pthread_t;
    

    2)pthread_create
    thread_create用来创建一个线程,它的原型为:

    extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr,
    void *(*__start_routine) (void *), void *__arg));
    

    第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。这里,我们的 函数thread不需要参数,所以最后一个参数设为空指针。第二个参数我们也设为空指针,这样将生成默认属性的线程。对线程属性的设定和修改我们将在下一 节阐述。当创建线程成功时,函数返回0,若不为0则说明创建线程失败,常见的错误返回代码为EAGAIN和EINVAL。前者表示系统限制创建新的线程, 例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一 行代码。
    3)pthread_join
    函数pthread_join用来等待一个线程的结束。函数原型为:

    extern int pthread_join __P ((pthread_t __th, void **__thread_return));
    

    第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。
    4)pthread_exit
    线程除了正常执行结束外,还可以通过函数pthread_exit来结束它,pthread_exit的函数原型为:

    extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));
    

    唯一的参数是函数的返回代码,只要pthread_join中的第二个参数thread_return不是NULL,这个值将被传递给 thread_return。最后要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线 程则返回错误代码ESRCH。

    3 修改线程的属性
    属性结构为pthread_attr_t,在头文件 /usr/include/pthread.h中定义。属性值不能直接设置,须使用相关函数进行操作,初始化的函数为 pthread_attr_init,这个函数必须在pthread_create函数之前调用。属性对象主要包括是否绑定、是否分离、堆栈地址、堆栈大 小、优先级。默认的属性为非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。

    1. 关于线程的绑定
      线程的绑定,牵涉到另外一个概念:轻进程(LWP:Light Weight Process)。轻进程可以理解为内核线程,它位于用户层和系统层之间。系统对线程资源的分配、对线程的控制是通过轻进程来实现的,一个轻进程可以控制 一个或多个线程。默认状况下,启动多少轻进程、哪些轻进程来控制哪些线程是由系统来控制的,这种状况即称为非绑定的。绑定状况下,则顾名思义,即某个线程 固定的"绑"在一个轻进程之上。被绑定的线程具有较高的响应速度,这是因为CPU时间片的调度是面向轻进程的,绑定的线程可以保证在需要的时候它总有一个 轻进程可用。通过设置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足诸如实时反应之类的要求。
      设置线程绑定状态的函数为 pthread_attr_setscope,它有两个参数,第一个是指向属性结构的指针,第二个是绑定类型,它有两个取值: PTHREAD_SCOPE_SYSTEM(绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)。下面的代码即创建了一个绑定的线程。
    #include
    pthread_attr_t attr;
    pthread_t tid;
    
    /*初始化属性值,均设为默认值*/
    pthread_attr_init(&attr);
    pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
    
    pthread_create(&tid, &attr, (void *) my_function, NULL);
    

    2)关于线程的状态:分离态/非分离态
    线程的分离状态决定一个线程以什么样的方式来终止自己。在上面的例子中,我们采用了线程的默认属性,即为非分离状态,这种情况下,原有的线程等待创建 的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的 线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。设置线程分离状态的函数为

    pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)
    

    第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)。这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在 pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的 线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用 pthread_cond_timewait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是 在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。
    3)关于线程的优先级
    线程的优先级,它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数 pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。
    线程的优先级取值范围为-20~20,值越大,其优先级越低,缺省值为0,这个参数仅当调度策略为实时(即SCHED_RR或SCHED_FIFO)时才有效,在运行时通过pthread_setschedparam()函数来改变:
    下面即是一段简单的例子。

    #include
    #include
    pthread_attr_t attr;
    pthread_t tid;
    sched_param param;
    int newprio=20;
    
    pthread_attr_init(&attr);
    pthread_attr_getschedparam(&attr, ¶m);
    param.sched_priority=newprio;
    pthread_attr_setschedparam(&attr, ¶m);
    pthread_create(&tid, &attr, (void *)myfunction, myarg);
    

    4 线程的数据处理
    和进程相比,线程的最大优点之一是数据的共享性,各个进程共享父进程处沿袭的数据段,可以方便的获得、修改数据。但这也给多线程编程带来了许多问题。 我们必须当心有多个不同的进程访问相同的变量。许多函数是不可重入的,即同时不能运行一个函数的多个拷贝(除非使用不同的数据段)。在函数中声明的静态变 量常常带来问题,函数的返回值也会有问题。因为如果返回的是函数内部静态声明的空间的地址,则在一个线程调用该函数得到地址后使用该地址指向的数据时,别 的线程可能调用此函数并修改了这一段数据。在进程中共享的变量必须用关键字volatile来定义,这是为了防止编译器在优化时(如gcc中使用-OX参 数)改变它们的使用方式。为了保护变量,我们必须使用信号量、互斥等方法来保证我们对变量的正确使用。

    1. 线程数据
      在单线程的程序里,有两种基本的数据:全局变量和局部变量。但在多线程程序里,还有第三种数据类型:线程数据(TSD: Thread-Specific Data)。它和全局变量很象,在线程内部,各个函数可以象使用全局变量一样调用它,但它对线程外部的其它线程是不可见的。这种数据的必要性是显而易见 的。例如我们常见的变量errno,它返回标准的出错信息。它显然不能是一个局部变量,几乎每个函数都应该可以调用它;但它又不能是一个全局变量,否则在 A线程里输出的很可能是B线程的出错信息。要实现诸如此类的变量,我们就必须使用线程数据。我们为每个线程数据创建一个键,它和这个键相关联,在各个线程 里,都使用这个键来指代线程数据,但在不同的线程里,这个键代表的数据是不同的,在同一个线程里,它代表同样的数据内容。
      和线程数据相关的函数主要有4个:创建一个键;为一个键指定线程数据;从一个键读取线程数据;删除键。
      创建键的函数原型为:
    extern int pthread_key_create __P ((pthread_key_t *__key,void (*__destr_function) (void *)));
    

    第一个参数为指向一个键值的指针,第二个参数指明了一个destructor函数,如果这个参数不为空,那么当每个线程结束时,系统将调用这个函数来 释放绑定在这个键上的内存块。这个函数常和函数pthread_once ((pthread_once_t*once_control, void (*initroutine) (void)))一起使用,为了让这个键只被创建一次。函数pthread_once声明一个初始化函数,第一次调用pthread_once时它执行这 个函数,以后的调用将被它忽略。

    在下面的例子中,我们创建一个键,并将它和某个数据相关联。我们要定义一个函数 createWindow,这个函数定义一个图形窗口(数据类型为Fl_Window *,这是图形界面开发工具FLTK中的数据类型)。由于各个线程都会调用这个函数,所以我们使用线程数据。

    /* 声明一个键*/
    pthread_key_t myWinKey;
    /* 函数 createWindow */
    void createWindow ( void ) {
    Fl_Window * win;
    static pthread_once_t once= PTHREAD_ONCE_INIT;
    /* 调用函数createMyKey,创建键*/
    pthread_once ( & once, createMyKey) ;
    /*win指向一个新建立的窗口*/
    win=new Fl_Window( 0, 0, 100, 100, "MyWindow");
    /* 对此窗口作一些可能的设置工作,如大小、位置、名称等*/
    setWindow(win);
    /* 将窗口指针值绑定在键myWinKey上*/
    pthread_setpecific ( myWinKey, win);
    }
    
    /* 函数 createMyKey,创建一个键,并指定了destructor */
    void createMyKey ( void ) {
    pthread_keycreate(&myWinKey, freeWinKey);
    }
    
    /* 函数 freeWinKey,释放空间*/
    void freeWinKey ( Fl_Window * win){
    delete win;
    }
    

    这样,在不同的线程中调用函数createMyWin,都可以得到在线程内部均可见的窗口变量,这个变量通过函数 pthread_getspecific得到。在上面的例子中,我们已经使用了函数pthread_setspecific来将线程数据和一个键绑定在一 起。这两个函数的原型如下:

    extern int pthread_setspecific __P ((pthread_key_t __key,__const void *__pointer));
    extern void *pthread_getspecific __P ((pthread_key_t __key));
    

    这两个函数的参数意义和使用方法是显而易见的。要注意的是,用pthread_setspecific为一个键指定新的线程数据时,必须自己释放原有 的线程数据以回收空间。这个过程函数pthread_key_delete用来删除一个键,这个键占用的内存将被释放,但同样要注意的是,它只释放键占用 的内存,并不释放该键关联的线程数据所占用的内存资源,而且它也不会触发函数pthread_key_create中定义的destructor函数。线 程数据的释放必须在释放键之前完成。

    1. 互斥锁
      互斥锁用来保证一段时间内只有一个线程在执行一段代码。
      我们先看下面一段代码。这是一个读/写程序,它们公用一个缓冲区,并且我们假定一个缓冲区只能保存一条信息。即缓冲区只有两个状态:有信息或没有信息。
    void reader_function ( void );
    void writer_function ( void );
    
    char buffer;
    int buffer_has_item=0;
    pthread_mutex_t mutex;
    struct timespec delay;
    void main ( void ){
    pthread_t reader;
    /* 定义延迟时间*/
    delay.tv_sec = 2;
    delay.tv_nec = 0;
    /* 用默认属性初始化一个互斥锁对象*/
    pthread_mutex_init (&mutex,NULL);
    pthread_create(&reader, pthread_attr_default, (void *)&reader_function), NULL);
    writer_function( );
    }
    
    void writer_function (void){
    while(1){
    /* 锁定互斥锁*/
    pthread_mutex_lock (&mutex);
    if (buffer_has_item==0){
    buffer=make_new_item( );
    buffer_has_item=1;
    }
    /* 打开互斥锁*/
    pthread_mutex_unlock(&mutex);
    pthread_delay_np(&delay);
    }
    }
    
    void reader_function(void){
    while(1){
    pthread_mutex_lock(&mutex);
    if(buffer_has_item==1){
    consume_item(buffer);
    buffer_has_item=0;
    }
    pthread_mutex_unlock(&mutex);
    pthread_delay_np(&delay);
    }
    }
    

    这里声明了互斥锁变量mutex,结构pthread_mutex_t为不公开的数据类型,其中包含一个系统分配的属性对象。函数 pthread_mutex_init用来生成一个互斥锁。NULL参数表明使用默认属性。如果需要声明特定属性的互斥锁,须调用函数 pthread_mutexattr_init。函数pthread_mutexattr_setpshared和函数 pthread_mutexattr_settype用来设置互斥锁属性。
    函数pthread_mutexattr_setpshared设置属性pshared,它有两个取值:
    PTHREAD_PROCESS_PRIVATE //用来不同进程中的线程同步
    PTHREAD_PROCESS_SHARED //用于同步本进程的不同线程
    在上面的例子中,我们使用的是默认属性PTHREAD_PROCESS_ PRIVATE。
    函数pthread_mutexattr_settype用来设置互斥锁类型,可选的类型有:
    PTHREAD_MUTEX_NORMAL
    PTHREAD_MUTEX_ERRORCHECK
    PTHREAD_MUTEX_RECURSIVE
    PTHREAD _MUTEX_DEFAULT
    它们分别定义了不同的上锁、解锁机制,一般情况下,选用最后一个默认属性。
    pthread_mutex_lock声明开始用互斥锁上锁,此后的代码直至调用pthread_mutex_unlock为止,均被上锁,即同一时间只 能被一个线程调用执行。当一个线程执行到pthread_mutex_lock处时,如果该锁此时被另一个线程使用,那此线程被阻塞,即程序将等待到另一 个线程释放此互斥锁。在上面的例子中,我们使用了pthread_delay_np函数,让线程睡眠一段时间,就是为了防止一个线程始终占据此函数。
    在使用互斥锁的过程中很有可能会出现死锁:两个线程试图同时占用两个资源,并按不同的次序锁定相应的互斥锁,例如两个线程都需要锁定互斥锁1和互斥锁 2,a线程先锁定互斥锁1,b线程先锁定互斥锁2,这时就出现了死锁。此时我们可以使用函数 pthread_mutex_trylock,它是函数pthread_mutex_lock的非阻塞版本,当它发现死锁不可避免时,它会返回相应的信 息,程序员可以针对死锁做出相应的处理。另外不同的互斥锁类型对死锁的处理不一样,但最主要的还是要程序员自己在程序设计注意这一点。

    1. 条件变量
      前一节中我们讲述了如何使用互斥锁来实现线程间数据的共享和通信,互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞 和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互 斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁 并重新测试条件是否满足。一般说来,条件变量被用来进行线承间的同步。
      条件变量的结构为pthread_cond_t,函数pthread_cond_init()被用来初始化一个条件变量。它的原型为:
    extern int pthread_cond_init __P ((pthread_cond_t *__cond,__const pthread_condattr_t *__cond_attr));
    

    其中cond是一个指向结构pthread_cond_t的指针,cond_attr是一个指向结构pthread_condattr_t的指针。结 构 pthread_condattr_t是条件变量的属性结构,和互斥锁一样我们可以用它来设置条件变量是进程内可用还是进程间可用,默认值是 PTHREAD_ PROCESS_PRIVATE,即此条件变量被同一进程内的各个线程使用。注意初始化条件变量只有未被使用时才能重新初始化或被释放。释放一个条件变量 的函数为pthread_cond_ destroy(pthread_cond_t cond)。 
    函数pthread_cond_wait()使线程阻塞在一个条件变量上。它的函数原型为:

    extern int pthread_cond_wait __P ((pthread_cond_t *__cond,
    pthread_mutex_t *__mutex));
    

    线程解开mutex指向的锁并被条件变量cond阻塞。线程可以被函数pthread_cond_signal和函数 pthread_cond_broadcast唤醒,但是要注意的是,条件变量只是起阻塞和唤醒线程的作用,具体的判断条件还需用户给出,例如一个变量是 否为0等等,这一点我们从后面的例子中可以看到。线程被唤醒后,它将重新检查判断条件是否满足,如果还不满足,一般说来线程应该仍阻塞在这里,被等待被下 一次唤醒。这个过程一般用while语句实现。
    另一个用来阻塞线程的函数是pthread_cond_timedwait(),它的原型为:

    extern int pthread_cond_timedwait __P ((pthread_cond_t *__cond,
    pthread_mutex_t *__mutex, __const struct timespec *__abstime));
    

    它比函数pthread_cond_wait()多了一个时间参数,经历abstime段时间后,即使条件变量不满足,阻塞也被解除。
    函数pthread_cond_signal()的原型为:

    extern int pthread_cond_signal __P ((pthread_cond_t *__cond));
    

    它用来释放被阻塞在条件变量cond上的一个线程。多个线程阻塞在此条件变量上时,哪一个线程被唤醒是由线程的调度策略所决定的。要注意的是,必须用 保护条件变量的互斥锁来保护这个函数,否则条件满足信号又可能在测试条件和调用pthread_cond_wait函数之间被发出,从而造成无限制的等 待。下面是使用函数pthread_cond_wait()和函数pthread_cond_signal()的一个简单的例子。

    pthread_mutex_t count_lock;
    pthread_cond_t count_nonzero;
    unsigned count;
    decrement_count () {
    pthread_mutex_lock (&count_lock);
    while(count==0)
    pthread_cond_wait( &count_nonzero, &count_lock);
    count=count -1;
    pthread_mutex_unlock (&count_lock);
    }
    
    increment_count(){
    pthread_mutex_lock(&count_lock);
    if(count==0)
    pthread_cond_signal(&count_nonzero);
    count=count+1;
    pthread_mutex_unlock(&count_lock);
    }
    

    count值为0时, decrement函数在pthread_cond_wait处被阻塞,并打开互斥锁count_lock。此时,当调用到函数 increment_count时,pthread_cond_signal()函数改变条件变量,告知decrement_count()停止阻塞。读 者可以试着让两个线程分别运行这两个函数,看看会出现什么样的结果。
    函数pthread_cond_broadcast(pthread_cond_t *cond)用来唤醒所有被阻塞在条件变量cond上的线程。这些线程被唤醒后将再次竞争相应的互斥锁,所以必须小心使用这个函数。

    1. 信号量
      信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。当公共资源增加时,调用函数sem_post()增加信号量。只有当信号量值大 于0时,才能使用公共资源,使用后,函数sem_wait()减少信号量。函数sem_trywait()和函数pthread_ mutex_trylock()起同样的作用,它是函数sem_wait()的非阻塞版本。下面我们逐个介绍和信号量有关的一些函数,它们都在头文件 /usr/include/semaphore.h中定义。
      信号量的数据类型为结构sem_t,它本质上是一个长整型的数。函数sem_init()用来初始化一个信号量。它的原型为:
      extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));
      sem为指向信号量结构的一个指针;pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享;value给出了信号量的初始值。
      函数sem_post( sem_t *sem )用来增加信号量的值。当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不在阻塞,选择机制同样是由线程的调度策略决定的。
      函数sem_wait( sem_t *sem )被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。函数sem_trywait ( sem_t *sem )是函数sem_wait()的非阻塞版本,它直接将信号量sem的值减一。
      函数sem_destroy(sem_t *sem)用来释放信号量sem。
      下面我们来看一个使用信号量的例子。在这个例子中,一共有4个线程,其中两个线程负责从文件读取数据到公共的缓冲区,另两个线程从缓冲区读取数据作不同的处理(加和乘运算)。
    /* File sem.c */
    #include
    #include
    #include
    #define MAXSTACK 100
    int stack[MAXSTACK][2];
    int size=0;
    sem_t sem;
    /* 从文件1.dat读取数据,每读一次,信号量加一*/
    void ReadData1(void){
    FILE *fp=fopen("1.dat","r");
    while(!feof(fp)){
    fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]);
    sem_post(&sem);
    ++size;
    }
    fclose(fp);
    }
    /*从文件2.dat读取数据*/
    void ReadData2(void){
    FILE *fp=fopen("2.dat","r");
    while(!feof(fp)){
    fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]);
    sem_post(&sem);
    ++size;
    }
    fclose(fp);
    }
    /*阻塞等待缓冲区有数据,读取数据后,释放空间,继续等待*/
    void HandleData1(void){
    while(1){
    sem_wait(&sem);
    printf("Plus:%d+%d=%d/n",stack[size][0],stack[size][1],
    stack[size][0]+stack[size][1]);
    --size;
    }
    }
    
    void HandleData2(void){
    while(1){
    sem_wait(&sem);
    printf("Multiply:%d*%d=%d/n",stack[size][0],stack[size][1],
    stack[size][0]*stack[size][1]);
    --size;
    }
    }
    int main(void){
    pthread_t t1,t2,t3,t4;
    sem_init(&sem,0,0);
    pthread_create(&t1,NULL,(void *)HandleData1,NULL);
    pthread_create(&t2,NULL,(void *)HandleData2,NULL);
    pthread_create(&t3,NULL,(void *)ReadData1,NULL);
    pthread_create(&t4,NULL,(void *)ReadData2,NULL);
    /* 防止程序过早退出,让它在此无限期等待*/
    pthread_join(t1,NULL);
    }
    

    5、 异步信号
    由于LinuxThreads是在核外使用核内轻量级进程实现的线程,所以基于内核的异步信号操作对于线程也是有效的。但同时,由于异步信号总是实际发往 某个进程,所以无法实现POSIX标准所要求的"信号到达某个进程,然后再由该进程将信号分发到所有没有阻塞该信号的线程中"原语,而是只能影响到其中一 个线程。
    POSIX异步信号同时也是一个标准C库提供的功能,主要包括信号集管理(sigemptyset()、sigfillset()、 sigaddset()、sigdelset()、sigismember()等)、信号处理函数安装(sigaction())、信号阻塞控制 (sigprocmask())、被阻塞信号查询(sigpending())、信号等待(sigsuspend())等,它们与发送信号的kill() 等函数配合就能实现进程间异步信号功能。LinuxThreads围绕线程封装了sigaction()何raise(),本节集中讨论 LinuxThreads中扩展的异步信号函数,包括pthread_sigmask()、pthread_kill()和sigwait()三个函数。 毫无疑问,所有POSIX异步信号函数对于线程都是可用的。
    int pthread_sigmask(int how, const sigset_t *newmask, sigset_t *oldmask)
    设置线程的信号屏蔽码,语义与sigprocmask()相同,但对不允许屏蔽的Cancel信号和不允许响应的Restart信号进行了保护。被屏蔽的信号保存在信号队列中,可由sigpending()函数取出。
    int pthread_kill(pthread_t thread, int signo)
    向thread号线程发送signo信号。实现中在通过thread线程号定位到对应进程号以后使用kill()系统调用完成发送。

    int sigwait(const sigset_t *set, int *sig)
    

    挂起线程,等待set中指定的信号之一到达,并将到达的信号存入*sig中。POSIX标准建议在调用sigwait()等待信号以前,进程中所有线程都 应屏蔽该信号,以保证仅有sigwait()的调用者获得该信号,因此,对于需要等待同步的异步信号,总是应该在创建任何线程以前调用 pthread_sigmask()屏蔽该信号的处理。而且,调用sigwait()期间,原来附接在该信号上的信号处理函数不会被调用。
    如果在等待期间接收到Cancel信号,则立即退出等待,也就是说sigwait()被实现为取消点。

    6、关于线程的撤消
    在多线程中,某些情况下可能需要撤消一个线程,恢复线程修改了的一些变量,并且释放线程占用的一些共享资源,将系统返回到这个线程执行的初始状态或特定状 态。在处理线程撤消时需要小心处理。不能遗留已上锁的交互锁(这会导致死锁),也不能留下末释放的内存区(这将导致内存泄漏)。
    pthread提供了撤消线程的相关接口函数,这些接口可用于允许或者禁止撤消一个线程,设定撤消点等。
    调用带有一个线程ID作为参数的函数pthread_cancel()可以撤消一个线程。函数pthread_setcancelstate()和 pthread_setcanceltype()用来设定目标线程的状态和属性值,这些值确定线程如何响应撤消请求。
    pthread_setcancelstate()可以将线程设置为PTHREAD-CANCEL-ENABLE或者PTHREAD-CANCEL- DISABLE两种状态。如果线程的状态是PTHREAD-CANCEL-DISABLE,那么所有对那个线程的撤消请求将被挂起。如果状态为 PTHREAD-CANCEL-ENABLE,那么撤消行为依赖于这个线程的撤消类型。线程的撤消类型由函数pthread_setcanceltype ()设定。
    pthread_setcanceltype()可以将线程的撤消类型设置为PTHREAD-CANCEL-DEFERRED或者PTHREAD- CANCEL-ASYNCHRONOUS。如果撤消类型是PTHREAD-CANCEL-ASYNCHRONOUS,接受到一个 pthread_cancel()调用后,线程会立即撤消。如果撤消类型是PTHREAD_CANCEL-DEFERRED,那么直到线程达到一个撤消点 时才发生撤消。
    通过在线程代码中插入函数调用pthread_testcancel()就可以建立一个撤消点。当此函数执行时,如果挂起一个撤消,那么pthread_testcancel()将不返回。
    除了使用pthread_testcancel()外,pthreads还定义一些撤消点。包括等待在pthread_cond_timedwait() 和pthread_cond_wait()上的线程、等待在pthread_join()中的另一线程结束的线程、阻塞在sigwait()上的线程。也 有许多可作为撤消点的标准库调用。通常这些函数会使线程在它上面阻塞。

    pthread库提供了一对pthread_cleanup_push()/pthread_cleanup_pop()函数对用于自动释放资源–从 pthread_cleanup_push()的调用点到pthread_cleanup_pop()之间的程序段中的终止动作(包括调用 pthread_exit()和取消点终止)都将执行pthread_cleanup_push()所指定的清理函数。两个函数定义如下:

    void pthread_cleanup_push(void (*routine) (void *), void *arg);
    void pthread_cleanup_pop(int execute);
    

    这两个函数采用先入后出的栈结构管理,void routine(void *arg)函数在调用pthread_cleanup_push()时压入清理函数栈,多次对pthread_cleanup_push()的调用将在清 理函数栈中形成一个函数链,在执行该函数链时按照压栈的相反顺序弹出。execute参数表示执行到pthread_cleanup_pop()时是否在 弹出清理函数的同时执行该函数,为0表示不执行,非0为执行;这个参数并不影响异常终止时清理函数的执行。
    pthread_cleanup_push()/pthread_cleanup_pop()是以宏方式实现的,这是pthread.h中的宏定义:

    #define pthread_cleanup_push(routine,arg) /
    { struct _pthread_cleanup_buffer _buffer; _pthread_cleanup_push (&_buffer, (routine), (arg));
    #define pthread_cleanup_pop(execute) _pthread_cleanup_pop (&_buffer, (execute)); }
    可见,pthread_cleanup_push()带有一个"",因此这两个函数必须成对出现,且必须位于程序的同一级别的代码段中才能通过编译。在下 面的例子里,当线程在"do some work"中终止时,将主动调用pthread_mutex_unlock(mut),以完成解锁动作。
    pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);
    pthread_mutex_lock(&mut);
    /* do some work */
    pthread_mutex_unlock(&mut);
    pthread_cleanup_pop(0);
    

    必须要注意的是,如果线程处于PTHREAD_CANCEL_ASYNCHRONOUS状态,上述代码段就有可能出错,因为CANCEL事件有可能在 pthread_cleanup_push()和pthread_mutex_lock()之间发生,或者在pthread_mutex_unlock ()和pthread_cleanup_pop()之间发生,从而导致清理函数unlock一个并没有加锁的mutex变量,造成错误。因此,在使用清理 函数的时候,都应该暂时设置成PTHREAD_CANCEL_DEFERRED模式。为此,POSIX的Linux实现中还提供了一对不保证可移植的扩展 函数:

    pthread_cleanup_push_defer_np()
    pthread_cleanup_pop_defer_np()
    功能与以下代码段相当:
    {
    int oldtype;
    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
    pthread_cleanup_push(routine, arg);
    ...
    pthread_cleanup_pop(execute);
    pthread_setcanceltype(oldtype, NULL);
    }
    

    ==================

    相关链接:
    POSIX thread (pthread) 简介
    POSIX thread (pthread) 多线程编程简介
    Linux下pthread线程库介绍
    Pthread 介绍

    展开全文
  • Linux下的 fork 函数

    千次阅读 2015-03-18 11:48:22
    之前只是了解到linux中的fork函数是用来创建进程,并没有太的去学习,这里学习记录如下。 撰写不易,转载需注明出处:http://blog.csdn.net/jscese/article/details/44401389 本文来自 【jscese】的博客!定义:...
  • 检查当前进程对指定的文件是否具有执行某种操作的权限。...mode: 需要测试的操作模式,赋的值是一个或者多个多个用“|”(与操作)取R_OK(可读?), W_OK(可写?), X_OK(可执行?) 或 F_OK(文件存在...
  • 其实有很的,结合手册内容,介绍以下6个函数。 1,exec函数 <?php $test = “ls /tmp/test”; //ls是linux下的查目录,文件的命令 exec($test,$array); //执行命令 print_r($array); ?> ...
  • Windows有GetModuleFileName,而Linux没有...Linux的同仁们有很多理由说GetModuleFileName不好,比如文件可以有多个名字、socket也是文件、exe文件可能已经被删除等等,但是即使抛开平台移植的话题来说,G
  • linux下的select函数

    2013-08-19 15:55:20
    select系统调用是用来让我们的程序监视多个... 文件在句柄在Linux里很多,如果你man某个函数,在函数返回值部分说到成功后有一个文件句柄被创建的都是的,如man socket可以看到“On success, a file descriptor for the
  • linux下 IO接口函数

    2020-11-29 21:10:31
    int open(const char *pathname, int flags, mode_t mode); pathname:待打开文件 ...可选项:可以选择多个,附加使用 O_CLOEXEC, O_CREAT, 如果文件不存在则创建文件 O_DIREC‐TORY, O_EXCL, O_NOCTTY, O_.
  • 最近做的项目涉及到线程处理的问题,因为考虑到在windows实现的效率,在里面是有了CSingleLock,现在需要...第二就是对windows的相关函数小修改甚至不修改,然后增加linux平台函数,此种方式优点是保持之前wi
  • 从今天开始好好看看Linux的内核代码,听说学习代码最好的老师就是看内核中的代码,学习内核中代码的编写习惯,我打算整理一些学习笔记来督促自己学习,...第一个函数:printk  源码:重点分析参数函数用法。 as
  • Linux下安装PCRE函数

    2020-11-19 09:52:17
    PCRE (Perl Compatible Regular Expressions) 是一轻量级的Perl函数库,包括 perl 兼容的正则表达式库。它比Boost之类的正则表达式库小得。PCRE十分易用,同时功能也很强大,性能超过了POSIX正则表达式库和一些...
  • [Linux] linux下的GetModuleFileName函数

    千次阅读 2007-12-14 13:18:00
    Linux的同仁们有很多理由说GetModuleFileName不好,比如文件可以有多个名字、socket也是文件、exe文件可能已经被删除等等,但是即使抛开平台移植的话题来说,GetModuleFileName有时也确实很有用。听说boost和KDE都...
  • 关于Linux下的ioctl函数

    2016-03-04 13:48:40
    最近接触android开发,因为有时间所以就关注了android的源码,在跟踪源码过程中到最后都会遇到icotl函数,虽然在Symbian中曾经遇到过RSocket的icotl函数,但是当时没有细究,今天有时间就搜索了,原来这个函数是...
  • 以前我知道有二个函数可以执行linux命令,一个...其实有很的,结合手册内容,介绍以下6个函数。1,exec函数$test = "ls /tmp/test"; //ls是linux下的查目录,文件的命令exec($test,$array); //执行命令print_r($ar...
  • 前言 接触 Linux 已经有几个月了,...下面总结一下学习 Linux 进程遇到的两个函数: fork( ) 和 exec( ) 函数族。 fork( ) 根据百度百科可以知道 fork 函数将运行着的程序分成2个(几乎)完全一样的进程,每个进...
  • Linux下使用daemon函数编写后台程序 2012-12-04 00:12 8792人阅读 评论(0) 收藏 举报  分类:   linux(150)  以前我们在看《unix环境高级编程》的时候,有专门的整章详细介绍如何编写一...
  • 一个项目可能包含多个so,但是不同的so中可能包含相同的函数名。 在编译和连接的时候不会报错,但是在执行的时候可能会出现莫名其妙的错误,多是段错误。 解决办法 1、修改函数名(费时费力) 2、隐藏so中的函数 最...
  • 以前我知道有二个函数可以执行linux命令,一个...其实有很的,结合手册内容,介绍以下6个函数。1,exec函数$test = "ls /tmp/test"; //ls是linux下的查目录,文件的命令exec($test,$array); //执行命令print_r($ar...
  • 适用于:在一个Project中,有多个*.cpp/*.c文件,多个文件中同时含有main函数。处于方便考虑,在Makefile文件中,目标可执行文件的依赖项,包含了所有源文件编译生成的*.o文件。这样的话,在编译的时候,就会产生...
  • Linux中,我们可以使用 select 函数实现I/O端口的复用,传递给 select 函数的参数会告诉内核: • 我们所关心的文件描述符 • 对每描述符,我们所关心的状态。(我们是要想从一文件描述符中读或者写,...
  • linux下有很很实用的函数,但对于一个具体的linux函数,我们在使用它的时候先是只需要知道这个函数需要包含哪个头文件、函数原型和它的参数、返回值等。 NAME (名字) unlink - delete a name and pos...
  • 开头引入头文件 :#include&lt;pthread.h&gt;   pthread_t(用于声明线程标识符的变量) eg: pthread_t tid;...´pthread_t在头文件/usr/include/bits/... 它是一线程的标识符。 pthread_cr...
  • Linux下函数库管理

    2017-11-19 19:06:11
    Linux下函数库管理 简介在Linux操作系统中,函数库是很重要的一项目,因为很软件之间都会互相使用彼此提供的函数库来进行特殊功能的运行,例如很需要验证身份的程序都习惯利用PAM这模块提供的验证机制来实作...
  • Linux下C函数中文手册

    2012-11-10 14:41:11
    对于在学习Linux下进行编程的时候,很学生可能觉得函数不知道什么意思,那么这时大家就可以查询一下这手册

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,512
精华内容 1,804
关键字:

linux下多个函数

linux 订阅