በዩኒክስ ውስጥ የቧንቧ መስመሮች እንዴት እንደሚተገበሩ

በዩኒክስ ውስጥ የቧንቧ መስመሮች እንዴት እንደሚተገበሩ
ይህ ጽሑፍ በዩኒክስ ከርነል ውስጥ የቧንቧ መስመሮችን ትግበራ ይገልጻል. " በሚል ርዕስ በቅርቡ የወጣ መጣጥፍ በተወሰነ መልኩ አዝኛለሁ።የቧንቧ መስመሮች በዩኒክስ ውስጥ እንዴት ይሠራሉ?" ሆነ አይደለም ስለ ውስጣዊ መዋቅር. ለማወቅ ጓጉቼ መልሱን ለማግኘት ወደ አሮጌ ምንጮች ገባሁ።

ስለ ምን እያወራን ነው?

የቧንቧ መስመሮች "ምናልባት በዩኒክስ ውስጥ በጣም አስፈላጊው ፈጠራ" ናቸው - የዩኒክስ መሰረታዊ ትንንሽ ፕሮግራሞችን የማዋሃድ ፍልስፍና እና የሚታወቀው የትእዛዝ መስመር መፈክር፡-

$ echo hello | wc -c
6

ይህ ተግባር በከርነል የቀረበው የስርዓት ጥሪ ላይ ይወሰናል pipeበሰነድ ገፆች ላይ የተገለጸው ቧንቧ (7) и ቧንቧ (2):

የቧንቧ መስመሮች ለሂደት ግንኙነት የአንድ መንገድ ቻናል ይሰጣሉ። የቧንቧ መስመር ግቤት (የፅሁፍ መጨረሻ) እና ውፅዓት (የተነባቢ መጨረሻ) አለው. በቧንቧው ግቤት ላይ የተፃፈው መረጃ በውጤቱ ላይ ሊነበብ ይችላል.

የቧንቧ መስመር የሚፈጠረው በመደወል ነው pipe(2), ሁለት የፋይል ገላጭዎችን ይመልሳል-አንደኛው የቧንቧ መስመር ግቤትን ያመለክታል, ሁለተኛው ደግሞ ውጤቱን ያመለክታል.

ከላይ ከተጠቀሰው ትዕዛዝ የተገኘው የክትትል ውጤት የቧንቧ መስመር መፈጠርን እና የውሂብ ፍሰት ከአንድ ሂደት ወደ ሌላ ያሳያል.

$ strace -qf -e execve,pipe,dup2,read,write 
    sh -c 'echo hello | wc -c'

execve("/bin/sh", ["sh", "-c", "echo hello | wc -c"], …)
pipe([3, 4])                            = 0
[pid 2604795] dup2(4, 1)                = 1
[pid 2604795] write(1, "hellon", 6)    = 6
[pid 2604796] dup2(3, 0)                = 0
[pid 2604796] execve("/usr/bin/wc", ["wc", "-c"], …)
[pid 2604796] read(0, "hellon", 16384) = 6
[pid 2604796] write(1, "6n", 2)        = 2

የወላጅ ሂደት ይጠራል pipe()የተያያዙ የፋይል ገላጭዎችን ለማግኘት. አንድ ልጅ ሂደት ለአንድ ገላጭ ይጽፋል እና ሌላ ሂደት ከሌላ ገላጭ ተመሳሳይ ውሂብ ያነባል። ዛጎሉ ከ stdin እና stdout ጋር ለማዛመድ 2 እና 3 ገላጮችን ከ dup4 ጋር "እንደገና ይሰይማል"።

የቧንቧ መስመሮች ከሌለ, ዛጎሉ ከፋይሉ ውስጥ ያለውን መረጃ ለማንበብ የአንዱን ሂደት ውጤት በፋይል ላይ መጻፍ እና ወደ ሌላ ሂደት ውስጥ ማስገባት ይኖርበታል. በውጤቱም, ተጨማሪ ሀብቶችን እና የዲስክ ቦታን እናባክናለን. ነገር ግን የቧንቧ መስመሮች ጊዜያዊ ፋይሎችን ከማስወገድ የበለጠ ጠቃሚ ናቸው፡-

ሂደቱ ከባዶ የቧንቧ መስመር ለማንበብ ከሞከረ, ከዚያ read(2) ውሂቡ እስኪገኝ ድረስ ይዘጋል። አንድ ሂደት ወደ ሙሉ የቧንቧ መስመር ለመጻፍ ከሞከረ, ከዚያ write(2) ጽሑፉን ለማጠናቀቅ በቂ መረጃ ከቧንቧው እስኪነበብ ድረስ ይዘጋል።

ልክ እንደ POSIX መስፈርት፣ ይህ አስፈላጊ ንብረት ነው፡ እስከ ቧንቧው ድረስ መጻፍ PIPE_BUF ባይት (ቢያንስ 512) አቶሚክ መሆን አለባቸው ስለዚህ ሂደቶች በቧንቧው በኩል እርስ በርስ ሊግባቡ በሚችሉበት መንገድ መደበኛ ፋይሎች (እንደነዚህ አይነት ዋስትናዎች የማይሰጡ)።

በመደበኛ ፋይል አንድ ሂደት ሁሉንም ውጤቶቹን ወደ እሱ ይጽፋል እና ወደ ሌላ ሂደት ያስተላልፋል። ወይም ሂደቶች በጠንካራ ትይዩ ሞድ ውስጥ ሊሰሩ ይችላሉ፣ የውጭ ምልክት ማድረጊያ ዘዴን (እንደ ሴማፎር) በመጠቀም ስለመፃፍ ወይም ስለ ማንበብ መጠናቀቅ እርስ በእርስ ለማሳወቅ። ማጓጓዣዎች ከዚህ ሁሉ ችግር ያድነናል።

ምን እየፈለግን ነው?

