如何使用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 );这样代码很清晰。

你可能感兴趣的:(struct,unix,Semaphore,System,Signal,returning)