ఉదాహరణలతో Linuxలో ఫైల్ డిస్క్రిప్టర్

ఒకసారి, ఒక ఇంటర్వ్యూలో, నన్ను అడిగారు, డిస్క్ ఖాళీ అయిపోవడంతో సేవ పని చేయకపోతే మీరు ఏమి చేస్తారు?

అయితే, ఈ స్థలం ఏమి ఆక్రమించబడిందో నేను చూస్తాను మరియు వీలైతే, నేను స్థలాన్ని శుభ్రం చేస్తానని బదులిచ్చాను.
అప్పుడు ఇంటర్వ్యూయర్ అడిగాడు, విభజనపై ఖాళీ స్థలం లేకుంటే ఏమి చేయాలి, అయితే మొత్తం స్థలాన్ని ఆక్రమించే ఫైల్‌లు కూడా మీకు కనిపించలేదా?

దీనికి నేను మీరు ఎల్లప్పుడూ ఓపెన్ ఫైల్ డిస్క్రిప్టర్‌లను చూడవచ్చు, ఉదాహరణకు lsof కమాండ్‌తో, మరియు అందుబాటులో ఉన్న మొత్తం స్థలాన్ని ఏ అప్లికేషన్ తీసుకుందో అర్థం చేసుకోండి, ఆపై డేటా అవసరమా అనే దానిపై ఆధారపడి మీరు పరిస్థితులకు అనుగుణంగా పని చేయవచ్చు. .

ఇంటర్వ్యూయర్ చివరి మాటలో నాకు అంతరాయం కలిగించాడు, అతని ప్రశ్నకు జోడించాడు: "మాకు డేటా అవసరం లేదని అనుకుందాం, ఇది కేవలం డీబగ్ లాగ్ మాత్రమే, కానీ అది డీబగ్ వ్రాయలేనందున అప్లికేషన్ పనిచేయదు"?

“సరే,” నేను బదులిచ్చాను, “మేము అప్లికేషన్ కాన్ఫిగరేషన్‌లో డీబగ్‌ని ఆఫ్ చేసి, దాన్ని రీస్టార్ట్ చేయవచ్చు.”
ఇంటర్వ్యూయర్ అభ్యంతరం వ్యక్తం చేశారు: "లేదు, మేము అప్లికేషన్‌ను పునఃప్రారంభించలేము, మేము ఇప్పటికీ మెమరీలో ముఖ్యమైన డేటాను కలిగి ఉన్నాము మరియు ముఖ్యమైన క్లయింట్లు సేవకు కనెక్ట్ చేయబడ్డారు, మేము మళ్లీ మళ్లీ కనెక్ట్ చేయమని బలవంతం చేయలేము."

“సరే,” నేను అన్నాను, “మనం అప్లికేషన్‌ను రీస్టార్ట్ చేయలేకపోతే మరియు డేటా మనకు ముఖ్యమైనది కాకపోతే, మనం ఈ ఓపెన్ ఫైల్‌ను ls కమాండ్‌లో చూడకపోయినా ఫైల్ డిస్క్రిప్టర్ ద్వారా క్లియర్ చేయవచ్చు. ఫైల్ సిస్టమ్‌పై."

ఇంటర్వ్యూయర్ సంతోషించారు, కానీ నేను సంతోషించలేదు.

అప్పుడు నేను అనుకున్నాను, నా జ్ఞానాన్ని పరీక్షించే వ్యక్తి ఎందుకు లోతుగా త్రవ్వడు? అయితే డేటా ముఖ్యమైనది అయితే? మనం ప్రాసెస్‌ను పునఃప్రారంభించలేకపోతే, మరియు ప్రక్రియ ఖాళీ స్థలం లేని విభజనపై ఫైల్ సిస్టమ్‌కు వ్రాస్తే? మేము ఇప్పటికే వ్రాసిన డేటాను మాత్రమే కాకుండా, ఈ ప్రక్రియ వ్రాసే లేదా వ్రాయడానికి ప్రయత్నించే డేటాను కూడా కోల్పోలేకపోతే?

తుజిక్

నా కెరీర్ ప్రారంభంలో, నేను వినియోగదారు సమాచారాన్ని నిల్వ చేయడానికి అవసరమైన చిన్న అప్లికేషన్‌ను రూపొందించడానికి ప్రయత్నించాను. ఆపై నేను ఆలోచించాను, నేను వినియోగదారుని అతని డేటాతో ఎలా సరిపోల్చగలను. ఉదాహరణకు, నాకు ఇవనోవ్ ఇవాన్ ఇవనోవిచ్ ఉంది, మరియు అతని వద్ద కొంత సమాచారం ఉంది, కానీ నేను వారితో ఎలా స్నేహం చేయగలను? "తుజిక్" అనే కుక్క ఈ ఇవాన్‌కు చెందినదని నేను నేరుగా సూచించగలను. కానీ అతను తన పేరును మార్చుకుంటే మరియు ఇవాన్ బదులుగా, ఉదాహరణకు, ఒలియా అవుతాడు? అప్పుడు మా ఒలియా ఇవనోవ్నా ఇవనోవాకు ఇకపై కుక్క ఉండదు, మరియు మా తుజిక్ ఇప్పటికీ ఉనికిలో లేని ఇవాన్‌కు చెందినవాడు. ప్రతి వినియోగదారుకు ఒక ప్రత్యేక ఐడెంటిఫైయర్ (ID)ని అందించిన డేటాబేస్ ఈ సమస్యను పరిష్కరించడంలో సహాయపడింది మరియు నా Tuzik ఈ IDతో ముడిపడి ఉంది, వాస్తవానికి ఇది కేవలం క్రమ సంఖ్య మాత్రమే. ఆ విధంగా, ఏస్ యజమానికి ID నంబర్ 2 ఉంది మరియు ఏదో ఒక సమయంలో ఇవాన్ ఈ ID కింద ఉన్నాడు, ఆపై ఒలియా అదే ID కింద మారింది. మానవత్వం మరియు పశుపోషణ సమస్య ఆచరణాత్మకంగా పరిష్కరించబడింది.

