燕青专栏

读书笔记及技术探讨

原创 如何使用System V的信号量收藏

System V的信号量是一个庞大而且复杂的IPC模块,对于它的使用也是相对比较困难的,我的建议是使用posix.1定义的信号量,它简单易用,并且移植性好。为了便于日后对posix.1信号量的讨论,这片文章主要讨论如何使用System V的信号量的使用方法,下面一段程序简单的演示了一般的使用方法:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/wait.h>

int v7_sem_create()
{
        key_t sem_set_key = ftok( "/tmp", 'S' );
        int sem_set_id = semget( sem_set_key, 1, IPC_CREAT | IPC_EXCL | 0666 );

        if ( sem_set_id != -1 )
        {
                if ( semctl( sem_set_id, 0, SETVAL, 1 ) == -1  )
                {
                        perror( "[v7_sem_create]" );
                        semctl( sem_set_id, 0, IPC_RMID );
                        sem_set_id = -1;
                }
        }
        else
        {
                perror( "[v7_sem_create]" );
        }

        return sem_set_id;
}

int v7_sem_get()
{
        key_t sem_set_key = ftok( "/tmp", 'S' );
        int sem_set_id = semget( sem_set_key, 1, 0666 );

        if ( sem_set_id == -1 )
                perror( "[v7_sem_get]" );

        return sem_set_id;
}

int v7_sem_destroy( int sem_set_id )
{
        int rs = semctl( sem_set_id, 0, IPC_RMID );

        if ( rs == -1 )
                perror( "[v7_sem_destroy]" );

        return rs;
}

int v7_sem_enter( int sem_set_id )
{
        struct sembuf sem;
        int rs;

        sem.sem_num = 0;
        sem.sem_op  = -1;
        sem.sem_flg = SEM_UNDO;

        rs = semop( sem_set_id, &sem, 1 );
        if ( rs  == -1 )
                perror( "[v7_sem_enter]" );

        return rs;
}

int v7_sem_exit( int sem_set_id )
{
        struct sembuf sem;
        int rs;

        sem.sem_num = 0;
        sem.sem_op  = 1;
        sem.sem_flg = SEM_UNDO;

        rs = semop( sem_set_id, &sem, 1 );
        if ( rs  == -1 )
                perror( "[v7_sem_exit]" );

        return rs;
}

void v7_show_status( pid_t pid, int status )
{
        int flag = 1;

        printf( "[v7_show_status]: pid = %ul => ", pid );

        if ( WIFEXITED( status ) )
        {
                flag = 0;
                printf( "true if the child terminated normally, that is, "
                                "by calling exit() or _exit(), or "
                                "by returning from main().\n" );
        }

        if ( WEXITSTATUS( status ) )
        {
                flag = 0;
                printf( "evaluates to the least significant eight bits of the "
                        "return code of  the  child  which terminated, which may "
                        "have been set as the"
            "argument to a call to exit() or _exit() or as the argument for a"
            "return  statement  in  the main program.  This macro can only be"
            " evaluated if WIFEXITED returned true. \n" );
        }

        if ( WIFSIGNALED( status ) )
        {
                flag = 0;
                printf( " true if the child process terminated because of a signal"
             " which was not caught.\n" );
        }

        if ( WTERMSIG( status ) )
        {
                flag = 0;
                printf( "  the  number of the signal that caused the child process"
              "to terminate. This macro can only be  evaluated  if  WIFSIGNALED"
              "returned non-zero.\n");
        }

        if ( WIFSTOPPED( status ) )
        {
                flag = 0;
                printf( "  true  if  the  child process which caused the return is"
              "currently stopped; this is only possible if the  call  was  done"
              "using   WUNTRACED  or  when  the  child  is  being  traced  (see"
              "ptrace(2)).\n" );
        }

        if ( WSTOPSIG( status ) )
        {
                flag = 0;
                printf( " the number of the signal which caused the child to stop."
             "This   macro  can  only  be  evaluated  if  WIFSTOPPED  returned"
             "non-zero.\n" );
        }

        if ( flag )
        {
                printf( "Unknown status = 0x%X\n", status );
        }
}

void v7_do_something( void )
{
        int i;
        int j;

        for ( i = 0, j = 0; i < 10000; ++ i )
        {
                if ( i % 2 == 0 )
                {
                        j += 2;
                }

                if ( i % 4 != 0 )
                {
                        j -= 4;
                }

                if ( i % 6 != 0 )
                {
                        j += 6;
                }

                if ( i % 8 == 0 )
                {
                        j -= 8;
                }

                if ( i % 10 == 0 )
                {
                        j += 10;
                }
        }
        i -= j;
}

void v7_child1( void )
{
        int sem_set_id;
        int i;

        sem_set_id = v7_sem_get();
        if ( sem_set_id == -1 )
                return;
        for ( i = 0; i < 10; ++ i )
        {
                v7_sem_enter( sem_set_id );
                printf( "[v7_child1]: enter semaphore\n" );
                v7_sem_exit( sem_set_id );
                v7_do_something();
        }
}