ማጓጓዣ እንዴት እንደሚሰራ ለመገመት ቀላል እንዲሆንልዎ በጣቶቼ ላይ እገልጻለሁ. በማህደረ ትውስታ ውስጥ ቋት እና የተወሰነ ሁኔታ መመደብ ያስፈልግዎታል። ውሂብን ከመጠባበቂያው ውስጥ ለማከል እና ለማስወገድ ተግባራት ያስፈልግዎታል። በፋይል ገላጭዎች ላይ በንባብ እና በመፃፍ ጊዜ ተግባራትን ለመጥራት አንዳንድ መገልገያ ያስፈልግዎታል። እና ከላይ የተገለፀውን ልዩ ባህሪ ለመተግበር መቆለፊያዎች ያስፈልጋሉ.

ግልጽ ያልሆነውን የአዕምሮ ሞዴላችንን ለማረጋገጥ ወይም ውድቅ ለማድረግ አሁን የከርነሉን ምንጭ ኮድ በደማቅ መብራት መብራት ለመጠየቅ ዝግጁ ነን። ግን ሁል ጊዜ ለማይጠበቁ ነገሮች ዝግጁ ይሁኑ።

የት ነው የምንፈልገው?

የታዋቂው መጽሐፍ ቅጂዬ የት እንዳለ አላውቅም።አንበሶች መጽሐፍ« ከዩኒክስ 6 ምንጭ ኮድ ጋር፣ ግን አመሰግናለሁ የዩኒክስ ቅርስ ማህበር በመስመር ላይ መፈለግ ይቻላል ምንጭ ኮድ እንዲያውም የቆዩ የዩኒክስ ስሪቶች.

በTUHS ማህደር ውስጥ መዞር ሙዚየምን እንደመጎብኘት ነው። የጋራ ታሪካችንን ማየት እንችላለን እና እነዚህን ሁሉ ነገሮች ከአሮጌ ካሴቶች እና ህትመቶች በጥቂቱ ለመመለስ ላለፉት ዓመታት ጥረት አከብራለሁ። እና አሁንም የጠፉትን እነዚያን ቁርጥራጮች ጠንቅቄ አውቃለሁ።

ስለ ቧንቧው ጥንታዊ ታሪክ ያለንን የማወቅ ጉጉት ካሟላን, ለማነፃፀር ዘመናዊ ኮርሞችን መመልከት እንችላለን.

በነገራችን ላይ, pipe በሰንጠረዡ ውስጥ የስርዓት ጥሪ ቁጥር 42 ነው sysent[]. በአጋጣሚ?

ባህላዊ ዩኒክስ ከርነሎች (1970-1974)

ምንም ዱካ አላገኘሁም። pipe(2) ውስጥም አይገቡም። PDP-7 ዩኒክስ (ጥር 1970)፣ ወይም እ.ኤ.አ የመጀመሪያ እትም ዩኒክስ (ህዳር 1971)፣ ወይም ባልተጠናቀቀ የምንጭ ኮድ ውስጥ ሁለተኛ እትም (ሰኔ 1972)

TUHS እንዲህ ይላል። ሦስተኛው እትም ዩኒክስ (የካቲት 1973) የቧንቧ መስመር ያለው የመጀመሪያው እትም ነበር፡-

ሦስተኛው የዩኒክስ እትም የመጨረሻው እትም በሰብሳቢ ውስጥ የተፃፈ ከርነል ፣ ግን ደግሞ ከቧንቧ መስመር ጋር የመጀመሪያው ስሪት ነው። እ.ኤ.አ. በ 1973 ፣ ሦስተኛውን እትም ለማሻሻል ሥራ በመካሄድ ላይ ነበር ፣ ኮርነሉ በ C እንደገና ተፃፈ ፣ እናም አራተኛው የዩኒክስ እትም ተወለደ።

አንድ አንባቢ ዶግ ማኪልሮይ "ፕሮግራሞችን እንደ የአትክልት ቱቦ ማገናኘት" የሚለውን ሀሳብ ያቀረበበትን ሰነድ ስካን አግኝቷል.

በዩኒክስ ውስጥ የቧንቧ መስመሮች እንዴት እንደሚተገበሩ
በብሪያን ከርኒግሃን መጽሐፍ ውስጥዩኒክስ፡ ታሪክ እና ማስታወሻ”፣ የእቃ ማጓጓዣዎች ገጽታ ታሪክም ይህንን ሰነድ ይጠቅሳል፡- “... በቤል ላብስ ቢሮዬ ውስጥ ግድግዳ ላይ ለ30 ዓመታት ተሰቅሏል። እዚህ ከማክሊሮይ ጋር የተደረገ ቃለ ምልልስእና ሌላ ታሪክ ከ የማክሊሮይ ሥራ፣ በ2014 የተጻፈ:

ዩኒክስ ብቅ ሲል፣ ለኮሮቲኖች ያለኝ ፍቅር የስርዓተ ክወናውን ደራሲ ኬን ቶምፕሰንን በአንድ ሂደት ላይ የተፃፈውን መረጃ ወደ መሳሪያው ብቻ ሳይሆን ወደ ሌላ ሂደት መውጣቱንም እንድፈቅድ እንድጠይቅ አድርጎኛል። ኬን የሚቻል መስሎት ነበር። ሆኖም ፣ እንደ ዝቅተኛነት ፣ እያንዳንዱ የስርዓት ባህሪ ጉልህ ሚና እንዲጫወት ይፈልጋል። በሂደቶች መካከል በቀጥታ መፃፍ በእውነቱ ወደ መካከለኛ ፋይል ከመፃፍ ትልቅ ጥቅም ነውን? እና በሚስብ ስም "ቧንቧ" እና የሂደቶች መስተጋብር አገባብ መግለጫ ጋር አንድ የተወሰነ ሀሳብ ሳቀርብ ብቻ ኬን በመጨረሻ “አደርገዋለሁ!” ብሎ ጮኸ።