ఫైల్ డిస్క్రిప్టర్

ఈ ఫైల్‌తో పనిచేసే ఫైల్ మరియు ప్రోగ్రామ్ యొక్క సమస్య దాదాపుగా మన కుక్క మరియు మనిషికి సంబంధించినది. నేను ivan.txt అనే ఫైల్‌ను తెరిచి, దానిలో tuzik అనే పదాన్ని రాయడం ప్రారంభించాను, కానీ ఫైల్‌లో మొదటి అక్షరం “t” మాత్రమే వ్రాయగలిగాను మరియు ఈ ఫైల్‌ని ఎవరో పేరు మార్చారు, ఉదాహరణకు, olya.txt. కానీ ఫైల్ అలాగే ఉంది మరియు నేను ఇప్పటికీ అందులో నా ఏస్‌ను రికార్డ్ చేయాలనుకుంటున్నాను. సిస్టమ్ కాల్ ద్వారా ఫైల్ తెరవబడిన ప్రతిసారీ ఓపెన్ ఏదైనా ప్రోగ్రామింగ్ లాంగ్వేజ్‌లో నేను ఫైల్‌కి నన్ను సూచించే ప్రత్యేక IDని స్వీకరిస్తాను, ఈ ID ఫైల్ డిస్క్రిప్టర్. మరియు ఈ ఫైల్‌తో తదుపరి ఏమి మరియు ఎవరు చేస్తారు అనేది అస్సలు పట్టింపు లేదు, దీన్ని తొలగించవచ్చు, దాని పేరు మార్చవచ్చు, యజమానిని మార్చవచ్చు లేదా చదవడానికి మరియు వ్రాయడానికి హక్కులను తీసివేయవచ్చు, నాకు ఇప్పటికీ యాక్సెస్ ఉంటుంది దానికి, ఎందుకంటే ఫైల్‌ను తెరిచే సమయంలో, దాన్ని చదవడానికి మరియు/లేదా వ్రాయడానికి నాకు హక్కులు ఉన్నాయి మరియు నేను దానితో పని చేయడం ప్రారంభించగలిగాను, అంటే నేను దీన్ని కొనసాగించాలి.

Linuxలో, libc లైబ్రరీ 3 సంఖ్యతో నడుస్తున్న ప్రతి అప్లికేషన్ (ప్రాసెస్) కోసం 0,1,2 డిస్క్రిప్టర్ ఫైల్‌లను తెరుస్తుంది. మరింత సమాచారం లింక్‌లలో చూడవచ్చు మనిషి stdio и మనిషి stdout

  • ఫైల్ డిస్క్రిప్టర్ 0ని STDIN అంటారు మరియు అప్లికేషన్ ఇన్‌పుట్‌తో అనుబంధించబడింది
  • ఫైల్ డిస్క్రిప్టర్ 1ని STDOUT అని పిలుస్తారు మరియు ప్రింట్ కమాండ్‌ల వంటి డేటాను అవుట్‌పుట్ చేయడానికి అప్లికేషన్‌లు ఉపయోగిస్తాయి
  • ఫైల్ డిస్క్రిప్టర్ 2ని STDERR అని పిలుస్తారు మరియు దోష సందేశాలను అవుట్‌పుట్ చేయడానికి అప్లికేషన్‌లు ఉపయోగిస్తాయి.

మీ ప్రోగ్రామ్‌లో మీరు చదవడానికి లేదా వ్రాయడానికి ఏదైనా ఫైల్‌ను తెరిస్తే, మీరు మొదటి ఉచిత IDని పొందుతారు మరియు అది నంబర్ 3 అవుతుంది.

ఫైల్ డిస్క్రిప్టర్‌ల జాబితా మీకు దాని PID తెలిస్తే ఏదైనా ప్రక్రియ కోసం చూడవచ్చు.

ఉదాహరణకు, బాష్ కన్సోల్‌ని తెరిచి, మన ప్రక్రియ యొక్క PIDని చూద్దాం

[user@localhost ]$ echo $$
15771

రెండవ కన్సోల్‌లో రన్ చేద్దాం

[user@localhost ]$ ls -lah /proc/15771/fd/
total 0
dr-x------ 2 user user  0 Oct  7 15:42 .
dr-xr-xr-x 9 user user  0 Oct  7 15:42 ..
lrwx------ 1 user user 64 Oct  7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 255 -> /dev/pts/21

మీరు ఈ కథనం యొక్క ప్రయోజనాల కోసం ఫైల్ డిస్క్రిప్టర్ నంబర్ 255ని సురక్షితంగా విస్మరించవచ్చు; ఇది దాని అవసరాల కోసం బాష్ ద్వారా తెరవబడింది మరియు లింక్ చేయబడిన లైబ్రరీ ద్వారా కాదు.

ఇప్పుడు మొత్తం 3 డిస్క్రిప్టర్ ఫైల్‌లు సూడో టెర్మినల్ పరికరంతో అనుబంధించబడ్డాయి /dev/pts, కానీ మేము వాటిని ఇప్పటికీ మార్చవచ్చు, ఉదాహరణకు, వాటిని రెండవ కన్సోల్‌లో అమలు చేయండి