void v7_child2( void )
{
        int sem_set_id;
        int i;

        sem_set_id = v7_sem_get();
        if ( sem_set_id == -1 )
                return;
        for ( i = 0; i < 10; ++ i )
        {
                v7_sem_enter( sem_set_id );
                printf( "[v7_child2]: enter semaphore\n" );
                v7_sem_exit( sem_set_id );
                v7_do_something();
        }
}

int main( void )
{
        int sem_set_id;
        int status;
        pid_t pid1;
        pid_t pid2;

        // create semaphore
        sem_set_id  = v7_sem_create();

        // acquire semaphore to synchronize children
        v7_sem_enter( sem_set_id );

        pid1 = fork();

        if ( pid1 < 0 )
                printf( "[main]: fail to fork child1\n" );
        else if ( pid1 == 0 ) // child process
        {
                v7_child1();

                return 0;
        }

        pid2 = fork();

        if ( pid2 < 0 )
                printf( "[main]: fail to fork child2\n" );
        else if ( pid2 == 0 ) // child process
        {
                v7_child2();

                return 0;
        }

        // parent process

        // Release semaphore
        v7_sem_exit( sem_set_id );

        if ( waitpid( pid1, &status, 0 ) != pid1 )
                perror( "[main, wait child1]" );

        v7_show_status( pid1, status );

        if ( waitpid( pid2, &status, 0 ) != pid2 )
                perror( "[main, wait child2]" );

        v7_show_status( pid2, status );

        v7_sem_destroy( sem_set_id );


        return 0;
}

System V的信号量的主要缺点是:
1)semget和semctl操作过于复杂,并且不是原子操作,也就是说v7_sem_create函数是多进程不安全的。
2)信号量的申请和释放是通过信号量集的id来控制的,相比于posix.1中的信号量,显的过于复杂,不易理解。
3)仅仅有IPC_NOWAIT操作,无法实现固定时间的超时机制。对于GNU定义的
int  semtimedop(int  semid, struct sembuf *sops, unsigned nsops, struct timespec *timeout);对于很多unix是不支持的。

System V信号量使用的注意点:
1)在调用semop时候,SEM_UNDO最好被设置在flg中,否则一旦使用进程异常死去,所占用的信号量资源不会被释放。
2)System V的信号量是kernal的持久性资源,只有reboot后才能释放。所以当全部进程退出时候,应该要删除它,否则消耗一定的资源。
3)之用IPC_CREAT 创建信号量集合而不用 IPC_EXCL的话,创建者无法确定是否获取的信号量集是否是新创建的还是已经存在,所以在用IPC_CREAT时最好追加IPC_EXCL。如果是仅仅获取信号量集合,那么这两个标记都不要加。
4)在使用semget函数时候,如果信号量集合已经存在,并且熟知他的访问属性的话,semflg可以设置为0,比如说int sem_set_id = semget( sem_set_key, 1, 0 );不过我建议最好明确的制定,比如:int sem_set_id = semget( sem_set_key, 1, 0666 );这样代码很清晰。

发表于 @ 2006年08月01日 15:52:00|评论(loading...)

新一篇: Base ClearCase与ClearQuest的集成 | 旧一篇: 如何用ftok来产生相关的key_t值

用户操作
[即时聊天] [发私信] [加为好友]
燕青
订阅我的博客
XML聚合  FeedSky
文章分类
收藏
    c/c++优秀网站
    boost
    c/c++ Reference
    C99标准
    Effective STL 中文版
    stlport
    stlsgi
    编程爱好者
    eBook
    rinet
    Excel/VBA
    VBA参考手册
    VBA自由代码库
    Linux GNU
    GNU Libs HOWTO
    Linux GUI
    QT中文论坛
    MICOM
    AVR MICRO
    WinAVR Tutorial
    MSND
    MSDN在线帮助
    web聊天网
    查看MSN联系人状态
    ebuddy
    iloveim
    imhaha
    meebo
    meebo11
    Web MSN
    米博
    澳洲移民
    前程专业留学移民公司
    百科全书
    QWika
    vesa标准
    wikipedia
    电子元器件网站
    eeworld
    好友链接
    Anders New Blog
    Anders的博客
    Anders的网站
    全胜花的生命痕迹
    小葛的博客
    尧的快乐一家
    捷仔的博客
    老郭的博客
    老钱的博客
    金种子母婴生活馆
    阿耀的博客
    家用网站
    上海移动通信
    中国电信网上营业厅
    金融网站
    纳斯达克
    开源网站
    apache
    opensource
    SVN
    垮平台GUI
    microwindows
    wxWidgets
    敏捷编程
    agilejournal
    extremeprogramming
    jayasoft
    maven
    上海四金网
    上海住房公积金网
    上海养老保险网
    上海医保网
    万年历
    21page万年历
    百渡万年历
    网页脚本教程
    DynamicDrive
    PageSource
    网站交换
    刘韧交换链接网站
    英语听力
    VOA慢速英语
    海词字典
    英语中级听力
    语高级听力
    优秀UNIX/LINUX网站
    IBM Linux论坛
    opengroup
    perl
    pthread
    socket
    UNIX Specification
    unix标准大全
    永远的UNIX
    存档
    Csdn Blog version 3.1a
    Copyright © 燕青