እና አደረገ። አንድ አስደሳች ምሽት ኬን ከርነል እና ሼል ለውጦ፣ ግብአት እንዴት እንደሚቀበሉ (ከቧንቧ መስመር ሊመጣ ይችላል) እና የፋይል ስሞችን ለመቀየር በርካታ መደበኛ ፕሮግራሞችን አስተካክሏል። በሚቀጥለው ቀን የቧንቧ መስመሮች በመተግበሪያዎች ውስጥ በጣም በሰፊው ጥቅም ላይ ውለዋል. በሳምንቱ መገባደጃ ላይ ጸሐፊዎቹ ሰነዶችን ከቃል አቀናባሪ ወደ አታሚው ለመላክ ተጠቅመውባቸዋል። ከተወሰነ ጊዜ በኋላ፣ ኬን የቧንቧ መስመር አጠቃቀምን ከዚያን ጊዜ ጀምሮ ጥቅም ላይ በዋሉት የንፁህ ስምምነቶች ለመጠቅለል ዋናውን ኤፒአይ እና አገባብ ተክቷል።

እንደ አለመታደል ሆኖ የሶስተኛው እትም ዩኒክስ ከርነል ምንጭ ኮድ ጠፍቷል። እና ምንም እንኳን የከርነል ምንጭ ኮድ በሲ ውስጥ የተጻፈ ቢሆንም አራተኛ እትምበኖቬምበር 1973 የተለቀቀው ነገር ግን በይፋ ከመለቀቁ ከጥቂት ወራት በፊት የወጣው እና የቧንቧ መስመሮችን አተገባበር አያካትትም. የዚህ አፈ ታሪክ የዩኒክስ ባህሪ ምንጭ ኮድ መጥፋቱ ያሳዝናል ምናልባትም ለዘላለም።

የሰነድ ጽሑፍ አለን። pipe(2) ከሁለቱም የተለቀቁ, ስለዚህ ሰነዶቹን በመፈለግ መጀመር ይችላሉ ሦስተኛው እትም (ለተወሰኑ ቃላት፣ "በእጅ" የተሰመረበት፣ የ^H ቃል በቃል ሕብረቁምፊ ከስር አስምር!) ይህ ፕሮቶ-pipe(2) በአሰባሳቢ የተጻፈ እና አንድ ፋይል ገላጭ ብቻ ይመልሳል፣ ግን አስቀድሞ የሚጠበቀውን ዋና ተግባር ያቀርባል፡-

የስርዓት ጥሪ ቱቦ የቧንቧ መስመር የሚባል የ I/O ዘዴን ይፈጥራል። የተመለሰው ፋይል ገላጭ ለንባብ እና ለመፃፍ ስራ ላይ ሊውል ይችላል። አንድ ነገር ወደ ቧንቧው ሲጻፍ እስከ 504 ባይት ዳታ ይይዛል፣ ከዚያ በኋላ የአጻጻፍ ሂደቱ ታግዷል። ከቧንቧው ውስጥ በሚያነቡበት ጊዜ, የታሸገው መረጃ ይወሰዳል.

በሚቀጥለው ዓመት ከርነል በ C ውስጥ እንደገና ተጽፏል እና ቧንቧ (2) አራተኛ እትም ዘመናዊ መልክውን በምሳሌው አገኘ"pipe(fildes)"

የስርዓት ጥሪ ቱቦ የቧንቧ መስመር የሚባል የ I/O ዘዴ ይፈጥራል። የተመለሱት የፋይል ገላጭዎች በንባብ እና በመፃፍ ስራዎች ውስጥ ጥቅም ላይ ሊውሉ ይችላሉ. አንድ ነገር ወደ ቧንቧው ሲጻፍ፣ ገላጭው በ r1 (resp. fildes [1]) ተመልሶ እስከ 4096 ባይት ውሂብ ቋት ይጠቀማል፣ ከዚያ በኋላ የመጻፍ ሂደቱ ታግዷል። ከቧንቧው ላይ በሚያነቡበት ጊዜ ገላጭ ወደ r0 (resp. fildes [0]) ተመለሰ ውሂቡን ይወስዳል.

አንድ ጊዜ የቧንቧ መስመር ከተገለጸ በኋላ ሁለት (ወይም ከዚያ በላይ) መስተጋብር ሂደቶች (በቀጣይ ጥሪዎች የተፈጠሩ) እንደሆኑ ይታሰባል. ራፍ) ጥሪዎችን በመጠቀም ከቧንቧው ላይ መረጃን ያስተላልፋል ያንብቡ и ጻፈ.

ዛጎሉ በቧንቧ መስመር በኩል የተገናኙትን የመስመራዊ ሂደቶችን ለመወሰን አገባብ አለው።

አንድ ጫፍ ብቻ ካለው ከባዶ የቧንቧ መስመር (ምንም የታሸገ መረጃ የሌለው) ለማንበብ የሚደረጉ ጥሪዎች (ሁሉም የፋይል ገላጭ ተዘግተዋል) "የፋይል መጨረሻ" ይመልሳሉ። በተመሳሳይ ሁኔታ ጥሪዎችን ይፃፉ ችላ ይባላሉ።

መጀመሪያ የተጠበቀ የቧንቧ መስመር ትግበራ ያመለክታል ወደ ዩኒክስ አምስተኛ እትም (ሰኔ 1974)፣ ግን በሚቀጥለው እትም ላይ ከሚታየው ጋር ተመሳሳይ ነው ማለት ይቻላል። የተጨመሩ አስተያየቶች ብቻ፣ ስለዚህ አምስተኛው እትም ሊዘለል ይችላል።

ዩኒክስ ስድስተኛ እትም (1975)

የዩኒክስ ምንጭ ኮድ ማንበብ በመጀመር ላይ ስድስተኛ እትም (ግንቦት 1975) በጣም አመሰግናለሁ አንበሶች ከቀደምት ስሪቶች ምንጮች ለማግኘት በጣም ቀላል ነው-

ለብዙ ዓመታት መጽሐፉ አንበሶች ከቤል ላብስ ውጪ በዩኒክስ ከርነል ላይ ያለው ብቸኛው ሰነድ ነበር። የስድስተኛው እትም ፈቃድ መምህራን የመነሻ ኮድ እንዲጠቀሙ ቢፈቅድም ሰባተኛው እትም ፈቃድ ግን ይህንን ዕድል ስላገለለ መጽሐፉ በሕገ-ወጥ የጽሕፈት መኪና ተሰራጭቷል።

