struct sem_array里有多个信号量,放在struct sem sems[]数组里面,在struct sem里面有当前的信号量的数值semval。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
struct sem { int semval; /* current value */ /* * PID of the process that last modified the semaphore. For * Linux, specifically these are: * - semop * - semctl, via SETVAL and SETALL. * - at task exit when performing undo adjustments (see exit_sem). */ int sempid; spinlock_t lock; /* spinlock for fine-grained semtimedop */ struct list_head pending_alter; /* pending single-sop operations that alter the semaphore */ struct list_head pending_const; /* pending single-sop operations that do not alter the semaphore*/ time_t sem_otime; /* candidate for sem_otime */ } ____cacheline_aligned_in_smp;
static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, int cmd, void __user *p) { struct sem_array *sma; struct sem *curr; int err, nsems; ushort fast_sem_io[SEMMSL_FAST]; ushort *sem_io = fast_sem_io; DEFINE_WAKE_Q(wake_q); sma = sem_obtain_object_check(ns, semid); nsems = sma->sem_nsems; ...... switch (cmd) { ...... case SETALL: { int i; struct sem_undo *un; ...... if (copy_from_user(sem_io, p, nsems*sizeof(ushort))) { ...... } ...... for (i = 0; i < nsems; i++) { sma->sems[i].semval = sem_io[i]; sma->sems[i].sempid = task_tgid_vnr(current); } ...... sma->sem_ctime = get_seconds(); /* maybe some queued-up processes were waiting for this */ do_smart_update(sma, NULL, 0, 0, &wake_q); err = 0; goto out_unlock; } } ...... wake_up_q(&wake_q); ...... }
在semctl_main函数中,先是通过sem_obtain_object_check,根据信号量集合的id在基数树里面找到struct sem_array对象,发现如果是SETALL操作,就将用户的参数中的unsigned short *array通过copy_from_user拷贝到内核里面的sem_io数组,然后是一个循环,对于信号量集合里面的每一个信号量,设置semval,以及修改这个信号量值的pid。
if (timeout) { struct timespec _timeout; if (copy_from_user(&_timeout, timeout, sizeof(*timeout))) { } jiffies_left = timespec_to_jiffies(&_timeout); } ...... /* On success, find_alloc_undo takes the rcu_read_lock */ un = find_alloc_undo(ns, semid); ...... sma = sem_obtain_object_check(ns, semid); ...... queue.sops = sops; queue.nsops = nsops; queue.undo = un; queue.pid = task_tgid_vnr(current); queue.alter = alter; queue.dupsop = dupsop;
error = perform_atomic_semop(sma, &queue); if (error == 0) { /* non-blocking succesfull path */ DEFINE_WAKE_Q(wake_q); ...... do_smart_update(sma, sops, nsops, 1, &wake_q); ...... wake_up_q(&wake_q); goto out_free; } /* * We need to sleep on this operation, so we put the current * task into the pending queue and go to sleep. */ if (nsops == 1) { struct sem *curr; curr = &sma->sems[sops->sem_num]; ...... list_add_tail(&queue.list, &curr->pending_alter); ...... } else { ...... list_add_tail(&queue.list, &sma->pending_alter); ...... }
do { queue.status = -EINTR; queue.sleeper = current;
__set_current_state(TASK_INTERRUPTIBLE); if (timeout) jiffies_left = schedule_timeout(jiffies_left); else schedule(); ...... /* * If an interrupt occurred we have to clean up the queue. */ if (timeout && jiffies_left == 0) error = -EAGAIN; } while (error == -EINTR && !signal_pending(current)); /* spurious */ ...... }
struct sem_queue { struct list_head list; /* queue of pending operations */ struct task_struct *sleeper; /* this process */ struct sem_undo *undo; /* undo structure */ int pid; /* process id of requesting process */ int status; /* completion status of operation */ struct sembuf *sops; /* array of pending operations */ struct sembuf *blocking; /* the operation that blocked */ int nsops; /* number of operations */ bool alter; /* does *sops alter the array? */ bool dupsop; /* sops on more than one sem_num */ };
struct sem_undo { struct list_head list_proc; /* per-process list: * * all undos from one process * rcu protected */ struct rcu_head rcu; /* rcu struct for sem_undo */ struct sem_undo_list *ulp; /* back ptr to sem_undo_list */ struct list_head list_id; /* per semaphore array list: * all undos for one array */ int semid; /* semaphore set identifier */ short *semadj; /* array of adjustments */ /* one per semaphore */ };