[user@localhost ]$ echo "hello world" > /proc/15771/fd/0

మరియు మొదటి కన్సోల్‌లో మనం చూస్తాము

[user@localhost ]$ hello world

దారి మళ్లింపు మరియు పైప్

మీరు ఈ 3 డిస్క్రిప్టర్ ఫైల్‌లను బాష్‌తో సహా ఏదైనా ప్రక్రియలో సులభంగా భర్తీ చేయవచ్చు, ఉదాహరణకు రెండు ప్రక్రియలను అనుసంధానించే పైప్ ద్వారా, చూడండి

[user@localhost ]$ cat /dev/zero | sleep 10000

మీరు ఈ ఆదేశాన్ని మీరే అమలు చేయవచ్చు strace -f మరియు లోపల ఏమి జరుగుతుందో చూడండి, కానీ నేను మీకు క్లుప్తంగా చెబుతాను.

PID 15771తో మా పేరెంట్ బాష్ ప్రాసెస్ మా ఆదేశాన్ని అన్వయిస్తుంది మరియు మనం ఎన్ని కమాండ్‌లను అమలు చేయాలనుకుంటున్నామో ఖచ్చితంగా అర్థం చేసుకుంటుంది, మా విషయంలో వాటిలో రెండు ఉన్నాయి: పిల్లి మరియు నిద్ర. రెండు చైల్డ్ ప్రాసెస్‌లను సృష్టించి, వాటిని ఒక పైపులో విలీనం చేయాలని బాష్‌కు తెలుసు. మొత్తంగా, బాష్‌కు 2 చైల్డ్ ప్రాసెస్‌లు మరియు ఒక పైపు అవసరం.

చైల్డ్ ప్రాసెస్‌లను సృష్టించే ముందు బాష్ సిస్టమ్ కాల్‌ని అమలు చేస్తుంది పైపు మరియు తాత్కాలిక పైప్ బఫర్‌పై కొత్త ఫైల్ డిస్క్రిప్టర్‌లను అందుకుంటుంది, కానీ ఈ బఫర్ మా రెండు చైల్డ్ ప్రాసెస్‌లను ఇంకా కనెక్ట్ చేయలేదు.

పేరెంట్ ప్రాసెస్ కోసం, ఇప్పటికే పైప్ ఉన్నట్లు కనిపిస్తోంది, కానీ ఇంకా చైల్డ్ ప్రాసెస్‌లు లేవు:

PID    command
15771  bash
lrwx------ 1 user user 64 Oct  7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct  7 15:42 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct  7 15:42 255 -> /dev/pts/21

ఆపై సిస్టమ్ కాల్‌ని ఉపయోగించడం క్లోన్ బాష్ రెండు చైల్డ్ ప్రాసెస్‌లను సృష్టిస్తుంది మరియు మా మూడు ప్రక్రియలు ఇలా ఉంటాయి:

PID    command
15771  bash
lrwx------ 1 user user 64 Oct  7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct  7 15:42 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct  7 15:42 255 -> /dev/pts/21
PID    command
9004  bash
lrwx------ 1 user user 64 Oct  7 15:57 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:57 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:57 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:57 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct  7 15:57 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct  7 15:57 255 -> /dev/pts/21
PID    command
9005  bash
lrwx------ 1 user user 64 Oct  7 15:57 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:57 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:57 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:57 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct  7 15:57 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct  7 15:57 255 -> /dev/pts/21

క్లోన్ అన్ని ఫైల్ డిస్క్రిప్టర్‌లతో పాటు ప్రక్రియను క్లోన్ చేస్తుందని మర్చిపోవద్దు, కాబట్టి అవి పేరెంట్ ప్రాసెస్‌లో మరియు పిల్లలలో ఒకే విధంగా ఉంటాయి. PID 15771తో పేరెంట్ ప్రాసెస్ యొక్క పని చైల్డ్ ప్రాసెస్‌లను పర్యవేక్షించడం, కాబట్టి ఇది పిల్లల నుండి ప్రతిస్పందన కోసం వేచి ఉంటుంది.

అందువల్ల, దీనికి పైప్ అవసరం లేదు మరియు ఇది 3 మరియు 4 నంబర్లతో కూడిన ఫైల్ డిస్క్రిప్టర్‌లను మూసివేస్తుంది.

PID 9004తో మొదటి చైల్డ్ బాష్ ప్రక్రియలో, సిస్టమ్ కాల్ డప్2, మా STDOUT ఫైల్ డిస్క్రిప్టర్ నంబర్ 1ని పైప్‌ని సూచించే ఫైల్ డిస్క్రిప్టర్‌గా మారుస్తుంది, మా విషయంలో ఇది నంబర్ 3. కాబట్టి, PID 9004తో మొదటి చైల్డ్ ప్రాసెస్ STDOUTకి వ్రాసే ప్రతిదీ స్వయంచాలకంగా పైప్ బఫర్‌లో ముగుస్తుంది.

PID 9005తో రెండవ చైల్డ్ ప్రాసెస్‌లో, ఫైల్ డిస్క్రిప్టర్ STDIN నంబర్ 2ని మార్చడానికి bash dup0ని ఉపయోగిస్తుంది. ఇప్పుడు PID 9005తో మన రెండవ బాష్ చదివే ప్రతిదీ పైప్ నుండి చదవబడుతుంది.

దీని తర్వాత, 3 మరియు 4 నంబర్ గల ఫైల్ డిస్క్రిప్టర్‌లు కూడా చైల్డ్ ప్రాసెస్‌లలో మూసివేయబడతాయి, ఎందుకంటే అవి ఇకపై ఉపయోగించబడవు.