ዛሬ የመጽሐፉን ድጋሚ የህትመት ቅጂ መግዛት ይችላሉ, ይህም ሽፋን ተማሪዎችን በኮፒው ላይ ያሳያል. እና እናመሰግናለን ዋረን ቶሜይ (የ TUHS ፕሮጄክትን የጀመረው)፣ ማውረድ ይችላሉ። ስድስተኛ እትም ምንጭ PDF. ፋይሉን ለመፍጠር ምን ያህል ጥረት እንደተደረገ ሀሳብ ልሰጥህ እፈልጋለሁ፡-

ከ15 ዓመታት በፊት፣ በ ውስጥ የቀረበውን የምንጭ ኮድ ቅጂ ጻፍኩ። አንበሶችምክንያቱም የእኔ ቅጂ ጥራት ከማይታወቁ ሌሎች ቅጂዎች አልወደድኩትም። TUHS እስካሁን አልነበረም፣ እና የድሮ ምንጮችን ማግኘት አልቻልኩም። ግን በ 1988 ከ PDP9 ኮምፒዩተር ምትኬ ያለው 11 ትራኮች ያለው አሮጌ ቴፕ አገኘሁ ። ይሠራ እንደሆነ ለማወቅ በጣም ከባድ ነበር፣ ነገር ግን አብዛኛዎቹ ፋይሎች በ1979 ምልክት የተደረገበት ያልተነካ/usr/src/ ዛፍ ነበር፣ ይህም አሁንም ጥንታዊ ይመስላል። እሱ ሰባተኛው እትም ነበር፣ ወይም PWB ተዋጽኦ፣ ብዬ አሰብኩ።

ግኝቱን እንደ መሰረት አድርጌ ወስጄ ምንጮቹን በእጅ ወደ ስድስተኛው እትም ሁኔታ አርትዕ አድርጌያለሁ። የኮዱ ክፍል አንድ አይነት ሆኖ ቀርቷል፣ ከፊሉ በትንሹ መታረም ነበረበት፣ ዘመናዊውን ማስመሰያ += ወደ ጊዜው ያለፈበት =+ በመቀየር። የሆነ ነገር በቀላሉ ተሰርዟል፣ እና የሆነ ነገር ሙሉ በሙሉ እንደገና መፃፍ ነበረበት፣ ግን በጣም ብዙ አይደለም።

እና ዛሬ በመስመር ላይ በ TUHS የስድስተኛው እትም ምንጭ ኮድ ማንበብ እንችላለን ዴኒስ ሪቺ እጅ ያለበት ማህደር.

በነገራችን ላይ በመጀመሪያ እይታ ፣ ከከርኒግሃን እና ከሪቺ ጊዜ በፊት የ C-code ዋና ባህሪ እሱ ነው። አጭር መግለጫ. በጣቢያዬ ላይ በአንፃራዊነት ጠባብ የማሳያ ቦታን ለመግጠም ሰፊ አርትዖት ሳላደርግ ቅንጣቢዎችን ማስገባት የቻልኩት ብዙ ጊዜ አይደለም።

በመጀመሪያ ላይ /usr/sys/ken/pipe.c ገላጭ አስተያየት አለ (እና አዎ፣ ተጨማሪ አለ። /usr/sys/dmr):

/*
 * Max allowable buffering per pipe.
 * This is also the max size of the
 * file created to implement the pipe.
 * If this size is bigger than 4096,
 * pipes will be implemented in LARG
 * files, which is probably not good.
 */
#define    PIPSIZ    4096

ከአራተኛው እትም ጀምሮ የመጠባበቂያው መጠን አልተለወጠም። ግን እዚህ እናያለን፣ ያለ ምንም ህዝባዊ ሰነድ፣ የቧንቧ መስመሮች በአንድ ወቅት ፋይሎችን እንደ የኋላ ማከማቻነት ይጠቀሙ ነበር!

እንደ LARG ፋይሎች፣ እነሱ ይዛመዳሉ inode-ባንዲራ LARGለማስኬድ በ"ትልቅ የአድራሻ አልጎሪዝም" ጥቅም ላይ ይውላል ቀጥተኛ ያልሆኑ ብሎኮች ትላልቅ የፋይል ስርዓቶችን ለመደገፍ. ኬን እነሱን አለመጠቀም የተሻለ እንደሆነ ስለተናገረ ቃሉን ለመቀበል ደስተኛ ነኝ።

ትክክለኛው የስርዓት ጥሪ እዚህ አለ። pipe:

/*
 * The sys-pipe entry.
 * Allocate an inode on the root device.
 * Allocate 2 file structures.
 * Put it all together with flags.
 */
pipe()
{
    register *ip, *rf, *wf;
    int r;

    ip = ialloc(rootdev);
    if(ip == NULL)
        return;
    rf = falloc();
    if(rf == NULL) {
        iput(ip);
        return;
    }
    r = u.u_ar0[R0];
    wf = falloc();
    if(wf == NULL) {
        rf->f_count = 0;
        u.u_ofile[r] = NULL;
        iput(ip);
        return;
    }
    u.u_ar0[R1] = u.u_ar0[R0]; /* wf's fd */
    u.u_ar0[R0] = r;           /* rf's fd */
    wf->f_flag = FWRITE|FPIPE;
    wf->f_inode = ip;
    rf->f_flag = FREAD|FPIPE;
    rf->f_inode = ip;
    ip->i_count = 2;
    ip->i_flag = IACC|IUPD;
    ip->i_mode = IALLOC;
}

አስተያየቱ እዚህ እየሆነ ያለውን ነገር በግልፅ ይገልጻል። ግን ኮዱን ለመረዳት ያን ያህል ቀላል አይደለም፣በከፊል ምክንያቱ እንዴት"የመዋቅር ተጠቃሚ u» እና ይመዘግባል R0 и R1 የስርዓት ጥሪ መለኪያዎች እና የመመለሻ ዋጋዎች ተላልፈዋል።

