免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1439 | 回复: 0

Linux 系统编程 ->进程通讯 -> 信号灯 [复制链接]

论坛徽章:
0
发表于 2009-12-13 01:10 |显示全部楼层

                 英文的 Semaphore  中文翻译成信号灯(大家都这么叫:))!它是IPC 通讯中很重要的一个元素 实现共享资源的分配控制  如何使用好信号灯的确是门学问 我们这里只讨论 linux API 的使用。信号灯类似于铁路上的信号灯 还有就是 P V 操作 学过操作系统的同学都知道不详细解释了。

Semaphore 也是linux kernel 维护的一个数据结构 在说API 之前先贴一些代码 位置在 /usr/include/linux/sem.h

#include
/* semop flags */
#define SEM_UNDO        0x1000  /* undo the operation on exit */
/* semctl Command Definitions. */
#define GETPID  11       /* get sempid */
#define GETVAL  12       /* get semval */
#define GETALL  13       /* get all semval's */
#define GETNCNT 14       /* get semncnt */
#define GETZCNT 15       /* get semzcnt */
#define SETVAL  16       /* set semval */
#define SETALL  17       /* set all semval's */
/* ipcs ctl cmds */
#define SEM_STAT 18
#define SEM_INFO 19
/* Obsolete, used only for backwards compatibility and libc5 compiles */
struct semid_ds {
        struct ipc_perm sem_perm;               /* permissions .. see ipc.h */
        __kernel_time_t sem_otime;              /* last semop time */
        __kernel_time_t sem_ctime;              /* last change time */
        struct sem      *sem_base;              /* ptr to first semaphore in array */
        struct sem_queue *sem_pending;          /* pending operations to be processed */
        struct sem_queue **sem_pending_last;    /* last pending operation */
        struct sem_undo *undo;                  /* undo requests on this array */
        unsigned short  sem_nsems;              /* no. of semaphores in array */
};
/* Include the definition of semid64_ds */
#include
/* semop system calls takes an array of these. */
struct [color="#0000ff"]sembuf {
        unsigned short  sem_num;        /* semaphore index in array */
        short           sem_op;         /* semaphore operation */
        short           sem_flg;        /* operation flags */
};
/* arg for semctl system calls. */
union [color="#0000ff"]semun {
        int val;                        /* value for SETVAL */
        struct semid_ds *buf;          /* buffer for IPC_STAT & IPC_SET */
        unsigned short *array;        /* array for GETALL & SETALL */
        struct seminfo *__buf;       /* buffer for IPC_INFO */
        void *__pad;
};
struct  seminfo {
        int semmap;
        int semmni;
        int semmns;
        int semmnu;
        int semmsl;
        int semopm;
        int semume;
        int semusz;
        int semvmx;
        int semaem;
};
[color="#0000ff"]/*具体系统的限制*
[color="#0000ff"]cat /proc/sys/kernel/sem 查看[color="#0000ff"]*/
#define SEMMNI  128             /*
/* unused */
#define SEMUME  SEMOPM          /* max num of undo entries per process */
#define SEMMNU  SEMMNS          /* num of undo structures system wide */
#define SEMMAP  SEMMNS          /* # of entries in semaphore map */
#define SEMUSZ  20              /* sizeof struct sem_undo */
#endif /* _LINUX_SEM_H */

typedef union
{
  char __size[__SIZEOF_SEM_T];
  long int __align;
} sem_t;
//===============================================================================
信号灯的定义 如下 来自  man 7 SEM_OVERVIEW

  A  semaphore  is  an  integer whose value is never allowed to fall below zero.  Two operations can be performed on semaphores: increment the semaphore value  by  one  (sem_post(3));  and  decrement  the  semaphore  value  by  one (sem_wait(3)).   If  the value of a semaphore is currently zero, then a sem_wait(3) operation will block until the value becomes greater than zero.  