నేను ఫైల్ డిస్క్రిప్టర్ 255ని ఉద్దేశపూర్వకంగా విస్మరిస్తాను; ఇది బాష్ ద్వారా అంతర్గత ప్రయోజనాల కోసం ఉపయోగించబడుతుంది మరియు చైల్డ్ ప్రాసెస్‌లలో కూడా మూసివేయబడుతుంది.

తర్వాత, PID 9004తో మొదటి చైల్డ్ ప్రాసెస్‌లో, బాష్ సిస్టమ్ కాల్‌ని ఉపయోగించడం ప్రారంభిస్తుంది కార్యనిర్వాహకుడు మేము కమాండ్ లైన్‌లో పేర్కొన్న ఎక్జిక్యూటబుల్ ఫైల్, మా విషయంలో ఇది /usr/bin/cat.

PID 9005తో రెండవ చైల్డ్ ప్రాసెస్‌లో, మా సందర్భంలో /usr/bin/sleepలో మేము పేర్కొన్న రెండవ ఎక్జిక్యూటబుల్‌ను bash అమలు చేస్తుంది.

ఓపెన్ కాల్ చేసిన సమయంలో O_CLOEXEC ఫ్లాగ్‌తో తెరవబడితే తప్ప, exec సిస్టమ్ కాల్ ఫైల్ హ్యాండిల్‌లను మూసివేయదు. మా విషయంలో, ఎక్జిక్యూటబుల్ ఫైల్‌లను ప్రారంభించిన తర్వాత, అన్ని ప్రస్తుత ఫైల్ డిస్క్రిప్టర్లు సేవ్ చేయబడతాయి.

కన్సోల్‌లో తనిఖీ చేయండి:

[user@localhost ]$ pgrep -P 15771
9004
9005
[user@localhost ]$ ls -lah /proc/15771/fd/
total 0
dr-x------ 2 user user  0 Oct  7 15:42 .
dr-xr-xr-x 9 user user  0 Oct  7 15:42 ..
lrwx------ 1 user user 64 Oct  7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 255 -> /dev/pts/21
[user@localhost ]$ ls -lah /proc/9004/fd
total 0
dr-x------ 2 user user  0 Oct  7 15:57 .
dr-xr-xr-x 9 user user  0 Oct  7 15:57 ..
lrwx------ 1 user user 64 Oct  7 15:57 0 -> /dev/pts/21
l-wx------ 1 user user 64 Oct  7 15:57 1 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct  7 15:57 2 -> /dev/pts/21
lr-x------ 1 user user 64 Oct  7 15:57 3 -> /dev/zero
[user@localhost ]$ ls -lah /proc/9005/fd
total 0
dr-x------ 2 user user  0 Oct  7 15:57 .
dr-xr-xr-x 9 user user  0 Oct  7 15:57 ..
lr-x------ 1 user user 64 Oct  7 15:57 0 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct  7 15:57 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:57 2 -> /dev/pts/21
[user@localhost ]$ ps -up 9004
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
user  9004  0.0  0.0 107972   620 pts/21   S+   15:57   0:00 cat /dev/zero
[user@localhost ]$ ps -up 9005
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
user  9005  0.0  0.0 107952   360 pts/21   S+   15:57   0:00 sleep 10000

మీరు చూడగలిగినట్లుగా, మా పైప్ యొక్క ప్రత్యేక సంఖ్య రెండు ప్రక్రియలలో ఒకే విధంగా ఉంటుంది. కాబట్టి ఒకే పేరెంట్‌తో రెండు వేర్వేరు ప్రక్రియల మధ్య మనకు కనెక్షన్ ఉంది.

బాష్ ఉపయోగించే సిస్టమ్ కాల్‌ల గురించి తెలియని వారికి, స్ట్రేస్ ద్వారా ఆదేశాలను అమలు చేయాలని మరియు అంతర్గతంగా ఏమి జరుగుతుందో చూడాలని నేను బాగా సిఫార్సు చేస్తున్నాను, ఉదాహరణకు ఇలా:

strace -s 1024 -f bash -c "ls | grep hello"

తక్కువ డిస్క్ స్థలం మరియు ప్రాసెస్‌ను పునఃప్రారంభించకుండా డేటాను సేవ్ చేయడానికి ప్రయత్నిస్తున్నప్పుడు మా సమస్యకు తిరిగి వెళ్దాం. డిస్క్‌కి సెకనుకు దాదాపు 1 మెగాబైట్‌ని వ్రాసే చిన్న ప్రోగ్రామ్‌ను వ్రాద్దాం. అంతేకాకుండా, కొన్ని కారణాల వల్ల మేము డిస్క్‌కి డేటాను వ్రాయలేకపోతే, మేము దీన్ని విస్మరించి, సెకనులో డేటాను మళ్లీ వ్రాయడానికి ప్రయత్నిస్తాము. నేను పైథాన్‌ని ఉపయోగిస్తున్న ఉదాహరణలో, మీరు ఏదైనా ఇతర ప్రోగ్రామింగ్ భాషని ఉపయోగించవచ్చు.

[user@localhost ]$ cat openforwrite.py 
import datetime
import time

mystr="a"*1024*1024+"n"
with open("123.txt", "w") as f:
    while True:
        try:
            f.write(str(datetime.datetime.now()))
            f.write(mystr)
            f.flush()
            time.sleep(1)
        except:
            pass

ప్రోగ్రామ్‌ని రన్ చేద్దాం మరియు ఫైల్ డిస్క్రిప్టర్‌లను చూద్దాం