እስቲ እንሞክር ialloc() በዲስክ ላይ ያስቀምጡ ኢኖድ (ኢኖድ), እና በእርዳታ falloc() - ሁለት ያከማቹ ፋይል. ሁሉም ነገር ጥሩ ከሆነ፣ እነዚህን ፋይሎች እንደ የቧንቧ መስመር ሁለት ጫፎች ለመለየት ባንዲራዎችን እናስቀምጣለን፣ ወደ አንድ አይነት inode እንጠቁማቸዋለን (የማመሳከሪያው ቁጥራቸው 2 ይሆናል) እና ኢንዶው እንደተሻሻለ እና ጥቅም ላይ እንደሚውል ምልክት እናደርጋለን። ለጥያቄዎች ትኩረት ይስጡ አይፑት() በአዲሱ inode ውስጥ ያለውን የማጣቀሻ ብዛት ለመቀነስ በስህተት መንገዶች.

pipe() በኩል ምክንያት R0 и R1 ለማንበብ እና ለመጻፍ የፋይል ገላጭ ቁጥሮችን ይመልሱ. falloc() ጠቋሚውን ወደ የፋይል መዋቅር ይመልሳል፣ ግን ደግሞ በ በኩል "ይመለሳል።" u.u_ar0[R0] እና የፋይል ገላጭ. ያም ማለት, ኮዱ በ ውስጥ ተከማችቷል r የፋይል ገላጭ ለማንበብ እና በቀጥታ ከ ለመጻፍ ገላጭ ይመድባል u.u_ar0[R0] ከሁለተኛ ጥሪ በኋላ falloc().

ሰንደቅ FPIPE, የቧንቧ መስመርን በሚፈጥሩበት ጊዜ የምናስቀምጠው, የተግባሩን ባህሪ ይቆጣጠራል rdwr () በ sys2.cየተወሰኑ የI/O ልማዶችን የሚጠራው፡-

/*
 * common code for read and write calls:
 * check permissions, set base, count, and offset,
 * and switch out to readi, writei, or pipe code.
 */
rdwr(mode)
{
    register *fp, m;

    m = mode;
    fp = getf(u.u_ar0[R0]);
        /* … */

    if(fp->f_flag&FPIPE) {
        if(m==FREAD)
            readp(fp); else
            writep(fp);
    }
        /* … */
}

ከዚያ ተግባሩ readp() в pipe.c ከቧንቧው ውስጥ መረጃን ያነባል. ነገር ግን አተገባበሩን ከ ጀምሮ መከታተል የተሻለ ነው writep(). እንደገና፣ በክርክሩ ተፈጥሮ ምክንያት ኮዱ ይበልጥ የተወሳሰበ ሆኗል፣ ነገር ግን አንዳንድ ዝርዝሮች ሊቀሩ ይችላሉ።

writep(fp)
{
    register *rp, *ip, c;

    rp = fp;
    ip = rp->f_inode;
    c = u.u_count;

loop:
    /* If all done, return. */

    plock(ip);
    if(c == 0) {
        prele(ip);
        u.u_count = 0;
        return;
    }

    /*
     * If there are not both read and write sides of the
     * pipe active, return error and signal too.
     */

    if(ip->i_count < 2) {
        prele(ip);
        u.u_error = EPIPE;
        psignal(u.u_procp, SIGPIPE);
        return;
    }

    /*
     * If the pipe is full, wait for reads to deplete
     * and truncate it.
     */

    if(ip->i_size1 == PIPSIZ) {
        ip->i_mode =| IWRITE;
        prele(ip);
        sleep(ip+1, PPIPE);
        goto loop;
    }

    /* Write what is possible and loop back. */

    u.u_offset[0] = 0;
    u.u_offset[1] = ip->i_size1;
    u.u_count = min(c, PIPSIZ-u.u_offset[1]);
    c =- u.u_count;
    writei(ip);
    prele(ip);
    if(ip->i_mode&IREAD) {
        ip->i_mode =& ~IREAD;
        wakeup(ip+2);
    }
    goto loop;
}

ወደ ቧንቧው ግቤት ባይት መፃፍ እንፈልጋለን u.u_count. በመጀመሪያ ኢንዶውን መቆለፍ አለብን (ከዚህ በታች ይመልከቱ plock/prele).

ከዚያም የኢኖድ ማመሳከሪያውን ቁጥር እንፈትሻለን. የቧንቧው ሁለቱም ጫፎች ክፍት እስካሉ ድረስ ቆጣሪው 2 መሆን አለበት. ወደ አንድ ማገናኛ እንይዛለን (ከ. rp->f_inode), ስለዚህ ቆጣሪው ከ 2 ያነሰ ከሆነ, ይህ ማለት የንባብ ሂደቱ የቧንቧውን ጫፍ ዘግቷል ማለት ነው. በሌላ አነጋገር, ወደ ዝግ የቧንቧ መስመር ለመጻፍ እየሞከርን ነው, ይህም ስህተት ነው. የመጀመሪያው የስህተት ኮድ EPIPE እና ምልክት SIGPIPE በዩኒክስ ስድስተኛ እትም ላይ ታየ.

ነገር ግን ማጓጓዣው ክፍት ቢሆንም, ሙሉ ሊሆን ይችላል. በዚህ ሁኔታ, መቆለፊያውን እንለቃለን እና ሌላ ሂደት ከቧንቧው ውስጥ እንዲነበብ እና በውስጡ በቂ ቦታ እንዲለቀቅ ተስፋ በማድረግ እንተኛለን. ከእንቅልፋችን ስንነቃ ወደ መጀመሪያው እንመለሳለን, መቆለፊያውን እንደገና አንጠልጥለን እና አዲስ የመጻፍ ዑደት እንጀምራለን.

በቧንቧው ውስጥ በቂ ነፃ ቦታ ካለ, ከዚያም በመጠቀም ውሂብ እንጽፋለን መጻፍ(). መለኪያ i_size1 inode'a (ከባዶ የቧንቧ መስመር ጋር ከ 0 ጋር እኩል ሊሆን ይችላል) አስቀድሞ የያዘውን የውሂብ መጨረሻ ይጠቁማል. ለመጻፍ በቂ ቦታ ካለ, የቧንቧ መስመርን መሙላት እንችላለን i_size1 ወደ PIPESIZ. ከዚያም መቆለፊያውን እንለቅቃለን እና ከቧንቧው ለማንበብ የሚጠብቀውን ማንኛውንም ሂደት ለማንቃት እንሞክራለን. የምንፈልገውን ያህል ባይት መፃፍ እንደቻልን ለማየት ወደ መጀመሪያው እንመለሳለን። ካልሆነ አዲስ የመቅዳት ዑደት እንጀምራለን.