为了移植方便我们严格按照 POSIX 标准  so POSIX semaphores come in two forms: named semaphores and unnamed semaphores
目前的linux 2.6.x.x 版本是支持 有名信号灯的
   named semaphore 创建
   The  sem_open(3)  function  creates  a new named semaphore or opens an existing named semaphore.  After the semaphore has been opened, it can be operated on using sem_post(3) and sem_wait(3).   When  a  process  has finished using the semaphore, it can use sem_close(3) to close the semaphore.  When all processes have fin‐ished using the semaphore, it can be removed from the system using sem_unlink(3).
  Unnamed semaphores 创建
   Before  being used, an unnamed semaphore must be initialized using sem_init(3).  It can then be operated on using sem_post(3) and sem_wait(3).  When the semaphore is no longer required,  and  before  the  memory  in which it is located is deallocated, the semaphore should be destroyed using sem_destroy(3).

先说 named semaphore how to

函数原型
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int val)

[color="#990099"]#include            /* For O_* constants */
[color="#660099"]#include         /* For mode constants */
[color="#990099"]#include
main() {
        sem_t * my_semaphore;
        int rc;
        my_semaphore = sem_open("myfile", //
                        O_CREAT, S_IRUSR | S_IWUSR, 10);
        if(my_semaphore==SEM_FAILED){
                perror(" Name semaphore failure ");
        }
    sleep(15); // for debug
        if( sem_unlink("myfile")!=0)
                perror("delete Name semaphore failure !\n");
}

特别注意一下 sem_open 的第一个参数 名字不能写成 dev/myfile 你可以写成 /myfile 为什么了 你可以看 代码 /nptl/sem_open.c 我简单说下 系统会在VFS上创建一个文件 位置在 /dev/shm/sem.myfile

[email=v@debian-NanJing:%7E/C/Clib/Semaphores$]@debian-NanJing:~/C/Clib/Semaphores$[/email]
./a.out &   
[1] 4573
[email=v@debian-NanJing:%7E/C/Clib/Semaphores$]@debian-NanJing:~/C/Clib/Semaphores$[/email]
ls -i /dev/shm/
16156 sem.myfile
[email=v@debian-NanJing:%7E/C/Clib/Semaphores$]@debian-NanJing:~/C/Clib/Semaphores$[/email]
ls -il /dev/shm/
total 4
16156 -rw------- 1 vivi vivi 16 2009-12-12 23:48 sem.myfile
[email=v@debian-NanJing:%7E/C/Clib/Semaphores$]@debian-NanJing:~/C/Clib/Semaphores$[/email]


如果你一定要写成那样的话需要自己建一级目录 其余的参数同 open 不多说了 。

匿名信号集的创建比较简单了

函数定义
int sem_init(sem_t *sem, int pshared, unsigned int value);

int semget(key_t key, int nsems, int semflg); 创建方式类似于消息队列

这2个函数都可以创建信号集 semget 需要做KEY 并且需要初始化通过
int semctl(int semid, int semnum, int cmd, ...);

sem_init 比较麻烦 需要分2种情况区别对待

    The pshared argument indicates whether this semaphore is to be shared between the [color="#0000ff"]threads of a process, or [color="#66cccc"]between processes.
       If  pshared  has the value 0, then the semaphore is shared between the threads of a process, and should be located at some address that is visible to all threads (e.g., a global variable, or a variable  allocated  dynamically  on the heap).
       If  pshared  is  non-zero,  then  the  semaphore is shared between processes, and should be located in a region of shared memory (see shm_open(3), mmap(2), and shmget(2)).  (Since a child created by fork(2) inherits its  parent’s
memory  mappings,  it  can  also  access the semaphore.)  Any process that can access the shared memory region can operate on the semaphore using sem_post(3), sem_wait(3), etc.

剩下API 在 2个例子里面 可以参考下 标准还是 man :)
先写个简单的 DEMO 我们就先从无名信号灯开始 运行结果如下

