1 名前:デフォルトの名無しさん mailto:sage [2008/07/05(土) 19:26:16 ] マルチスレッドプログラミングについて語るスレ ■前スレ マルチスレッドプログラミング相談室 その6 ttp://pc11.2ch.net/test/read.cgi/tech/1187008532/ ■過去スレ その1 ttp://pc3.2ch.net/tech/kako/997/997345868.html その2 ttp://pc5.2ch.net/test/read.cgi/tech/1037636153/ その3 ttp://pc8.2ch.net/test/read.cgi/tech/1098268137/ その4 ttp://pc8.2ch.net/test/read.cgi/tech/1130984585/ その5 ttp://pc11.2ch.net/test/read.cgi/tech/1157814833/ OS・言語・環境は問わないが、それゆえ明記すべし。 テンプレ 【OS】 【言語】 【実行環境】 【その他突起する事項】
855 名前:デフォルトの名無しさん mailto:sage [2009/09/09(水) 01:06:23 ] >>853 ja.wikipedia.org/wiki/%E5%89%B2%E3%82%8A%E8%BE%BC%E3%81%BF%E3%83%8F%E3%83%B3%E3%83%89%E3%83%A9 > 割り込みスレッド > Solaris、Mac OS X、FreeBSD などのOSでは、割り込みスレッド(Interrupt thread)と > 呼ばれる方式が採用されている。割り込みハンドラは高優先度のスレッドであり、割り > 込みによって起動され、排他制御でブロックされるという重要な特徴がある。
856 名前:デフォルトの名無しさん mailto:sage [2009/09/09(水) 01:46:16 ] >>854 、>>855 だから、割り込みスレッドと、割り込みコンテキストはちゃうっつーのに。 2chで何かを期待した俺がアフォだったよ。
857 名前:824 mailto:sage [2009/09/09(水) 01:56:56 ] >>852 Solarisは専門外なので書籍は持っておらず、Sunのサイトで Writing Device Drivers という文書を斜め読みしてみました。 以下は第8章 Interrupt Handlers の節 Interrupt Handler Overview からの引用です。 ・docs.sun.com/app/docs/doc/816-4854/interrupt-15678?l=ja&a=view .... The job of the interrupt handler is to service the device and stop the device from interrupting. When the interrupt handler returns, the CPU resumes the work it was doing before the interrupt occurred. ここには「割込ハンドラがデバイスからの割込を禁止し(stop)、ハンドラがリターンすると CPUは以前の処理を再開(resume)する」とあります。これは割禁を指していると思うのですが....? Solarisの場合には、デバイス構造体に「特殊な」優先度付きmutexが含まれているみたいですね。 割込ハンドラが、この特殊なmutexを操作することで割禁制御を実現しているように推測できます。 ただし、この優先度はハード割込の優先度であって、スレッド実行の優先度ではありません。 また、このmutexの操作対象はデバイス(の割込)であって、割込ハンドラとサービススレッドとの 間にあるリソースでもありません。 スレッド間の排他制御に用いられる「一般的な」優先度継承付きmutexと混同していませんか?
858 名前:824 mailto:sage [2009/09/09(水) 02:22:51 ] >>854 たとえコンテクストスイッチしたとしても、その間に再度ハード割り込みが 次々に入ったらどうなりますか?それを避けるのが割禁の役目だと思います。 >>855 「割り込みスレッド」というブラックボックスな言葉を鵜呑みにしていませんか? そのwikipediaからの引用部分の前段には、割り込みハンドラが第1レベルと 第2レベルの2つの部分に別れているとあります。Solarisの場合であれば、 カーネル(デバイス)内にあるのが第1レベルであり、その「実行中は割禁」される。 また、デベロッパが作る割り込みハンドラが第2レベルであり、 一般的には「割り込みスレッド」と呼ばれている、と自分は解釈しています。 結局、割り込みスレッド方式であっても「デバイスドライバにおいては、 割禁は避けられない」と思うのですが、間違っていますか? Solarisの場合には、特殊なデバイスmutexによって割禁制御を抽象化しているから、 デベロッパは割禁を意識せずにデバドラを開発できる、という説明なら理解できますが。
859 名前:デフォルトの名無しさん mailto:sage [2009/09/09(水) 03:16:16 ] うーん、なんか論点がずれてきているような… > ドライバ開発で、割り込みハンドラでさわるリソースが、 > 通常コンテキストで触るリソースと競合するなら、 > 割り込み禁止以外ないと思ってたけど、そうでない > 一般的なやり方があるなら教えてほしいのぉ。 ってのに対して「少なくともSolarisは違うよ」と言いたいだけなんだけど… >>857 > たとえコンテクストスイッチしたとしても、その間に再度ハード割り込みが > 次々に入ったらどうなりますか?それを避けるのが割禁の役目だと思います。 たとえば、ディスクIOに関する割り込み処理を行なっているときに クロック割り込みが発生したら、クロックの処理を優先させるべきだからそっちの割り込み処理を先におこなう。 でも、クロック割り込みを処理しているときにディスク割り込みが発生しても、ディスク割り込みの処理は後回しにされる。 そういう制御を行なうために「割り込みレベル」ってのはある。 # これは「割り込みスレッドの優先度」とは違うもの。 でも、これはリソースの排他のためのものじゃないよね。 > そのwikipediaからの引用部分の前段には、割り込みハンドラが第1レベルと > 第2レベルの2つの部分に別れているとあります。Solarisの場合であれば、 > カーネル(デバイス)内にあるのが第1レベルであり、その「実行中は割禁」される。 > また、デベロッパが作る割り込みハンドラが第2レベルであり、 > 一般的には「割り込みスレッド」と呼ばれている、と自分は解釈しています。 第2レベルもカーネル内にあるものだよ。 というか、Solarisでは第1レベルと第2レベルが明確に分かれておらず、 割り込みハンドラは、最初のうちは割り込んだスレッドのコンテキストを「借りて」処理をおこなう。 で、mutexでブロックすることになった時点で、完全なカーネルスレッドとして設定され、コンテキストスイッチが起こるの。 ただ、このとき優先度逆転現象が起こって競合したmutexがいつまで経っても開放されなかったらマズいので、優先度継承の仕組みが働くってこと。
860 名前:デフォルトの名無しさん mailto:sage [2009/09/09(水) 03:47:59 ] 調停スレッドを置く形態だとCASは別に必須じゃない。普通にwikipediaの記事が間違ってる。
861 名前:デフォルトの名無しさん mailto:sage [2009/09/09(水) 04:17:44 ] なんだいきなり
862 名前:デフォルトの名無しさん mailto:sage [2009/09/09(水) 10:18:21 ] >>825 スレッドプールとの何がちがうのかわからない・・・教えてエロイ人!
863 名前:デフォルトの名無しさん mailto:sage [2009/09/09(水) 10:30:12 ] 記事を斜め読みしただけだけど、 ブロックの中から外側のローカル変数を見れるようになってるっぽいのは便利そう どういう扱いなのかしらんけど
864 名前:デフォルトの名無しさん mailto:sage [2009/09/09(水) 11:03:00 ] >>862 スレッドプール+タスクキューってのは定番の仕組み(たとえばJavaのThreadPoolExecutorとか)だけど、 それを利用しやすくするためにClosureを言語拡張として用意したのは凄い。
865 名前:デフォルトの名無しさん mailto:sage [2009/09/09(水) 11:07:29 ] 昔俺が作ったモニタだとハードウェア割り込みが入った ら別の割り込みで失われる情報(割り込み要因とか)だ け保存してまず enable interrupt してたな。 >>859 のいうようなレベル設定はハードウェアで実施さ れてた。だから自動的に高いレベルのものが順次受け付 け可能だったよ。 >>857 は「じゃあ同じ要因の割り込みが割り込み処理中 に発生したら?」と思うかも知れないが、それはそもそも 処理が間に合ってないってことだ。
866 名前:デフォルトの名無しさん mailto:sage [2009/09/09(水) 11:09:11 ] > ブロックの中から外側のローカル変数を見れるようになってるっぽいのは便利そう ていうかクロージャーってのは基本的にそういうもん。
867 名前:801 mailto:sage [2009/09/09(水) 15:00:41 ] すまないですがデバイスドライバとかそういうのに限定した話ではないので 割り禁に相当するものはないという前提でおねがいします。 関係ないけど割り金って書くと玉がキュッとなるよね
868 名前:デフォルトの名無しさん mailto:sage [2009/09/09(水) 17:55:18 ] >>862 スレッドプールを使いこなせている人にはあんまり関係ない気もするが、 初めてマルチスレッドに触れる人にとっては敷居が低いと思うの
869 名前:デフォルトの名無しさん mailto:sage [2009/09/09(水) 17:57:37 ] >>867 >関係ないけど割り金って書くと玉がキュッとなるよね 女の子だからわからないわ
870 名前:デフォルトの名無しさん mailto:sage [2009/09/09(水) 20:24:24 ] > 昔俺が作ったモニタだとハードウェア割り込みが入った > ら別の割り込みで失われる情報(割り込み要因とか)だ > け保存してまず enable interrupt してたな。 ふつーのOSは、割り込みを発生させた割り込みだけ禁止して 他の割り込みを許可して割り込みハンドラをよびだすよね。 だから当然他の割り込みは受付可能。ハンドラ呼び出す前に 発生させた割り込みまで許可するような変態^H^H面倒くさい 実装はゆるさん。 > >>857 は「じゃあ同じ要因の割り込みが割り込み処理中 > に発生したら?」と思うかも知れないが、それはそもそも > 処理が間に合ってないってことだ。 間に合ってないっつーか、割り込みコンテキストで割り込みを 発生させた要因をクリアもしくは禁止しないで、割り込み コンテキスト抜けたら無限に割り込み入り続けるっつーことを 言ってるだけなんじゃね(レベル割り込みなら)
871 名前:デフォルトの名無しさん mailto:sage [2009/09/10(木) 00:10:55 ] 言語本ではなくマルチスレッドプログラミングの基本を概念からわかりやすくまとめた本があれば売れると思う
872 名前:デフォルトの名無しさん mailto:sage [2009/09/10(木) 00:11:47 ] >>871 自作OS入門読めば解るだろ
873 名前:デフォルトの名無しさん mailto:sage [2009/09/10(木) 16:04:04 ] >>871 Win32マルチスレッドプログラミングがオススメ コードはwindowsのapi使ってるけどスレッドの概念とか使い方とかわかりやすく書いてる まぁ絶版になってるみたいだけどね
874 名前:デフォルトの名無しさん mailto:sage [2009/09/10(木) 16:41:09 ] >>871 「Java並行処理プログラミング」がオススメ。 絶讃している人も多い。 d.hatena.ne.jp/higepon/20090326/1238067284 入手が非常に困難だけど、復刊交渉も始まったらしいので、 興味があったら投票よろしく。 www.fukkan.com/fk/VoteDetail?no=46255 blog.book-ing.co.jp/fukkanrepo/2009/09/post-76fd.html
875 名前:デフォルトの名無しさん mailto:sage [2009/09/10(木) 17:04:39 ] >>870 > ハンドラ呼び出す前に 発生させた割り込みまで許可 > するような変態^H^H面倒くさい実装 これ、割り込みコントローラがカスケード接続されてる 場合は要るかも。 玄関から割り込みを受け付けて「本当は誰だったか」を 調べて、そのハンドラを呼び出す前に他のハンドラの為 にもういっぺん玄関を開けたりとか。 結局「本当の誰か」に EOI 発行するまでその人からの 再度の割り込みは来ないからマクロに見れば同じだけど、 ハンドラ実行から後を別スレッドでやるように考えれば 割とスマートなんじゃないだろうか。
876 名前:デフォルトの名無しさん mailto:sage [2009/09/10(木) 23:01:47 ] >>874 Java向けの本ではあるけど、確かにお薦めだと思う。
877 名前:デフォルトの名無しさん mailto:sage [2009/09/11(金) 18:27:02 ] >>871 言語に依存しない入門書は確かに欲しい 各言語での扱いは巻末にちょろっと載ってるぐらいでイナフ
878 名前:824 mailto:sage [2009/09/11(金) 22:29:34 ] >>859 遅レスになりましたが、頭を冷やして考えまてみました。 >でも、これはリソースの排他のためのものじゃないよね。 古典的なOSでは、割り込みコンテクストとサービススレッドとの間にある リソースの排他のために、割り込み禁止、あるいは割り込みレベルを(一時的に)上げることで 実現していたけど、「少なくともSolarisは違うよ」ということですね.... >第2レベルもカーネル内にあるものだよ。 より正確には、カーネル空間内にある、ですよね。 >割り込みハンドラは、最初のうちは割り込んだスレッドのコンテキストを「借りて」処理をおこなう。 >で、mutexでブロックすることになった時点で、完全なカーネルスレッドとして設定され、 >コンテキストスイッチが起こるの。 つまり、通常は(競合が発生しない場合は)、割り込みコンテクストとしてハンドラは動き、 その間は割り込みレベルを上げる事で、(同じレベルでの)新たなハード割り込みの発生を防ぐ。 ただし、競合が発生すると(mutexでブロックされると)、割り込みコンテクストは終了して(リターンして)、 カーネルスレッドへのコンテクストスイッチが発生し、残りの処理を継続するという訳ですね。 これなら割り込みコンテクスト内での処理オーバヘッドを最小限に押さえつつ、 同時に、ハンドラとサービススレッド間のリソース排他を実現できるような気がします。 STRAMSのput&srvエントリと同じ考え方ですね。 詳しい解説、ありがとうございました。よい勉強をさせてもらうことができました。 自分としては納得できたので、このデバイスドライバ実装に関する話題は、これで終わりにします。
879 名前:デフォルトの名無しさん mailto:sage [2009/09/12(土) 15:29:31 ] アトミック変数クラス(C++0xのstd::atomic<T>みたいなやつ)の ソースとか作り方ってどこかにあります?
880 名前:デフォルトの名無しさん mailto:sage [2009/09/12(土) 15:31:50 ] >>879 あるけど教えない
881 名前:デフォルトの名無しさん mailto:sage [2009/09/12(土) 15:48:03 ] >>879 もしWindows使いなら、Win32APIのInterlockedIncrementなど、Interlockedから始まるAPIの使い方を調べればよい。
882 名前:デフォルトの名無しさん mailto:sage [2009/09/12(土) 15:56:27 ] 「ソースとか作り方」って見た時、 一瞬、lock + cmpxchg とか lock + xadd のことかと思ってしまった。 そんなわけないよな。クラスって書いてるし。
883 名前:879 mailto:sage [2009/09/12(土) 20:37:05 ] >>881 >>882 大体予想はつくし実の所もう作ってはあるんだけど、 キャッシュの整合性とか色々考え出すと生半可な知識ではどうしようもないので、 既存の検証された実装があればいいなと思って。
884 名前:デフォルトの名無しさん mailto:sage [2009/09/12(土) 20:55:54 ] ttp://www.dre.vanderbilt.edu/Doxygen/5.6.6/html/ace/a00029.html
885 名前:デフォルトの名無しさん [2009/09/13(日) 02:31:59 ] Art Of MultiProcessor本の10章 ABA問題Pragma10.6.1のところで CompareAndSet(T expectedReference, T newReference, int expectedStamp, int newStamp) ってなメソッドが定義されてる。 これってDCASっぽいのだが 質問は説明の後半 「C/C++でやるなら64bitアーキテクチャなら"stealing"bits from pointer, 32bitアーキテクチャでも間接参照?でできる(although a 32-bit architecture woild probably require a level of indirection.)」 ってあるがどうやるんだ? わかる人、教えて。
886 名前:デフォルトの名無しさん [2009/09/13(日) 02:51:31 ] 上の質問の追記 ReferenceとStampがどっちも小さい値なら 例えばintegerの上と下で16bitづつつかって maskで値を取り出すとか工夫できるだろうけど、 例えば32bitマシンで referenceもstampもどっちも32bitの値を使う場合にどうするんだろう ってのが質問。
887 名前:デフォルトの名無しさん mailto:sage [2009/09/13(日) 09:02:36 ] そりゃペアで保持するオブジェクトの領域作って そこへの参照をCompareAndSetするんじゃないの?
888 名前:デフォルトの名無しさん [2009/09/13(日) 11:05:39 ] >>887 cmpxchg使ったことある? 質問変えると x86、Cでcmpxchgでどう書くか
889 名前:デフォルトの名無しさん mailto:sage [2009/09/13(日) 11:18:48 ] 俺も普通に>>887 としか思えないし>>885 の英文もそう読めるしCASも理解してるが
890 名前:デフォルトの名無しさん [2009/09/13(日) 11:34:02 ] 俺わかんねorz 具体的にCとgasで疑似サンプルコード書いてもらえると助かる
891 名前:デフォルトの名無しさん mailto:sage [2009/09/13(日) 11:58:41 ] >>890 金かかるよ? 高いよ? いいの?
892 名前:デフォルトの名無しさん mailto:sage [2009/09/13(日) 12:01:27 ] いいよ。>>890 にツケとけ。
893 名前:デフォルトの名無しさん mailto:sage [2009/09/13(日) 12:36:05 ] みんな ほんとにできんの? 64ビットと32ビットのアーキテクチャのアプローチの違いもいわず ただ「オブジェクトつくって...」って 分かった気しただけで実装したことないんちゃうか cmpxchg使うんだよ
894 名前:デフォルトの名無しさん mailto:sage [2009/09/13(日) 12:46:12 ] >>893 cmpxchg直接使うってバカか勉強中の学生以外 使わないと思うけど? キミいくつなの?レベル低すぎなんだよね〜
895 名前:デフォルトの名無しさん mailto:sage [2009/09/13(日) 14:31:59 ] x86前提ならそもそも64ビットCAS命令がネイティブでなかったっけ?
896 名前:デフォルトの名無しさん mailto:sage [2009/09/13(日) 17:11:34 ] >>x86前提ならそもそも64ビットCAS命令がネイティブでなかったっけ? 元の質問読んだ? DCASっぽい動きをCAS命令でやるのってどうやるのって話。 誰も64ビット版ではstealing bits、 32ビット版ではindirectionの意味も語らず、 厨房よばわりしてるけど 誰も原著を読み込んでないし実装もできないじゃないか >>cmpxchg直接使うってバカか勉強中の学生以外 >>使わないと思うけど? >>キミいくつなの?レベル低すぎなんだよね〜 ようするに出来ないんだろ
897 名前:デフォルトの名無しさん [2009/09/13(日) 17:19:40 ] >>894 このメソッド、 x86の32ビットと64ビットの両アーキテクチャ上で Cで実装してみ? cmpxchg使わないでw public boolean compareAndSet(V expectedReference, V newReference, boolean expectedMark, boolean newMark) 「現在の参照 == 予想される参照」であり、現在のマークが予想されるマークに等しい場合、参照およびマークの値を指定された更新値に原子的に設定します。 パラメータ: expectedReference - 参照の予想される値 newReference - 参照の新しい値 expectedMark - マークの予想される値 newMark - マークの新しい値 戻り値: 成功した場合は true
898 名前:デフォルトの名無しさん [2009/09/13(日) 17:20:42 ] IntelのTBBってどうですか 使える? これから主流になる?
899 名前:デフォルトの名無しさん mailto:sage [2009/09/13(日) 17:28:52 ] 一部はC++0xに取り込まれるので、やっておいて損はないと思う。
900 名前:デフォルトの名無しさん mailto:sage [2009/09/13(日) 17:35:48 ] >>898 TBB使ったアルゴリズムが100%正しいのか 検証する手段がないから危険で使えないよ?
901 名前:デフォルトの名無しさん mailto:sage [2009/09/13(日) 17:42:44 ] 原子的に、って訳は正直どうかと思う
902 名前:デフォルトの名無しさん [2009/09/13(日) 18:51:04 ] >>896 つttp://www.nagi.org/diary/?date=20090614#p01
903 名前:デフォルトの名無しさん mailto:sage [2009/09/13(日) 18:55:38 ] 原子的に quark的に
904 名前:デフォルトの名無しさん mailto:sage [2009/09/13(日) 19:40:00 ] 不可分的に
905 名前:デフォルトの名無しさん [2009/09/13(日) 21:07:54 ] 結局、ここの連中は自分じゃコードが書けない 知ったかばっかりなんだな
906 名前:デフォルトの名無しさん mailto:sage [2009/09/13(日) 21:14:41 ] >>905 まず金を払え 話はそれからだ
907 名前:デフォルトの名無しさん [2009/09/13(日) 21:31:47 ] >>906 馬鹿らしいが相手にしてやると サンプルコードも書けない奴に先払いするやつなんていない。 slealing bitsってどういう意味だ? せめてこれの意味くらいきっちり説明してみろ。 全然わかってねえじゃねえか、このスレの連中w
908 名前:デフォルトの名無しさん mailto:sage [2009/09/13(日) 21:58:35 ] >>907 キミってその解ってないと卑下する連中と同列なんじゃないの? ちなみに、煽れば回答でてくると思ってるでしょ?
909 名前:デフォルトの名無しさん mailto:sage [2009/09/13(日) 22:15:16 ] >>907 キミってこんなのも書けないの? template<typename T> bool compareAndSet(T* const expPtr, T* const newPtr, const uint32_t expStamp, const uint32_t newStamp) { bool r = false; if(v == expPtr && s == expStamp) { TAS.lock(); if(v == expPtr && s == expStamp) { v = newPtr; s = newStamp; r = true; } TAS.unlock(); } return r; } TASぐらい実装できるよね?そこまでバカじゃないよね? Javaの実装とVCかgccのcompareAndSwapのインラインアセンブラ 組み合わせれば実装ぐらいできるでしょ?それもできないってレベル低すぎるでしょ? キミだめだわ
910 名前:デフォルトの名無しさん [2009/09/13(日) 22:47:13 ] えーとだな、 そもそもの質問はArtOfMultiProcessor本の Lock-Free Queueの実装なんで おお威張りでlock()使われると 失笑するしかないんだがなw 君は本当の馬鹿なんだね
911 名前:デフォルトの名無しさん [2009/09/13(日) 22:51:26 ] >>ちなみに、煽れば回答でてくると思ってるでしょ? 902に回答出てるじゃん。 できる人は出し惜しみしないで公開してるし回答もできるだろう。 ここに張りついてる連中は揚げ足とりばっかで 質問の本質も理解できない知ったかばっか
912 名前:デフォルトの名無しさん mailto:sage [2009/09/13(日) 22:53:20 ] >>887 で終わってんのに何で続いてんの? >誰も64ビット版ではstealing bits、 >32ビット版ではindirectionの意味も語らず、 当たり前すぎて説明する必要があるとも思わんわ。 64ビットではポインタのビットから一部拝借してスタンプに使う。 32ビットではポインタとスタンプをまとめて直接ではなく、 ペアの領域を作成してそのポインタで間接的にCASを行う。
913 名前:デフォルトの名無しさん mailto:sage [2009/09/13(日) 23:08:05 ] >>912 uint64_t expV = ((((uint64_t)expPtr) << 32) | ((uint64_t)expStamp)); uint64_t newV = ((((uint64_t)newPtr) << 32) | ((uint64_t)newStamp)); return (expV == CAS64(&ptr, expV, newV)); これでOK?
914 名前:デフォルトの名無しさん [2009/09/14(月) 00:17:59 ] >> 887で終わってんのに何で続いてんの? 909みたいなのが質問者を馬鹿にしたんで荒れたんじゃないの。 lock使ってCASを実装するような奴が威張り散らしてるのみて爆笑させてもらったよ。 いずれにしても知ったかが多いのが明らかになったのはよかったんじゃない。 >>912 当り前すぎたとしても、質問者みたいな初心者もいるから最初から説明してあげれば。
915 名前:デフォルトの名無しさん mailto:sage [2009/09/14(月) 00:39:43 ] どう見ても、キミが質問者で 煽って回答を引き出してるだけに見えるけど。 週末だしね。
916 名前:デフォルトの名無しさん mailto:sage [2009/09/14(月) 00:56:51 ] >>913 uint64_t expV = ((((uint64_t)&expPtr) << 32) | ((uint64_t)expStamp)); uint64_t newV = ((((uint64_t)&newPtr) << 32) | ((uint64_t)newStamp));
917 名前:デフォルトの名無しさん mailto:sage [2009/09/14(月) 01:00:27 ] >>894 >>908 >>909 >>915 煽ってる同一人物なんで 無視の方向で
918 名前:デフォルトの名無しさん mailto:sage [2009/09/14(月) 04:16:34 ] 火元の人 やり方が理解できない質問者 俺に分からないならこのスレにも理解できる奴いないんじゃね、とか思っていて、 それが態度にも滲み出ている 煽る人 分かってるつもりだけど分かってないで煽り続ける こいつを見た火元は「やっぱり分かってる奴いないんじゃないか」と思いこむ 住人タイプA 一目で分かるがお前の態度が気に入らないしコード示すのマンドクセ つーかこの説明で分かれボユゲ 住人タイプB みんな何言ってんだかわかんね