አብዛኛውን ጊዜ መለኪያ i_mode inode ፍቃዶችን ለማከማቸት ጥቅም ላይ ይውላል r, w и x. ነገር ግን የቧንቧ መስመሮችን በተመለከተ, አንዳንድ ሂደቶች ቢትስን በመጠቀም ለመጻፍ ወይም ለማንበብ እየጠበቁ መሆናቸውን እንጠቁማለን IREAD и IWRITE በቅደም ተከተል. ሂደቱ ባንዲራውን እና ጥሪዎችን ያዘጋጃል sleep()ወደፊትም ሌላ ሂደት እንደሚመጣ ይጠበቃል wakeup().

እውነተኛው አስማት በ ውስጥ ይከሰታል sleep() и wakeup(). ውስጥ ይተገበራሉ Slp.c, የታዋቂው "ይህን እንድትረዱት አይጠበቅም" አስተያየት ምንጭ. እንደ እድል ሆኖ, ኮዱን መረዳት የለብንም, አንዳንድ አስተያየቶችን ብቻ ይመልከቱ:

/*
 * Give up the processor till a wakeup occurs
 * on chan, at which time the process
 * enters the scheduling queue at priority pri.
 * The most important effect of pri is that when
 * pri<0 a signal cannot disturb the sleep;
 * if pri>=0 signals will be processed.
 * Callers of this routine must be prepared for
 * premature return, and check that the reason for
 * sleeping has gone away.
 */
sleep(chan, pri) /* … */

/*
 * Wake up all processes sleeping on chan.
 */
wakeup(chan) /* … */

የሚጠራው ሂደት sleep() ለአንድ የተወሰነ ሰርጥ በኋላ ላይ በሌላ ሂደት ሊነቃ ይችላል, እሱም ይጠራል wakeup() ለተመሳሳይ ቻናል. writep() и readp() እንደዚህ ባሉ የተጣመሩ ጥሪዎች ድርጊቶቻቸውን ያስተባብሩ። አስታውስ አትርሳ pipe.c ሁልጊዜ ቅድሚያ ይስጡ PPIPE ሲጠራ sleep(), ስለዚህ ሁሉም sleep() በምልክት ሊቋረጥ ይችላል.

አሁን ተግባሩን ለመረዳት ሁሉም ነገር አለን readp():

readp(fp)
int *fp;
{
    register *rp, *ip;

    rp = fp;
    ip = rp->f_inode;

loop:
    /* Very conservative locking. */

    plock(ip);

    /*
     * If the head (read) has caught up with
     * the tail (write), reset both to 0.
     */

    if(rp->f_offset[1] == ip->i_size1) {
        if(rp->f_offset[1] != 0) {
            rp->f_offset[1] = 0;
            ip->i_size1 = 0;
            if(ip->i_mode&IWRITE) {
                ip->i_mode =& ~IWRITE;
                wakeup(ip+1);
            }
        }

        /*
         * If there are not both reader and
         * writer active, return without
         * satisfying read.
         */

        prele(ip);
        if(ip->i_count < 2)
            return;
        ip->i_mode =| IREAD;
        sleep(ip+2, PPIPE);
        goto loop;
    }

    /* Read and return */

    u.u_offset[0] = 0;
    u.u_offset[1] = rp->f_offset[1];
    readi(ip);
    rp->f_offset[1] = u.u_offset[1];
    prele(ip);
}

ይህን ተግባር ከታች ወደ ላይ ለማንበብ ቀላል ሆኖ ሊያገኙት ይችላሉ። የ "ማንበብ እና መመለስ" ቅርንጫፍ አብዛኛውን ጊዜ ጥቅም ላይ የሚውለው በቧንቧ ውስጥ አንዳንድ መረጃዎች ሲኖሩ ነው. በዚህ ሁኔታ, እንጠቀማለን አንብብ() ከአሁኑ ጀምሮ ያለውን ያህል መረጃ አንብብ f_offset አንብብ፣ እና ከዚያ ተዛማጅ ማካካሻውን ዋጋ አዘምን።

በቀጣዮቹ ንባቦች ላይ የንባብ ማካካሻው ከደረሰ የቧንቧ መስመር ባዶ ይሆናል i_size1 በ inode. ቦታውን ወደ 0 እናስቀምጠዋለን እና ወደ ቧንቧው ለመጻፍ የሚፈልግ ማንኛውንም ሂደት ለማንቃት እንሞክራለን. ማጓጓዣው ሲሞላ, እናውቃለን. writep() ላይ መተኛት ip+1. እና አሁን የቧንቧ መስመር ባዶ ስለሆነ, የአጻጻፍ ዑደቱን ለመቀጠል ልንነቃው እንችላለን.

የሚነበብ ነገር ከሌለ, እንግዲያውስ readp() ባንዲራ ማዘጋጀት ይችላል IREAD እና እንቅልፍ ይተኛሉ ip+2. ምን እንደሚያነቃው እናውቃለን writep()አንዳንድ መረጃዎችን ወደ ቧንቧው ሲጽፍ.

ላይ አስተያየቶች ማንበብ () እና መጻፍ () መለኪያዎችን ከማለፍ ይልቅ ያንን ለመረዳት ይረዳዎታል"u» እንደ መደበኛ የI/O ተግባራት እንደ ፋይል ልንይዘው እንችላለን፣ ቦታ፣ የማህደረ ትውስታ ቋት እና ለማንበብ ወይም ለመፃፍ ባይት ብዛት።

/*
 * Read the file corresponding to
 * the inode pointed at by the argument.
 * The actual read arguments are found
 * in the variables:
 *    u_base        core address for destination
 *    u_offset    byte offset in file
 *    u_count        number of bytes to read
 *    u_segflg    read to kernel/user
 */