[user@localhost ]$ python openforwrite.py &
[1] 3762
[user@localhost ]$ ps axuf | grep [o]penforwrite
user  3762  0.0  0.0 128600  5744 pts/22   S+   16:28   0:00  |   _ python openforwrite.py
[user@localhost ]$ ls -la /proc/3762/fd
total 0
dr-x------ 2 user user  0 Oct  7 16:29 .
dr-xr-xr-x 9 user user  0 Oct  7 16:29 ..
lrwx------ 1 user user 64 Oct  7 16:29 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  7 16:29 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  7 16:29 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct  7 16:29 3 -> /home/user/123.txt

మీరు చూడగలిగినట్లుగా, మా వద్ద మా 3 ప్రామాణిక ఫైల్ డిస్క్రిప్టర్లు ఉన్నాయి మరియు మేము తెరిచిన మరొకటి ఉన్నాయి. ఫైల్ పరిమాణాన్ని తనిఖీ చేద్దాం:

[user@localhost ]$ ls -lah 123.txt 
-rw-rw-r-- 1 user user 117M Oct  7 16:30 123.txt

డేటా వ్రాయబడుతోంది, మేము ఫైల్‌లోని అనుమతులను మార్చడానికి ప్రయత్నిస్తాము:

[user@localhost ]$ sudo chown root: 123.txt
[user@localhost ]$ ls -lah 123.txt 
-rw-rw-r-- 1 root root 168M Oct  7 16:31 123.txt
[user@localhost ]$ ls -lah 123.txt 
-rw-rw-r-- 1 root root 172M Oct  7 16:31 123.txt

ఫైల్‌కి వ్రాయడానికి మా వినియోగదారుకు అనుమతి లేనప్పటికీ, డేటా ఇప్పటికీ వ్రాయబడడాన్ని మేము చూస్తున్నాము. దాన్ని తీసివేయడానికి ప్రయత్నిద్దాం:

[user@localhost ]$ sudo rm 123.txt 
[user@localhost ]$ ls 123.txt
ls: cannot access 123.txt: No such file or directory

డేటా ఎక్కడ వ్రాయబడింది? మరియు అవి అస్సలు వ్రాయబడి ఉన్నాయా? మేము తనిఖీ చేస్తాము:

[user@localhost ]$ ls -la /proc/3762/fd
total 0
dr-x------ 2 user user  0 Oct  7 16:29 .
dr-xr-xr-x 9 user user  0 Oct  7 16:29 ..
lrwx------ 1 user user 64 Oct  7 16:29 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  7 16:29 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  7 16:29 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct  7 16:29 3 -> /home/user/123.txt (deleted)

అవును, మా ఫైల్ డిస్క్రిప్టర్ ఇప్పటికీ ఉంది మరియు మేము ఈ ఫైల్ డిస్క్రిప్టర్‌ని మా పాత ఫైల్ లాగా పరిగణించవచ్చు, మేము దీన్ని చదవవచ్చు, క్లియర్ చేయవచ్చు మరియు కాపీ చేయవచ్చు.

ఫైల్ పరిమాణాన్ని చూద్దాం:

[user@localhost ]$ lsof | grep 123.txt
python    31083             user    3w      REG                8,5   19923457   2621522 /home/user/123.txt

ఫైల్ పరిమాణం 19923457. ఫైల్‌ను క్లియర్ చేయడానికి ప్రయత్నిద్దాం:

[user@localhost ]$ truncate -s 0 /proc/31083/fd/3
[user@localhost ]$ lsof | grep 123.txt
python    31083             user    3w      REG                8,5  136318390   2621522 /home/user/123.txt

మీరు గమనిస్తే, ఫైల్ పరిమాణం మాత్రమే పెరుగుతోంది మరియు మా ట్రంక్ పని చేయలేదు. సిస్టమ్ కాల్ డాక్యుమెంటేషన్ చూద్దాం ఓపెన్. మేము ఫైల్‌ను తెరిచేటప్పుడు O_APPEND ఫ్లాగ్‌ని ఉపయోగిస్తే, ప్రతి రైట్‌తో, ఆపరేటింగ్ సిస్టమ్ ఫైల్ పరిమాణాన్ని తనిఖీ చేస్తుంది మరియు ఫైల్ చివరి వరకు డేటాను వ్రాస్తుంది మరియు దీన్ని పరమాణుపరంగా చేస్తుంది. ఇది ఒకే ఫైల్‌కు బహుళ థ్రెడ్‌లు లేదా ప్రక్రియలను వ్రాయడానికి అనుమతిస్తుంది. కానీ మా కోడ్‌లో మేము ఈ జెండాను ఉపయోగించము. అదనపు రైటింగ్ కోసం ఫైల్‌ని ఓపెన్ చేస్తే మాత్రమే ట్రంక్ తర్వాత lsofలో వేరే ఫైల్ పరిమాణాన్ని చూడగలం, అంటే బదులుగా మన కోడ్‌లో

with open("123.txt", "w") as f:

మనం పెట్టాలి

with open("123.txt", "a") as f:

"w" ఫ్లాగ్‌తో తనిఖీ చేస్తోంది