[email=v@debian-NanJing:%7E/C/Clib/Semaphores$]@debian-:~/C/Clib/Semaphores$[/email]
strace -c ./a.out
.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.--job1 closing up
o--job2 closing up
dummy value is 40
answer is 40 is OK? carriage return  over testing
注意看下面的图标
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
   nan    0.000000           0         4           read
   nan    0.000000           0         3           write
   nan    0.000000           0         4           open
   nan    0.000000           0         4           close
   nan    0.000000           0         1           execve
   nan    0.000000           0         5         5 access
   nan    0.000000           0         3           brk
   nan    0.000000           0         1           munmap
   nan    0.000000           0         2           clone
   nan    0.000000           0         1           uname
   nan    0.000000           0         3           mprotect
   nan    0.000000           0         2           rt_sigaction
   nan    0.000000           0         1           rt_sigprocmask
   nan    0.000000           0         1           getrlimit
   nan    0.000000           0        15           mmap2
   nan    0.000000           0         5           fstat64
   nan    0.000000           0         3           [color="#ff0000"]futex
   nan    0.000000           0         1           set_thread_area
   nan    0.000000           0         1           set_tid_address
   nan    0.000000           0         1           set_robust_list
------ ----------- ----------- --------- --------- ----------------
100.00    0.000000                    61         5 total
/*
*
* NAME : Demosem.c
*
* PURPOSE: Demo local Semaphore
*
* AUTHOR: Masonzhang
*
*/
#define _POSIX_SOURCE
#include stdio.h>
#include unistd.h> /* syscall */
#include stdlib.h>
#include linux/sem.h>
#include semaphore.h>
#include errno.h>
#include pthread.h>

/*
gcc -std=c99 Demosem.c -lrt
*/
/* define GLOBAL */
sem_t critical_val;
int dummy=0;
static void *
job1(){
        int n=0;
        int err;
        for(int i=0;i20;i++){
                do{
                        err=sem_trywait(&critical_val);
                        if(err==EAGAIN){
                                sleep(1);
                                continue;
                        }
                        else if(err !=0)
                                continue;
                        else
                        ;
                }while(err);
                n=dummy;
                n+=1;
                dummy=n;
                if(sem_post(&critical_val)){
                        perror(" job1 leaving critical section failure\n");
                        pthread_exit(NULL);
                }
                sleep(1);
                [color="#ff0000"]puts(".");
                fflush(NULL);
        }
        puts("--job1 closing up\n");
        pthread_exit(NULL);
}
static void *
job2(){
    int n=0;
        int err;
        for(int i=0;i20;i++){
                do{
                        err=sem_trywait(&critical_val);
                        if(err==EAGAIN){
                                sleep(1);
                                continue;
                        }
                        else if(err!=0)
                                continue;
                        else
                                ;
                }while(err);
                n=dummy;
                n+=1;
                dummy=n;
                if(sem_post(&critical_val)){
                        perror(" job2 leaving critical section failure\n");
                        pthread_exit(NULL);
                }
                sleep(1);
                [color="#ff0000"]puts("o");
                fflush(NULL);
        }
        puts("--job2 closing up\n");
        pthread_exit(NULL);
}
extern int
main(int argc, char * argv[]){
        pthread_t job[2]={0,};
        if(sem_init(&critical_val,0,1)){
                perror(" critical create failure !");
                abort();
        }
        if(pthread_create(&job[0],NULL,job1,NULL)){
                perror(" thread create failure!");
                abort();
        }
        if(pthread_create(&job[1],NULL,job2,NULL)){
                perror(" thread create failure!");
                abort();
        }
        /* free thread resource */
        for(int i=0;i2;i++)
                if(pthread_join(job,NULL)){
                        perror(" pthread Join Error !");
                        abort();
                }
        printf(" dummy value is %d \n",dummy);
        puts(" answer is 40 is OK? carriage return over testing \n");
        getchar();
        exit(EXIT_SUCCESS);
}