readi(aip)
struct inode *aip;
/* … */

/*
 * Write the file corresponding to
 * the inode pointed at by the argument.
 * The actual write arguments are found
 * in the variables:
 *    u_base        core address for source
 *    u_offset    byte offset in file
 *    u_count        number of bytes to write
 *    u_segflg    write to kernel/user
 */
writei(aip)
struct inode *aip;
/* … */

እንደ "ወግ አጥባቂ" እገዳ, ከዚያ readp() и writep() እስኪጨርሱ ወይም ውጤት እስኪያገኙ ድረስ inodes ይቆልፉ (ማለትም ይደውሉ wakeup). plock() и prele() በቀላሉ ስራ: የተለየ የጥሪ ስብስብ በመጠቀም sleep и wakeup አሁን የለቀቅነውን መቆለፊያ የሚያስፈልገው ማንኛውንም ሂደት እንድንነቃ ይፍቀዱልን፡-

/*
 * Lock a pipe.
 * If its already locked, set the WANT bit and sleep.
 */
plock(ip)
int *ip;
{
    register *rp;

    rp = ip;
    while(rp->i_flag&ILOCK) {
        rp->i_flag =| IWANT;
        sleep(rp, PPIPE);
    }
    rp->i_flag =| ILOCK;
}

/*
 * Unlock a pipe.
 * If WANT bit is on, wakeup.
 * This routine is also used to unlock inodes in general.
 */
prele(ip)
int *ip;
{
    register *rp;

    rp = ip;
    rp->i_flag =& ~ILOCK;
    if(rp->i_flag&IWANT) {
        rp->i_flag =& ~IWANT;
        wakeup(rp);
    }
}

መጀመሪያ ላይ ለምን እንደሆነ ሊገባኝ አልቻለም readp() አያስከትልም። prele(ip) ከጥሪው በፊት wakeup(ip+1). የመጀመሪያው ነገር writep() ይህ ሉፕ ውስጥ ጥሪዎች plock(ip), ይህም ከሆነ መዘጋትን ያስከትላል readp() እገዳውን እስካሁን አላስወገደም፣ ስለዚህ ኮዱ በሆነ መንገድ በትክክል መስራት አለበት። ብትመለከቱት wakeup(), ወደፊት ለመፈጸም የመኝታ ሂደትን ብቻ እንደሚያመለክት ግልጽ ይሆናል sched() በእውነት ጀምሯል። ስለዚህ readp() መንስኤዎች wakeup(), መክፈቻዎች, ስብስቦች IREAD እና ጥሪዎች sleep(ip+2)- ይህ ሁሉ በፊት writep() ዑደቱን እንደገና ያስጀምራል.

ይህ በስድስተኛው እትም ውስጥ የቧንቧ መስመሮችን መግለጫ ያጠናቅቃል. ቀላል ኮድ ፣ ሰፊ አንድምታ።

ሰባተኛ እትም ዩኒክስ (ጥር 1979) ብዙ አዳዲስ አፕሊኬሽኖችን እና የከርነል ባህሪያትን ያስተዋወቀ አዲስ ዋና ልቀት (ከአራት አመት በኋላ) ነበር። በዓይነት መውሰጃ፣ በማኅበራት እና በመተየብ ጠቋሚዎች ላይ ከመዋቅር አጠቃቀም ጋር በተያያዘ ከፍተኛ ለውጦችን አድርጓል። ቢሆንም የቧንቧ መስመር ኮድ በተግባር አልተለወጠም. ይህንን እትም መዝለል እንችላለን።

Xv6፣ ቀላል ዩኒክስ የመሰለ ከርነል

ኒውክሊየስ ለመፍጠር Xv6 በዩኒክስ ስድስተኛው እትም ላይ ተጽእኖ ያሳድራል, ነገር ግን በዘመናዊ C በ x86 ፕሮሰሰር ላይ እንዲሰራ ተጽፏል. ኮዱ ለማንበብ ቀላል እና ለመረዳት የሚያስቸግር ነው። እንዲሁም፣ እንደ ዩኒክስ ምንጮች ከTUHS በተለየ፣ ማጠናቀር፣ ማሻሻል እና ከPDP 11/70 ሌላ በሆነ ነገር ላይ ማስኬድ ይችላሉ። ስለዚህ ይህ አንኳር በኦፕሬቲንግ ሲስተሞች ላይ እንደ የማስተማሪያ ቁሳቁስ በዩኒቨርሲቲዎች ውስጥ በሰፊው ጥቅም ላይ ይውላል። ምንጮች Github ላይ ናቸው።.

ኮዱ ግልጽ እና አሳቢ አተገባበር ይዟል pipe.cበዲስክ ላይ ካለው ኢኖድ ይልቅ በማህደረ ትውስታ ቋት የተደገፈ። እዚህ ላይ "የመዋቅር ቧንቧ መስመር" እና ተግባሩን ፍቺ ብቻ እሰጣለሁ pipealloc():

#define PIPESIZE 512

struct pipe {
  struct spinlock lock;
  char data[PIPESIZE];
  uint nread;     // number of bytes read
  uint nwrite;    // number of bytes written
  int readopen;   // read fd is still open
  int writeopen;  // write fd is still open
};

int
pipealloc(struct file **f0, struct file **f1)
{
  struct pipe *p;

  p = 0;
  *f0 = *f1 = 0;
  if((*f0 = filealloc()) == 0 || (*f1 = filealloc()) == 0)
    goto bad;
  if((p = (struct pipe*)kalloc()) == 0)
    goto bad;
  p->readopen = 1;
  p->writeopen = 1;
  p->nwrite = 0;
  p->nread = 0;
  initlock(&p->lock, "pipe");
  (*f0)->type = FD_PIPE;
  (*f0)->readable = 1;
  (*f0)->writable = 0;
  (*f0)->pipe = p;
  (*f1)->type = FD_PIPE;
  (*f1)->readable = 0;
  (*f1)->writable = 1;
  (*f1)->pipe = p;
  return 0;

 bad:
  if(p)
    kfree((char*)p);
  if(*f0)
    fileclose(*f0);
  if(*f1)
    fileclose(*f1);
  return -1;
}

pipealloc() የተቀሩትን ትግበራዎች ሁኔታ ያዘጋጃል, ይህም ተግባራትን ያካትታል piperead(), pipewrite() и pipeclose(). ትክክለኛው የስርዓት ጥሪ sys_pipe ውስጥ የተተገበረ መጠቅለያ ነው። sysfile.c. ሁሉንም የእሱን ኮድ እንዲያነቡ እመክራለሁ. ውስብስብነቱ በስድስተኛው እትም ምንጭ ኮድ ደረጃ ላይ ነው, ግን ለማንበብ በጣም ቀላል እና የበለጠ አስደሳች ነው.

Linux 0.01

የሊኑክስ 0.01 ምንጭ ኮድ ማግኘት ይችላሉ። በእሱ ውስጥ የቧንቧ መስመሮችን ትግበራ ማጥናት ጠቃሚ ይሆናል fs/pipe.c. እዚህ ላይ ኢንኖድ የቧንቧ መስመርን ለመወከል ጥቅም ላይ ይውላል, ነገር ግን ቧንቧው ራሱ በዘመናዊው ሲ ተጽፏል. በስድስተኛው እትም ኮድ ውስጥ መንገድዎን ከጠለፉ, እዚህ ምንም ችግር አይኖርዎትም. ተግባሩ ይህን ይመስላል write_pipe():

int write_pipe(struct m_inode * inode, char * buf, int count)
{
    char * b=buf;

    wake_up(&inode->i_wait);
    if (inode->i_count != 2) { /* no readers */
        current->signal |= (1<<(SIGPIPE-1));
        return -1;
    }
    while (count-->0) {
        while (PIPE_FULL(*inode)) {
            wake_up(&inode->i_wait);
            if (inode->i_count != 2) {
                current->signal |= (1<<(SIGPIPE-1));
                return b-buf;
            }
            sleep_on(&inode->i_wait);
        }
        ((char *)inode->i_size)[PIPE_HEAD(*inode)] =
            get_fs_byte(b++);
        INC_PIPE( PIPE_HEAD(*inode) );
        wake_up(&inode->i_wait);
    }
    wake_up(&inode->i_wait);
    return b-buf;
}

የመዋቅር ፍቺዎችን ሳይመለከቱ እንኳን፣ የጽሑፍ አሰራር ውጤቱን ማረጋገጥ አለመሆኑ ለማረጋገጥ የኢኖድ ማጣቀሻ ቆጠራ እንዴት ጥቅም ላይ እንደሚውል ማወቅ ይችላሉ። SIGPIPE. ከባይት-ባይት ሥራ በተጨማሪ, ይህ ተግባር ከላይ ከተጠቀሱት ሀሳቦች ጋር ለማነፃፀር ቀላል ነው. ሎጂክ እንኳን sleep_on/wake_up እንግዳ አይመስልም።

ዘመናዊ ሊኑክስ ከርነልስ፣ FreeBSD፣ NetBSD፣ OpenBSD

አንዳንድ ዘመናዊ ፍሬዎችን በፍጥነት ሄድኩኝ። አንዳቸውም ቢሆኑ በዲስክ ላይ የተመሰረተ አተገባበር የላቸውም (አያስደንቅም)። ሊኑክስ የራሱ አተገባበር አለው። ምንም እንኳን ሶስቱ ዘመናዊ የቢኤስዲ አስኳሎች በጆን ዳይሰን በተፃፈው ኮድ ላይ የተመሰረቱ አተገባበርን ቢይዙም ፣ከአመታት በኋላ አንዳቸው ከሌላው በጣም የተለዩ ሆነዋል።

ማንበብ fs/pipe.c (በሊኑክስ ላይ) ወይም sys/kern/sys_pipe.c (በ*BSD)፣ እውነተኛ ራስን መወሰን ያስፈልጋል። እንደ ቬክተር እና ያልተመሳሰለ I/O ያሉ ባህሪያት አፈጻጸም እና ድጋፍ ዛሬ በኮድ ውስጥ አስፈላጊ ናቸው። እና የማህደረ ትውስታ ምደባ፣ መቆለፊያዎች እና የከርነል ውቅር ዝርዝሮች ሁሉም በጣም ይለያያሉ። በስርዓተ ክወናዎች ላይ የመግቢያ ኮርስ ዩኒቨርሲቲዎች የሚፈልጉት ይህ አይደለም።

ያም ሆነ ይህ፣ ጥቂት የቆዩ ቅጦችን ማውጣቴ ለእኔ አስደሳች ነበር (ለምሳሌ፣ ማመንጨት SIGPIPE እና ይመለሱ EPIPE ወደ ዝግ የቧንቧ መስመር ሲጽፉ) በእነዚህ ሁሉ, በጣም የተለያዩ, ዘመናዊ እንክብሎች. ፒፒዲ-11 ኮምፒዩተርን በቀጥታ ማየት አልችልም ነገር ግን እኔ ከመወለዴ ጥቂት አመታት በፊት ከተጻፈው ኮድ ገና ብዙ የምንማረው ነገር አለ።

እ.ኤ.አ. በ 2011 በዲቪ ካፑር የተጻፈ ፣ ጽሑፉ "የቧንቧ እና FIFOs የሊኑክስ ከርነል አተገባበርየሊኑክስ ቧንቧዎች (እስካሁን) እንዴት እንደሚሠሩ አጠቃላይ እይታ ነው። ሀ በሊኑክስ ላይ የቅርብ ጊዜ ቁርጠኝነት ከጊዚያዊ ፋይሎች የበለጠ አቅም ያለው የግንኙነት ቧንቧ መስመርን ያሳያል ፣ እና እንዲሁም በስድስተኛው እትም ዩኒክስ ከርነል ውስጥ የቧንቧ መስመሮች ከ"በጣም ወግ አጥባቂ መቆለፊያ" ምን ያህል እንደሄዱ ያሳያል።

ምንጭ: hab.com

አስተያየት ያክሉ