[user@localhost ]$ strace -e trace=open python openforwrite.py 2>&1| grep 123.txt
open("123.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3

మరియు "a" జెండాతో

[user@localhost ]$ strace -e trace=open python openforwrite.py 2>&1| grep 123.txt
open("123.txt", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3

ప్రోగ్రామింగ్ ఇప్పటికే నడుస్తున్న ప్రక్రియ

తరచుగా ప్రోగ్రామర్లు, ప్రోగ్రామ్‌లను సృష్టించేటప్పుడు మరియు పరీక్షించేటప్పుడు, డీబగ్గర్‌లను (ఉదాహరణకు GDB) లేదా అప్లికేషన్‌లో వివిధ స్థాయిల లాగింగ్‌ను ఉపయోగిస్తారు. ఇప్పటికే నడుస్తున్న ప్రోగ్రామ్‌ను వాస్తవానికి వ్రాయడానికి మరియు మార్చగల సామర్థ్యాన్ని Linux అందిస్తుంది, ఉదాహరణకు, వేరియబుల్స్ విలువలను మార్చడం, బ్రేక్‌పాయింట్ సెట్ చేయడం మొదలైనవి.

ఫైల్‌ను వ్రాయడానికి తగినంత డిస్క్ స్థలం లేకపోవడం గురించి అసలు ప్రశ్నకు తిరిగి వస్తున్నప్పుడు, సమస్యను అనుకరించడానికి ప్రయత్నిద్దాం.

మన విభజన కోసం ఫైల్‌ను సృష్టిద్దాం, దానిని మనం ప్రత్యేక డిస్క్‌గా మౌంట్ చేస్తాము:

[user@localhost ~]$ dd if=/dev/zero of=~/tempfile_for_article.dd bs=1M count=10
10+0 records in
10+0 records out
10485760 bytes (10 MB) copied, 0.00525929 s, 2.0 GB/s
[user@localhost ~]$

ఫైల్ సిస్టమ్‌ని క్రియేట్ చేద్దాం:

[user@localhost ~]$ mkfs.ext4 ~/tempfile_for_article.dd
mke2fs 1.42.9 (28-Dec-2013)
/home/user/tempfile_for_article.dd is not a block special device.
Proceed anyway? (y,n) y
...
Writing superblocks and filesystem accounting information: done
[user@localhost ~]$

ఫైల్ సిస్టమ్‌ను మౌంట్ చేయండి:

[user@localhost ~]$ sudo mount ~/tempfile_for_article.dd /mnt/
[sudo] password for user: 
[user@localhost ~]$ df -h | grep mnt
/dev/loop0      8.7M  172K  7.9M   3% /mnt

మేము మా యజమానితో డైరెక్టరీని సృష్టిస్తాము:

[user@localhost ~]$ sudo mkdir /mnt/logs
[user@localhost ~]$ sudo chown user: /mnt/logs

మా ప్రోగ్రామ్‌లో మాత్రమే వ్రాయడం కోసం ఫైల్‌ను తెరవండి:

with open("/mnt/logs/123.txt", "w") as f:

ప్రారంభించండి

[user@localhost ]$ python openforwrite.py 

మేము కొన్ని సెకన్లు వేచి ఉంటాము

[user@localhost ~]$ df -h | grep mnt
/dev/loop0      8.7M  8.0M     0 100% /mnt

కాబట్టి, ఈ వ్యాసం ప్రారంభంలో వివరించిన సమస్య మాకు ఉంది. ఖాళీ స్థలం 0, 100% ఆక్రమించబడింది.

పని యొక్క పరిస్థితుల ప్రకారం, మేము కోల్పోలేని చాలా ముఖ్యమైన డేటాను రికార్డ్ చేయడానికి ప్రయత్నిస్తున్నామని మేము గుర్తుంచుకోవాలి. మరియు అదే సమయంలో, మేము ప్రక్రియను పునఃప్రారంభించకుండా సేవను పరిష్కరించాలి.

మనకు ఇప్పటికీ డిస్క్ స్థలం ఉందని అనుకుందాం, కానీ వేరే విభజనలో, ఉదాహరణకు /హోమ్‌లో.

మన కోడ్‌ను “ఫ్లైలో రీప్రోగ్రామ్” చేయడానికి ప్రయత్నిద్దాం.

డిస్క్ స్థలం మొత్తాన్ని తిన్న మా ప్రక్రియ యొక్క PIDని చూద్దాం:

[user@localhost ~]$ ps axuf | grep [o]penfor
user 10078 27.2  0.0 128600  5744 pts/22   R+   11:06   0:02  |   _ python openforwrite.py

gdb ద్వారా ప్రక్రియకు కనెక్ట్ చేయండి

[user@localhost ~]$ gdb -p 10078
...
(gdb) 

ఓపెన్ ఫైల్ డిస్క్రిప్టర్లను చూద్దాం:

(gdb) shell ls -lah /proc/10078/fd/
total 0
dr-x------ 2 user user  0 Oct  8 11:06 .
dr-xr-xr-x 9 user user  0 Oct  8 11:06 ..
lrwx------ 1 user user 64 Oct  8 11:09 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:09 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:06 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct  8 11:09 3 -> /mnt/logs/123.txt

మాకు ఆసక్తి ఉన్న ఫైల్ డిస్క్రిప్టర్ నంబర్ 3 గురించిన సమాచారాన్ని మేము పరిశీలిస్తాము

(gdb) shell cat /proc/10078/fdinfo/3
pos:    8189952
flags:  0100001
mnt_id: 482

పైథాన్ ఏ సిస్టమ్ కాల్ చేస్తుందో గుర్తుంచుకోండి (పైన మనం స్ట్రేస్ రన్ చేసి ఓపెన్ కాల్‌ని ఎక్కడ కనుగొన్నామో చూడండి), ఫైల్‌ని తెరవడానికి మన కోడ్‌ని ప్రాసెస్ చేస్తున్నప్పుడు, మన ప్రాసెస్ తరపున మనమే అదే చేస్తాము, అయితే మనకు O_WRONLY|O_CREAT| O_TRUNC బిట్‌లు సంఖ్యా విలువతో భర్తీ చేయబడతాయి. దీన్ని చేయడానికి, కెర్నల్ మూలాలను తెరవండి, ఉదాహరణకు ఇక్కడ మరియు ఏ జెండాలు దేనికి బాధ్యత వహిస్తాయో చూడండి

#O_WRONLY 00000001ని నిర్వచించండి
#O_CREAT 00000100ని నిర్వచించండి
#O_TRUNC 00001000ని నిర్వచించండి

మేము అన్ని విలువలను ఒకటిగా కలుపుతాము, మనకు 00001101 వస్తుంది

మేము మా కాల్‌ని gdb నుండి అమలు చేస్తాము

(gdb) call open("/home/user/123.txt", 00001101,0666)
$1 = 4

కాబట్టి మేము నంబర్ 4తో కొత్త ఫైల్ డిస్క్రిప్టర్ మరియు మరొక విభజనలో కొత్త ఓపెన్ ఫైల్‌ని పొందాము, మేము తనిఖీ చేస్తాము:

(gdb) shell ls -lah /proc/10078/fd/
total 0
dr-x------ 2 user user  0 Oct  8 11:06 .
dr-xr-xr-x 9 user user  0 Oct  8 11:06 ..
lrwx------ 1 user user 64 Oct  8 11:09 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:09 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:06 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct  8 11:09 3 -> /mnt/logs/123.txt
l-wx------ 1 user user 64 Oct  8 11:15 4 -> /home/user/123.txt

మేము పైపుతో ఉదాహరణను గుర్తుంచుకుంటాము - బాష్ ఫైల్ డిస్క్రిప్టర్లను ఎలా మారుస్తుంది మరియు మేము ఇప్పటికే dup2 సిస్టమ్ కాల్ నేర్చుకున్నాము.

మేము ఒక ఫైల్ డిస్క్రిప్టర్‌ను మరొక దానితో భర్తీ చేయడానికి ప్రయత్నిస్తాము

(gdb) call dup2(4,3)
$2 = 3

మేము తనిఖీ చేస్తాము:

(gdb) shell ls -lah /proc/10078/fd/
total 0
dr-x------ 2 user user  0 Oct  8 11:06 .
dr-xr-xr-x 9 user user  0 Oct  8 11:06 ..
lrwx------ 1 user user 64 Oct  8 11:09 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:09 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:06 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct  8 11:09 3 -> /home/user/123.txt
l-wx------ 1 user user 64 Oct  8 11:15 4 -> /home/user/123.txt

మేము ఫైల్ డిస్క్రిప్టర్ 4ని మూసివేస్తాము, ఎందుకంటే ఇది మాకు అవసరం లేదు:

(gdb) call close (4)
$1 = 0

మరియు gdb నుండి నిష్క్రమించండి

(gdb) quit
A debugging session is active.

    Inferior 1 [process 10078] will be detached.

Quit anyway? (y or n) y
Detaching from program: /usr/bin/python2.7, process 10078

కొత్త ఫైల్‌ని తనిఖీ చేస్తోంది:

[user@localhost ~]$ ls -lah /home/user/123.txt
-rw-rw-r-- 1 user user 5.1M Oct  8 11:18 /home/user/123.txt
[user@localhost ~]$ ls -lah /home/user/123.txt
-rw-rw-r-- 1 user user 7.1M Oct  8 11:18 /home/user/123.txt

మీరు చూడగలిగినట్లుగా, డేటా కొత్త ఫైల్‌కి వ్రాయబడింది, పాతదాన్ని తనిఖీ చేద్దాం:

[user@localhost ~]$ ls -lah /mnt/logs/123.txt 
-rw-rw-r-- 1 user user 7.9M Oct  8 11:08 /mnt/logs/123.txt

డేటా కోల్పోలేదు, అప్లికేషన్ పని చేస్తుంది, లాగ్‌లు కొత్త స్థానానికి వ్రాయబడతాయి.

పనిని కొద్దిగా క్లిష్టతరం చేద్దాం

డేటా మనకు ముఖ్యమైనదని ఊహించుదాం, కానీ మనకు ఏ విభజనలోనూ డిస్క్ స్థలం లేదు మరియు మేము డిస్క్‌ను కనెక్ట్ చేయలేము.

మనం చేయగలిగేది మన డేటాను ఎక్కడికో దారి మళ్లించడం, ఉదాహరణకు పైప్‌కి, మరియు కొంత ప్రోగ్రామ్ ద్వారా పైప్ నుండి నెట్‌క్యాట్‌కు డేటాను మళ్లించడం, ఉదాహరణకు నెట్‌క్యాట్.
మేము mkfifo కమాండ్‌తో పేరున్న పైపును సృష్టించవచ్చు. ఫైల్ సిస్టమ్‌పై ఖాళీ స్థలం లేనప్పటికీ ఇది ఒక నకిలీ ఫైల్‌ను సృష్టిస్తుంది.

అప్లికేషన్‌ను పునఃప్రారంభించి, తనిఖీ చేయండి:

[user@localhost ]$ python openforwrite.py 
[user@localhost ~]$ ps axuf | grep [o]pen
user  5946 72.9  0.0 128600  5744 pts/22   R+   11:27   0:20  |   _ python openforwrite.py
[user@localhost ~]$ ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user  0 Oct  8 11:27 .
dr-xr-xr-x 9 user user  0 Oct  8 11:27 ..
lrwx------ 1 user user 64 Oct  8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct  8 11:28 3 -> /mnt/logs/123.txt
[user@localhost ~]$ df -h | grep mnt
/dev/loop0      8.7M  8.0M     0 100% /mnt

డిస్క్ స్థలం లేదు, కానీ మేము అక్కడ పేరున్న పైపును విజయవంతంగా సృష్టిస్తాము:

[user@localhost ~]$ mkfifo /mnt/logs/megapipe
[user@localhost ~]$ ls -lah /mnt/logs/megapipe 
prw-rw-r-- 1 user user 0 Oct  8 11:28 /mnt/logs/megapipe

ఇప్పుడు మనం ఈ పైప్‌లోకి వెళ్ళే మొత్తం డేటాను నెట్‌వర్క్ ద్వారా మరొక సర్వర్‌కు ఎలాగైనా చుట్టాలి; అదే నెట్‌క్యాట్ దీనికి అనుకూలంగా ఉంటుంది.

సర్వర్‌లో remote-server.example.com మేము ప్రారంభిస్తాము

[user@localhost ~]$ nc -l 7777 > 123.txt 

మా సమస్యాత్మక సర్వర్‌లో మేము ప్రత్యేక టెర్మినల్‌లో ప్రారంభిస్తాము

[user@localhost ~]$ nc remote-server.example.com 7777 < /mnt/logs/megapipe 

ఇప్పుడు పైప్‌లో ముగిసే మొత్తం డేటా స్వయంచాలకంగా నెట్‌క్యాట్‌లోని stdinకి వెళుతుంది, అది దానిని పోర్ట్ 7777లోని నెట్‌వర్క్‌కు పంపుతుంది.

మనం చేయాల్సిందల్లా ఈ పేరున్న పైప్‌లో మన డేటాను రాయడం ప్రారంభించడమే.

మేము ఇప్పటికే అప్లికేషన్ అమలులో ఉన్నాము:

[user@localhost ~]$ ps axuf | grep [o]pen
user  5946 99.8  0.0 128600  5744 pts/22   R+   11:27 169:27  |   _ python openforwrite.py
[user@localhost ~]$ ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user  0 Oct  8 11:27 .
dr-xr-xr-x 9 user user  0 Oct  8 11:27 ..
lrwx------ 1 user user 64 Oct  8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct  8 11:28 3 -> /mnt/logs/123.txt

అన్ని ఫ్లాగ్‌లలో, ఫైల్ ఇప్పటికే ఉన్నందున మాకు O_WRONLY మాత్రమే అవసరం మరియు మేము దానిని క్లియర్ చేయాల్సిన అవసరం లేదు

[user@localhost ~]$ gdb -p 5946
...
(gdb) call open("/mnt/logs/megapipe", 00000001,0666)
$1 = 4
(gdb) shell ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user  0 Oct  8 11:27 .
dr-xr-xr-x 9 user user  0 Oct  8 11:27 ..
lrwx------ 1 user user 64 Oct  8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct  8 11:28 3 -> /mnt/logs/123.txt
l-wx------ 1 user user 64 Oct  8 14:20 4 -> /mnt/logs/megapipe
(gdb) call dup2(4,3)
$2 = 3
(gdb) shell ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user  0 Oct  8 11:27 .
dr-xr-xr-x 9 user user  0 Oct  8 11:27 ..
lrwx------ 1 user user 64 Oct  8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct  8 11:28 3 -> /mnt/logs/megapipe
l-wx------ 1 user user 64 Oct  8 14:20 4 -> /mnt/logs/megapipe
(gdb) call close(4)
$3 = 0
(gdb) shell ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user  0 Oct  8 11:27 .
dr-xr-xr-x 9 user user  0 Oct  8 11:27 ..
lrwx------ 1 user user 64 Oct  8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct  8 11:28 3 -> /mnt/logs/megapipe
(gdb) quit
A debugging session is active.

    Inferior 1 [process 5946] will be detached.

Quit anyway? (y or n) y
Detaching from program: /usr/bin/python2.7, process 5946

రిమోట్ సర్వర్‌ని తనిఖీ చేస్తోంది remote-server.example.com

[user@localhost ~]$ ls -lah 123.txt 
-rw-rw-r-- 1 user user 38M Oct  8 14:21 123.txt

డేటా వస్తోంది, మేము సమస్య సర్వర్‌ని తనిఖీ చేస్తాము

[user@localhost ~]$ ls -lah /mnt/logs/
total 7.9M
drwxr-xr-x 2 user user 1.0K Oct  8 11:28 .
drwxr-xr-x 4 root     root     1.0K Oct  8 10:55 ..
-rw-rw-r-- 1 user user 7.9M Oct  8 14:17 123.txt
prw-rw-r-- 1 user user    0 Oct  8 14:22 megapipe

డేటా సేవ్ చేయబడింది, సమస్య పరిష్కరించబడుతుంది.

డెగిరో నుండి నా సహోద్యోగులకు హలో చెప్పడానికి నేను ఈ అవకాశాన్ని ఉపయోగించుకుంటాను.
రేడియో-టి పాడ్‌క్యాస్ట్‌లను వినండి.

అంతా మంచి జరుగుగాక.

హోంవర్క్‌గా, మీరు ఈ క్రింది ఆదేశాన్ని అమలు చేస్తే, క్యాట్ అండ్ స్లీప్ డిస్క్రిప్టర్స్ ఫైల్‌లో ఏమి ఉంటుందో ఆలోచించమని నేను మీకు సూచిస్తున్నాను:

[user@localhost ~]$ cat /dev/zero 2>/dev/null| sleep 10000

మూలం: www.habr.com

ఒక వ్యాఖ్యను జోడించండి