今日はsemop()の実装を。システムコール的にはsemop(2)とsemtimedop(2)の2つの関数がありますが、カーネルのsemop()側はsemtimedop(semid, sops, nsops, NULL);と呼び出してるだけなので、実体は一つです。
では、semop(2)とsemtimedop(2)の共通実装部分、というかsemtimedop(2)の実装を見ていきます。最初は変数定義なので飛ばそうと思うんだけど、sops = fast_sopsは後で出てくるので覚えときます。
1774 SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, 1775 unsigned, nsops, const struct timespec __user *, timeout) 1776 { 1777 int error = -EINVAL; 1778 struct sem_array *sma; 1779 struct sembuf fast_sops[SEMOPM_FAST]; 1780 struct sembuf *sops = fast_sops, *sop; 1781 struct sem_undo *un; 1782 int undos = 0, alter = 0, max, locknum; 1783 struct sem_queue queue; 1784 unsigned long jiffies_left = 0; 1785 struct ipc_namespace *ns; 1786 struct list_head tasks;
カレントプロセスのnamespaceからipc namespaceを取得します。そして引数のチェックです。
最後のnsops > SEMOPM_FASTが先ほど覚えておこうと言ったところにつながります。
1788 ns = current->nsproxy->ipc_ns; 1789 1790 if (nsops < 1 || semid < 0) 1791 return -EINVAL; 1792 if (nsops > ns->sc_semopm) 1793 return -E2BIG; 1794 if (nsops > SEMOPM_FAST) { 1795 sops = kmalloc(sizeof(*sops)*nsops, GFP_KERNEL); 1796 if (sops == NULL) 1797 return -ENOMEM; 1798 }
SEMOPM_FASTはipc/sem.cで定義されていて、操作するセマフォの数が64個以下ならstack上のデータ(fast_sops)、それ以上ならメモリを確保するというふうに処理しています。
154 #define SEMOPM_FAST 64 /* ~ 372 bytes on stack */
ユーザーランドからのデータをコピーします。
1799 if (copy_from_user(sops, tsops, nsops * sizeof(*tsops))) { 1800 error = -EFAULT; 1801 goto out_free; 1802 }
ここはsemtimedop(2)の場合の処理です。やっているのはtimeoutのデータをユーザーランドからコピーして、設定値のチェック。そして、struct timespecをjiffiesに変換します。
803 if (timeout) { 1804 struct timespec _timeout; 1805 if (copy_from_user(&_timeout, timeout, sizeof(*timeout))) { 1806 error = -EFAULT; 1807 goto out_free; 1808 } 1809 if (_timeout.tv_sec < 0 || _timeout.tv_nsec < 0 || 1810 _timeout.tv_nsec >= 1000000000L) { 1811 error = -EINVAL; 1812 goto out_free; 1813 } 1814 jiffies_left = timespec_to_jiffies(&_timeout); 1815 }
次にセマフォ1つ1つに対して値のチェック・フラグの設定を行います。
1816 max = 0; 1817 for (sop = sops; sop < sops + nsops; sop++) { 1818 if (sop->sem_num >= max) 1819 max = sop->sem_num; 1820 if (sop->sem_flg & SEM_UNDO) 1821 undos = 1; 1822 if (sop->sem_op != 0) 1823 alter = 1; 1824 }
このリストは後々の処理で起床させるタスクを繋ぐのに使用しています。
1826 INIT_LIST_HEAD(&tasks);
undosはstruct sembufのsem_flgにSEM_UNDOが設定されていた場合に1になっています。セットしているのは上述のループ部です。undo操作は別途読もうということにして、ここでは飛ばします。そうすると実行するのはrcu_read_lock()となります。
1828 if (undos) { 1829 /* On success, find_alloc_undo takes the rcu_read_lock */ 1830 un = find_alloc_undo(ns, semid); 1831 if (IS_ERR(un)) { 1832 error = PTR_ERR(un); 1833 goto out_free; 1834 } 1835 } else { 1836 un = NULL; 1837 rcu_read_lock(); 1838 }
semidに該当するオブジェクトがnamespace内にあるかチェックと取得。
1840 sma = sem_obtain_object_check(ns, semid); 1841 if (IS_ERR(sma)) { 1842 rcu_read_unlock(); 1843 error = PTR_ERR(sma); 1844 goto out_free; 1845 }
セマフォの数が大きくなり過ぎたらエラーです。maxは先に見たループで設定しています。
1847 error = -EFBIG; 1848 if (max >= sma->sem_nsems) 1849 goto out_rcu_wakeup;
パーミッションのチェックです。
1851 error = -EACCES; 1852 if (ipcperms(ns, &sma->sem_perm, alter ? S_IWUGO : S_IRUGO)) 1853 goto out_rcu_wakeup;
security_と付いているようにここはselinux等の処理なので飛ばします。
1855 error = security_sem_semop(sma, sops, nsops, alter); 1856 if (error) 1857 goto out_rcu_wakeup;
ロックを取ります。sem_lock()はsops->sem_numもしくは-1を返します。
1859 error = -EIDRM; 1860 locknum = sem_lock(sma, sops, nsops);
オブジェクトのチェック。
1869 if (!ipc_valid_object(&sma->sem_perm)) 1870 goto out_unlock_free;
sem_flgにSEM_UNDOが指定されていた場合のエラーチェック。
1878 if (un && un->semid == -1) 1879 goto out_unlock_free;
queueの型はstruct queueです。定義場所はipc/sem.cなのでここでしか使われないはずです。
1881 queue.sops = sops; 1882 queue.nsops = nsops; 1883 queue.undo = un; 1884 queue.pid = task_tgid_vnr(current); 1885 queue.alter = alter;
構造体の定義はこのようになっています。寝ているプロセスをsem_queueに設定してリストで繋ぐ感じですね。
104 /* One queue for each sleeping process in the system. */ 105 struct sem_queue { 106 struct list_head list; /* queue of pending operations */ 107 struct task_struct *sleeper; /* this process */ 108 struct sem_undo *undo; /* undo structure */ 109 int pid; /* process id of requesting process */ 110 int status; /* completion status of operation */ 111 struct sembuf *sops; /* array of pending operations */ 112 struct sembuf *blocking; /* the operation that blocked */ 113 int nsops; /* number of operations */ 114 int alter; /* does *sops alter the array? */ 115 };
perform_atomic_semop()はnsops個のセマフォを操作します。すべて実行できたら0が返ります。一つでも操作ができなかった場合は-EAGAINが返ります(IPC_NOWAITがsem_flgに設定されていた場合)。IPC_NOWAITが設定されていない場合は1が返ります。
if分に入って、alterはsem_opが0じゃなかった場合に1になります。
do_smart_update()は大雑把に説明するとdo_smart_wakeup_zero()を呼んでstruct semのsemvalが0なプロセスを起床してセマフォ操作を実行させるものです。
set_semotime()はstruct semのsem_otimeにUNIX時刻を設定します。
1887 error = perform_atomic_semop(sma, &queue); 1888 if (error == 0) { 1889 /* If the operation was successful, then do 1890 * the required updates. 1891 */ 1892 if (alter) 1893 do_smart_update(sma, sops, nsops, 1, &tasks); 1894 else 1895 set_semotime(sma, sops); 1896 }
perform_atomic_semop()より-EAGAINが返った場合はここで終了です。
1897 if (error <= 0) 1898 goto out_unlock_free;
次ですが、操作するセマフォ数が1つの時と複数の時で処理が違っていますがいづれにせよqueueのリストにpending_alterもしくはpending_constが繋がっていきます。
1904 if (nsops == 1) { 1905 struct sem *curr; 1906 curr = &sma->sem_base[sops->sem_num]; 1907 1908 if (alter) { 1909 if (sma->complex_count) { 1910 list_add_tail(&queue.list, 1911 &sma->pending_alter); 1912 } else { 1913 1914 list_add_tail(&queue.list, 1915 &curr->pending_alter); 1916 } 1917 } else { 1918 list_add_tail(&queue.list, &curr->pending_const); 1919 } 1920 } else { 1921 if (!sma->complex_count) 1922 merge_queues(sma); 1923 1924 if (alter) 1925 list_add_tail(&queue.list, &sma->pending_alter); 1926 else 1927 list_add_tail(&queue.list, &sma->pending_const); 1928 1929 sma->complex_count++; 1930 }
この次からの処理のための初期設定です。
1932 queue.status = -EINTR; 1933 queue.sleeper = current; 1934
カレントプロセスを割り込み可能にして今掛けられているlockを外します。gotoのラベル「sleep_again」があるので、またここに戻ってくることもあります。
1935 sleep_again: 1936 current->state = TASK_INTERRUPTIBLE; 1937 sem_unlock(sma, locknum); 1938 rcu_read_unlock();
timeoutの設定がある場合は指定された時間待ち、指定が無ければ別のプロセスに実行を譲ります。
1940 if (timeout) 1941 jiffies_left = schedule_timeout(jiffies_left); 1942 else 1943 schedule();
キューにつないだプロセスに変化があったかを確認します。
1963 /* 1964 * Wait until it's guaranteed that no wakeup_sem_queue_do() is ongoing. 1965 */ 1945 error = get_queue_result(&queue);
エラーチェック。
1968 /* 1969 * Array removed? If yes, leave without sem_unlock(). 1970 */ 1971 if (IS_ERR(sma)) { 1972 rcu_read_unlock(); 1973 goto out_free; 1974 }
sleep_againラベルの前でqueue.statusを-EINTRにしているのでget_queue_result()の返り値が-EINTRでないということは誰か他のプロセスによって起床したということで終了処理に飛ぶ。
1977 /* 1978 * If queue.status != -EINTR we are woken up by another process. 1979 * Leave without unlink_queue(), but with sem_unlock(). 1980 */ 1981 if (error != -EINTR) 1982 goto out_unlock_free;
タイムアウトした場合は-EAGAINを設定。
1984 /* 1985 * If an interrupt occurred we have to clean up the queue 1986 */ 1987 if (timeout && jiffies_left == 0) 1988 error = -EAGAIN;
queue.statusが相変わらず-EINTRかつ保留中のシグナルがない場合はsleep_againに戻る。
1990 /* 1991 * If the wakeup was spurious, just retry 1992 */ 1993 if (error == -EINTR && !signal_pending(current)) 1994 goto sleep_again;
ここからは関数を抜ける前の終了処理です。
1996 unlink_queue(sma, &queue); 1997 1998 out_unlock_free: 1999 sem_unlock(sma, locknum); 2000 out_rcu_wakeup: 2001 rcu_read_unlock(); 2002 wake_up_sem_queue_do(&tasks); 2003 out_free: 2004 if (sops != fast_sops) 2005 kfree(sops); 2006 return error; 2007 }
- 作者: Tae Yeon Kim,Hyung Joo Song,Ji Hoon Park,Bak Lee,Ki Young Lim,Androidフレームワーク研究会
- 出版社/メーカー: パーソナルメディア
- 発売日: 2013/12/18
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る