注意 信号量操作机制通过 [color="#ff0000"]futex [color="#000000"]意思就是不用跑的内核里面耍效率问题 这个问题比较复杂 偶还不是很清楚 :(
大家{
man futex (4)            - Fast Userspace Locking
man futex (7)            - Fast Userspace Locking
man futex (2)            - Fast Userspace Locking system call
}
同样的程序我们用进程来做下
               
                /*
*
*  NAME   :DemosemProcess.c
*  PUPURE :between process semaphore demo
*  author :maosnzhang
*
*
*/
#define _POSIX_SOURCE
#include stdio.h>
#include stdlib.h>
#include strings.h>
#include unistd.h>
#include linux/sem.h>
#include semaphore.h>
#include errno.h>
#include fcntl.h>
#include sys/mman.h>
#include sys/stat.h>
#include sys/types.h>
#include sys/wait.h>
/*
*  macro define
*/
#define DEVICE_NULL  "/dev/zero"
#define ERROR_MSG(MSG) \
        do {\
                perror(MSG) ; \
                exit(EXIT_FAILURE); \
        }while(0)
/*
*  global define
*/
volatile char  * global_buf;
enum {mem_size=16};
struct work_data{
        int m_num;
        int private;
        int value;
};
sem_t critical_val;
/*
*  function define
*/
static char *
get_mem(const int size){
        char * s_ptr=NULL;
        int fd=open(DEVICE_NULL,O_RDWR);
        if(fd==-1) ERROR_MSG("OPEN DEVICE_NULL FAILURE \n");
        s_ptr=(char *)mmap(NULL,  size, PROT_READ|PROT_WRITE, MAP_SHARED,fd, 0);
        if(s_ptr==MAP_FAILED) ERROR_MSG(" MEM MAP FAILURE \n");
        close(fd);
        return s_ptr;
}
static void
del_mem(void * v_ptr,const int size){
        if(munmap( v_ptr ,size))
                ERROR_MSG("DELETE MEMORY FAILURE \n");
}
static void
job(const char *s ,void * v_ptr){
        int i=0;
        int n=0;
        int err=1;
   volatile     struct work_data* w_ptr=( volatile struct work_data*)v_ptr;
        while(err){
                err=sem_wait(&critical_val);
                if(err==EAGAIN) {
                        //sleep(1) ;
                        continue;
                }
        }
        n=w_ptr->value;
        n+=1;
        w_ptr->value=n;
        sleep(1);
        sem_post(&critical_val);
        usleep(100);
        printf(s);
        fflush(NULL);
}
extern int
main(int argc,char * argv[]){
        int cpid ,i,status;
        global_buf=get_mem(mem_size);
        volatile struct work_data * w_ptr=(volatile struct work_data *)global_buf;
        bzero(w_ptr,mem_size);
        if(sem_init(&critical_val,~(0),1)) ERROR_MSG(" CRITICAL CREATE FAILURE\n");
        cpid=fork();
        if(cpid==0){
                i=0;
                for(;i20;i++)
                        job("o",w_ptr);
                _exit(0);
        }
        if(cpid0) ERROR_MSG("CHILD FAILURE \n");
        /* parent do job */
        i=0;
        for(;i20;i++)
                job(".",w_ptr);
        do {
                int w = waitpid(cpid, &status, WUNTRACED | WCONTINUED);
                if (w == -1) ERROR_MSG("waitpid \n");
                if (WIFEXITED(status)) {
                        printf("exited, status=%d\n", WEXITSTATUS(status));
                } else if (WIFSIGNALED(status)) {
                        printf("killed by signal %d\n", WTERMSIG(status));
                } else if (WIFSTOPPED(status)) {
                        printf("stopped by signal %d\n", WSTOPSIG(status));
                } else if (WIFCONTINUED(status)) {
                        printf("continued\n");
                }
        } while (!WIFEXITED(status) && !WIFSIGNALED(status));
#if 0
        printf(" TESTING WAS OVER   AND RESULT IS  %d \n",w_ptr->value);
        puts("THIS IS TEST VALUE IS 40 ? TRUE IS PRESS KEY Y OR Y");
#endif
        if(w_ptr->value==40)
                puts("OK\n");
        else
        printf("failure is %d\n",w_ptr->value);
#if 0
        int c=getchar();
        if(c=='Y' ||c=='y')
                puts("TEST IS OK!\n");
        else
                puts("TESTING IS FAILURE\n");
#endif
        del_mem(global_buf,mem_size);
        sem_destroy(&critical_val);
        exit(EXIT_SUCCESS);
}/*main*/
特别留意用于留意printf 函数调试可以用正式版本务必去除


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/94090/showart_2119651.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP