ಚಿಕ್ಕ ಮಕ್ಕಳಿಗಾಗಿ BPF, ಭಾಗ ಒಂದು: ವಿಸ್ತೃತ BPF

ಆರಂಭದಲ್ಲಿ ಒಂದು ತಂತ್ರಜ್ಞಾನವಿತ್ತು ಮತ್ತು ಅದನ್ನು ಬಿಪಿಎಫ್ ಎಂದು ಕರೆಯಲಾಗುತ್ತಿತ್ತು. ನಾವು ಅದನ್ನು ಹಿಂದಿನ, ಹಳೆಯ ಒಡಂಬಡಿಕೆ, ಈ ಸರಣಿಯ ಲೇಖನ. 2013 ರಲ್ಲಿ, ಅಲೆಕ್ಸಿ ಸ್ಟಾರೊವೊಯಿಟೋವ್ ಮತ್ತು ಡೇನಿಯಲ್ ಬೋರ್ಕ್‌ಮನ್ ಅವರ ಪ್ರಯತ್ನಗಳ ಮೂಲಕ, ಆಧುನಿಕ 64-ಬಿಟ್ ಯಂತ್ರಗಳಿಗೆ ಹೊಂದುವಂತೆ ಅದರ ಸುಧಾರಿತ ಆವೃತ್ತಿಯನ್ನು ಅಭಿವೃದ್ಧಿಪಡಿಸಲಾಯಿತು ಮತ್ತು ಲಿನಕ್ಸ್ ಕರ್ನಲ್‌ನಲ್ಲಿ ಸೇರಿಸಲಾಯಿತು. ಈ ಹೊಸ ತಂತ್ರಜ್ಞಾನವನ್ನು ಸಂಕ್ಷಿಪ್ತವಾಗಿ ಆಂತರಿಕ ಬಿಪಿಎಫ್ ಎಂದು ಕರೆಯಲಾಯಿತು, ನಂತರ ವಿಸ್ತೃತ ಬಿಪಿಎಫ್ ಎಂದು ಮರುನಾಮಕರಣ ಮಾಡಲಾಯಿತು ಮತ್ತು ಈಗ, ಹಲವಾರು ವರ್ಷಗಳ ನಂತರ, ಎಲ್ಲರೂ ಇದನ್ನು ಬಿಪಿಎಫ್ ಎಂದು ಕರೆಯುತ್ತಾರೆ.

ಸ್ಥೂಲವಾಗಿ ಹೇಳುವುದಾದರೆ, BPF, ಲಿನಕ್ಸ್ ಕರ್ನಲ್ ಜಾಗದಲ್ಲಿ ಅನಿಯಂತ್ರಿತ ಬಳಕೆದಾರ-ಒದಗಿಸಿದ ಕೋಡ್ ಅನ್ನು ಚಲಾಯಿಸಲು ಅನುಮತಿಸುತ್ತದೆ, ಮತ್ತು ಹೊಸ ವಾಸ್ತುಶಿಲ್ಪವು ಎಷ್ಟು ಯಶಸ್ವಿಯಾಗಿದೆ ಎಂದರೆ ಅದರ ಎಲ್ಲಾ ಉಪಯೋಗಗಳನ್ನು ವಿವರಿಸಲು ನಮಗೆ ಇನ್ನೂ ಒಂದು ಡಜನ್ ಲೇಖನಗಳು ಬೇಕಾಗುತ್ತವೆ. (ಕೆಳಗಿನ KPDV ಯಲ್ಲಿ ನೀವು ನೋಡಬಹುದಾದಂತೆ, ಡೆವಲಪರ್‌ಗಳು ಮಾಡಲು ವಿಫಲವಾದ ಏಕೈಕ ವಿಷಯವೆಂದರೆ ಯೋಗ್ಯವಾದ ಲೋಗೋವನ್ನು ರಚಿಸುವುದು.)

ಈ ಲೇಖನವು BPF ವರ್ಚುವಲ್ ಯಂತ್ರದ ರಚನೆ, BPF ನೊಂದಿಗೆ ಕೆಲಸ ಮಾಡಲು ಕರ್ನಲ್ ಇಂಟರ್ಫೇಸ್‌ಗಳು, ಅಭಿವೃದ್ಧಿ ಪರಿಕರಗಳು ಮತ್ತು ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಸಾಮರ್ಥ್ಯಗಳ ಸಂಕ್ಷಿಪ್ತ, ಅತ್ಯಂತ ಸಂಕ್ಷಿಪ್ತ ಅವಲೋಕನವನ್ನು ವಿವರಿಸುತ್ತದೆ, ಅಂದರೆ BPF ನ ಪ್ರಾಯೋಗಿಕ ಅನ್ವಯಿಕೆಗಳ ಹೆಚ್ಚು ಆಳವಾದ ಅಧ್ಯಯನಕ್ಕಾಗಿ ಭವಿಷ್ಯದಲ್ಲಿ ನಮಗೆ ಅಗತ್ಯವಿರುವ ಎಲ್ಲವನ್ನೂ ವಿವರಿಸುತ್ತದೆ.
ಚಿಕ್ಕ ಮಕ್ಕಳಿಗಾಗಿ BPF, ಭಾಗ ಒಂದು: ವಿಸ್ತೃತ BPF

ಲೇಖನದ ಸಾರಾಂಶ

ಬಿಪಿಎಫ್ ವಾಸ್ತುಶಿಲ್ಪದ ಪರಿಚಯ. ನಾವು ಮೊದಲು ಬಿಪಿಎಫ್ ವಾಸ್ತುಶಿಲ್ಪದ ಪಕ್ಷಿನೋಟವನ್ನು ತೆಗೆದುಕೊಂಡು ಮುಖ್ಯ ಘಟಕಗಳನ್ನು ರೂಪಿಸುತ್ತೇವೆ.

ಬಿಪಿಎಫ್ ವರ್ಚುವಲ್ ಯಂತ್ರದ ರಿಜಿಸ್ಟರ್‌ಗಳು ಮತ್ತು ಆಜ್ಞಾ ವ್ಯವಸ್ಥೆ. ಒಟ್ಟಾರೆಯಾಗಿ ವಾಸ್ತುಶಿಲ್ಪದ ಕಲ್ಪನೆಯನ್ನು ಈಗಾಗಲೇ ಪಡೆದ ನಂತರ, ನಾವು BPF ವರ್ಚುವಲ್ ಯಂತ್ರದ ರಚನೆಯನ್ನು ವಿವರಿಸುತ್ತೇವೆ.

ಬಿಪಿಎಫ್ ವಸ್ತುಗಳ ಜೀವನ ಚಕ್ರ, ಬಿಪಿಎಫ್ಎಫ್ ಫೈಲ್ ಸಿಸ್ಟಮ್. ಈ ವಿಭಾಗದಲ್ಲಿ ನಾವು BPF ವಸ್ತುಗಳ ಜೀವನ ಚಕ್ರವನ್ನು ಹತ್ತಿರದಿಂದ ನೋಡೋಣ - ಕಾರ್ಯಕ್ರಮಗಳು ಮತ್ತು ನಕ್ಷೆಗಳು.

ಬಿಪಿಎಫ್ ಸಿಸ್ಟಮ್ ಕರೆಯನ್ನು ಬಳಸಿಕೊಂಡು ವಸ್ತುಗಳನ್ನು ನಿರ್ವಹಿಸುವುದು. ಸಿಸ್ಟಮ್ ಬಗ್ಗೆ ಸ್ವಲ್ಪ ಕಲ್ಪನೆಯನ್ನು ಹೊಂದಿರುವ ನಾವು, ವಿಶೇಷ ಸಿಸ್ಟಮ್ ಕರೆಯನ್ನು ಬಳಸಿಕೊಂಡು ಬಳಕೆದಾರ ಸ್ಥಳದಿಂದ ವಸ್ತುಗಳನ್ನು ಹೇಗೆ ರಚಿಸುವುದು ಮತ್ತು ನಿರ್ವಹಿಸುವುದು ಎಂಬುದನ್ನು ಅಂತಿಮವಾಗಿ ನೋಡೋಣ - bpf(2).

Пишем программы BPF с помощью libbpf. ಸಹಜವಾಗಿ, ಸಿಸ್ಟಮ್ ಕರೆಯನ್ನು ಬಳಸಿಕೊಂಡು ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಬರೆಯಲು ಸಾಧ್ಯವಿದೆ. ಆದರೆ ಅದು ಕಷ್ಟ. ಹೆಚ್ಚು ವಾಸ್ತವಿಕ ಸನ್ನಿವೇಶಕ್ಕಾಗಿ, ನ್ಯೂಕ್ಲಿಯರ್ ಪ್ರೋಗ್ರಾಮರ್‌ಗಳು ಲೈಬ್ರರಿಯನ್ನು ಅಭಿವೃದ್ಧಿಪಡಿಸಿದರು. libbpf. ನಾವು ಈ ಕೆಳಗಿನ ಉದಾಹರಣೆಗಳಲ್ಲಿ ಬಳಸುವ BPF ಅಪ್ಲಿಕೇಶನ್‌ನ ಅತ್ಯಂತ ಸರಳವಾದ ಅಸ್ಥಿಪಂಜರವನ್ನು ರಚಿಸುತ್ತೇವೆ.

ಕರ್ನಲ್ ಸಹಾಯಕರು. ಇಲ್ಲಿ ನಾವು BPF ಪ್ರೋಗ್ರಾಂಗಳು ಕರ್ನಲ್‌ನ ಸಹಾಯಕ ಕಾರ್ಯಗಳನ್ನು ಹೇಗೆ ಪ್ರವೇಶಿಸಬಹುದು ಎಂಬುದನ್ನು ಕಲಿಯುತ್ತೇವೆ, ಇದು ನಕ್ಷೆಗಳ ಜೊತೆಗೆ, ಕ್ಲಾಸಿಕ್ ಒಂದಕ್ಕೆ ಹೋಲಿಸಿದರೆ ಹೊಸ BPF ನ ಸಾಮರ್ಥ್ಯಗಳನ್ನು ಮೂಲಭೂತವಾಗಿ ವಿಸ್ತರಿಸುವ ಸಾಧನವಾಗಿದೆ.

ಬಿಪಿಎಫ್ ಕಾರ್ಯಕ್ರಮಗಳಿಂದ ನಕ್ಷೆಗಳನ್ನು ಪ್ರವೇಶಿಸುವುದು. ಈ ಹೊತ್ತಿಗೆ, ನಕ್ಷೆಗಳನ್ನು ಬಳಸುವ ಕಾರ್ಯಕ್ರಮಗಳನ್ನು ನಾವು ಹೇಗೆ ನಿಖರವಾಗಿ ರಚಿಸಬಹುದು ಎಂಬುದನ್ನು ಅರ್ಥಮಾಡಿಕೊಳ್ಳುವಷ್ಟು ನಮಗೆ ತಿಳಿದಿರುತ್ತದೆ. ಮತ್ತು ನಾವು ಆ ಶ್ರೇಷ್ಠ ಮತ್ತು ಪ್ರಬಲ ಪರಿಶೀಲಕವನ್ನು ಸಹ ತ್ವರಿತವಾಗಿ ಪರಿಶೀಲಿಸುತ್ತೇವೆ.

ಅಭಿವೃದ್ಧಿ ಪರಿಕರಗಳು. ಪ್ರಯೋಗಕ್ಕಾಗಿ ಅಗತ್ಯವಿರುವ ಉಪಯುಕ್ತತೆಗಳು ಮತ್ತು ಕರ್ನಲ್ ಅನ್ನು ಹೇಗೆ ನಿರ್ಮಿಸುವುದು ಎಂಬುದರ ಕುರಿತು ಒಂದು ಉಲ್ಲೇಖ ವಿಭಾಗ.

ತೀರ್ಮಾನ. ಲೇಖನದ ಕೊನೆಯಲ್ಲಿ, ಇಲ್ಲಿಯವರೆಗೆ ಓದಿದವರು ಪ್ರೇರಣಾದಾಯಕ ಪದಗಳನ್ನು ಮತ್ತು ಮುಂದಿನ ಲೇಖನಗಳಲ್ಲಿ ಏನಾಗಲಿದೆ ಎಂಬುದರ ಸಂಕ್ಷಿಪ್ತ ವಿವರಣೆಯನ್ನು ಕಾಣಬಹುದು. ಮುಂದುವರಿಕೆಗಾಗಿ ಕಾಯಲು ಬಯಸದ ಅಥವಾ ಅವಕಾಶವಿಲ್ಲದವರಿಗೆ ಸ್ವತಂತ್ರ ಅಧ್ಯಯನಕ್ಕಾಗಿ ನಾವು ಹಲವಾರು ಲಿಂಕ್‌ಗಳನ್ನು ಸಹ ಪಟ್ಟಿ ಮಾಡುತ್ತೇವೆ.

ಬಿಪಿಎಫ್ ವಾಸ್ತುಶಿಲ್ಪದ ಪರಿಚಯ

ಬಿಪಿಎಫ್ ವಾಸ್ತುಶಿಲ್ಪವನ್ನು ನೋಡುವ ಮೊದಲು, ನಾವು ಕೊನೆಯ ಬಾರಿಗೆ (ಓಹ್, ನಿಜವಾಗಿಯೂ) ಉಲ್ಲೇಖಿಸುತ್ತೇವೆ ಕ್ಲಾಸಿಕ್ ಬಿಪಿಎಫ್RISC ಯಂತ್ರಗಳ ಆಗಮನಕ್ಕೆ ಪ್ರತಿಕ್ರಿಯೆಯಾಗಿ ಅಭಿವೃದ್ಧಿಪಡಿಸಲಾಯಿತು ಮತ್ತು ಪರಿಣಾಮಕಾರಿ ಪ್ಯಾಕೆಟ್ ಫಿಲ್ಟರಿಂಗ್ ಸಮಸ್ಯೆಯನ್ನು ಪರಿಹರಿಸಲಾಯಿತು. ವಾಸ್ತುಶಿಲ್ಪವು ಎಷ್ಟು ಯಶಸ್ವಿಯಾಯಿತು ಎಂದರೆ, ತೊಂಬತ್ತರ ದಶಕದಲ್ಲಿ ಬರ್ಕ್ಲಿ UNIX ನಲ್ಲಿ ಜನಿಸಿದ ನಂತರ, ಅದನ್ನು ಹೆಚ್ಚಿನ ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಆಪರೇಟಿಂಗ್ ಸಿಸ್ಟಮ್‌ಗಳಿಗೆ ಪೋರ್ಟ್ ಮಾಡಲಾಯಿತು, ಇಪ್ಪತ್ತರ ದಶಕದವರೆಗೆ ಇತ್ತು ಮತ್ತು ಇನ್ನೂ ಹೊಸ ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಹುಡುಕುತ್ತಿದೆ.

64-ಬಿಟ್ ಯಂತ್ರಗಳು, ಕ್ಲೌಡ್ ಸೇವೆಗಳು ಮತ್ತು SDN ಅನ್ನು ನಿರ್ಮಿಸಲು ಪರಿಕರಗಳ ಹೆಚ್ಚಿದ ಅಗತ್ಯಕ್ಕೆ ಪ್ರತಿಕ್ರಿಯೆಯಾಗಿ ಹೊಸ BPF ಅನ್ನು ಅಭಿವೃದ್ಧಿಪಡಿಸಲಾಗಿದೆ (Sಆಗಾಗ್ಗೆ-dವ್ಯಾಖ್ಯಾನಿಸಲಾಗಿದೆ networking). ಕ್ಲಾಸಿಕ್ BPF ಗೆ ಸುಧಾರಿತ ಬದಲಿಯಾಗಿ ಕರ್ನಲ್ ನೆಟ್‌ವರ್ಕ್ ಎಂಜಿನಿಯರ್‌ಗಳು ಅಭಿವೃದ್ಧಿಪಡಿಸಿದ ಹೊಸ BPF, ಆರು ತಿಂಗಳೊಳಗೆ ಲಿನಕ್ಸ್ ವ್ಯವಸ್ಥೆಗಳನ್ನು ಪತ್ತೆಹಚ್ಚುವ ಕಷ್ಟಕರ ಕೆಲಸದಲ್ಲಿ ಅಕ್ಷರಶಃ ಅನ್ವಯವನ್ನು ಕಂಡುಕೊಂಡಿತು ಮತ್ತು ಈಗ, ಅದು ಕಾಣಿಸಿಕೊಂಡ ಆರು ವರ್ಷಗಳ ನಂತರ, ವಿವಿಧ ರೀತಿಯ ಕಾರ್ಯಕ್ರಮಗಳನ್ನು ಪಟ್ಟಿ ಮಾಡಲು ನಮಗೆ ಸಂಪೂರ್ಣ, ಮುಂದಿನ, ಲೇಖನದ ಅಗತ್ಯವಿದೆ.

ತಮಾಷೆಯ ಚಿತ್ರಗಳು

ಅದರ ಮೂಲದಲ್ಲಿ, BPF ಒಂದು ವರ್ಚುವಲ್ ಸ್ಯಾಂಡ್‌ಬಾಕ್ಸ್ ಯಂತ್ರವಾಗಿದ್ದು, ಭದ್ರತೆಗೆ ಧಕ್ಕೆಯಾಗದಂತೆ ಕರ್ನಲ್ ಜಾಗದಲ್ಲಿ "ಅನಿಯಂತ್ರಿತ" ಕೋಡ್ ಅನ್ನು ಚಲಾಯಿಸಲು ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ. BPF ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಬಳಕೆದಾರ ಜಾಗದಲ್ಲಿ ರಚಿಸಲಾಗುತ್ತದೆ, ಕರ್ನಲ್‌ಗೆ ಲೋಡ್ ಮಾಡಲಾಗುತ್ತದೆ ಮತ್ತು ಕೆಲವು ಈವೆಂಟ್ ಮೂಲಕ್ಕೆ ಸಂಪರ್ಕಿಸಲಾಗುತ್ತದೆ. ಒಂದು ಈವೆಂಟ್, ಉದಾಹರಣೆಗೆ, ನೆಟ್‌ವರ್ಕ್ ಇಂಟರ್ಫೇಸ್‌ಗೆ ಪ್ಯಾಕೆಟ್ ಅನ್ನು ತಲುಪಿಸುವುದು, ಕರ್ನಲ್ ಕಾರ್ಯವನ್ನು ಪ್ರಾರಂಭಿಸುವುದು ಇತ್ಯಾದಿ ಆಗಿರಬಹುದು. ಪ್ಯಾಕೆಟ್‌ನ ಸಂದರ್ಭದಲ್ಲಿ, BPF ಪ್ರೋಗ್ರಾಂ ಪ್ಯಾಕೆಟ್‌ನ ಡೇಟಾ ಮತ್ತು ಮೆಟಾಡೇಟಾಗೆ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿರುತ್ತದೆ (ಓದಲು ಮತ್ತು ಬಹುಶಃ ಬರೆಯಲು, ಪ್ರೋಗ್ರಾಂ ಪ್ರಕಾರವನ್ನು ಅವಲಂಬಿಸಿ); ಕರ್ನಲ್ ಕಾರ್ಯವನ್ನು ಪ್ರಾರಂಭಿಸುವ ಸಂದರ್ಭದಲ್ಲಿ, ಕರ್ನಲ್ ಮೆಮೊರಿಗೆ ಪಾಯಿಂಟರ್‌ಗಳನ್ನು ಒಳಗೊಂಡಂತೆ ಕಾರ್ಯದ ವಾದಗಳು, ಇತ್ಯಾದಿ.

ಈ ಪ್ರಕ್ರಿಯೆಯನ್ನು ಹತ್ತಿರದಿಂದ ನೋಡೋಣ. ಮೊದಲು, ಕ್ಲಾಸಿಕ್ BPF ಗಿಂತ ಮೊದಲ ವ್ಯತ್ಯಾಸದ ಬಗ್ಗೆ ಮಾತನಾಡೋಣ, ಇದಕ್ಕಾಗಿ ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಅಸೆಂಬ್ಲರ್‌ನಲ್ಲಿ ಬರೆಯಲಾಗಿದೆ. ಹೊಸ ಆವೃತ್ತಿಯಲ್ಲಿ, ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಉನ್ನತ ಮಟ್ಟದ ಭಾಷೆಗಳಲ್ಲಿ ಬರೆಯಲು ಸಾಧ್ಯವಾಗುವಂತೆ ವಾಸ್ತುಶಿಲ್ಪವನ್ನು ಪೂರಕಗೊಳಿಸಲಾಗಿದೆ, ಪ್ರಾಥಮಿಕವಾಗಿ, ಸಹಜವಾಗಿ, C ನಲ್ಲಿ. ಈ ಉದ್ದೇಶಕ್ಕಾಗಿ, llvm ಗಾಗಿ ಬ್ಯಾಕೆಂಡ್ ಅನ್ನು ಅಭಿವೃದ್ಧಿಪಡಿಸಲಾಯಿತು, ಇದು BPF ವಾಸ್ತುಶಿಲ್ಪಕ್ಕಾಗಿ ಬೈಟ್‌ಕೋಡ್ ಉತ್ಪಾದನೆಯನ್ನು ಅನುಮತಿಸುತ್ತದೆ.

ಚಿಕ್ಕ ಮಕ್ಕಳಿಗಾಗಿ BPF, ಭಾಗ ಒಂದು: ವಿಸ್ತೃತ BPF

ಬಿಪಿಎಫ್ ಆರ್ಕಿಟೆಕ್ಚರ್ ಅನ್ನು ಭಾಗಶಃ ಆಧುನಿಕ ಯಂತ್ರಗಳಲ್ಲಿ ಪರಿಣಾಮಕಾರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸಲು ವಿನ್ಯಾಸಗೊಳಿಸಲಾಗಿದೆ. ಇದನ್ನು ಪ್ರಾಯೋಗಿಕವಾಗಿ ಕೆಲಸ ಮಾಡಲು, ಬಿಪಿಎಫ್ ಬೈಟ್‌ಕೋಡ್ ಅನ್ನು ಕರ್ನಲ್‌ಗೆ ಲೋಡ್ ಮಾಡಿದ ನಂತರ, ಜೆಐಟಿ ಕಂಪೈಲರ್ ಎಂಬ ಘಟಕವನ್ನು ಬಳಸಿಕೊಂಡು ಸ್ಥಳೀಯ ಕೋಡ್‌ಗೆ ಅನುವಾದಿಸಲಾಗುತ್ತದೆ (Just In Time). ಈಗ, ನಿಮಗೆ ನೆನಪಿದ್ದರೆ, ಕ್ಲಾಸಿಕ್ BPF ನಲ್ಲಿ, ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಕರ್ನಲ್‌ಗೆ ಲೋಡ್ ಮಾಡಲಾಗಿದೆ ಮತ್ತು ಈವೆಂಟ್ ಮೂಲಕ್ಕೆ ಪರಮಾಣುವಾಗಿ ಲಗತ್ತಿಸಲಾಗಿದೆ - ಒಂದು ಸಿಸ್ಟಮ್ ಕರೆಯ ಸಂದರ್ಭದಲ್ಲಿ. ಹೊಸ ವಾಸ್ತುಶಿಲ್ಪದಲ್ಲಿ, ಇದು ಎರಡು ಹಂತಗಳಲ್ಲಿ ಸಂಭವಿಸುತ್ತದೆ - ಮೊದಲು, ಕೋಡ್ ಅನ್ನು ಸಿಸ್ಟಮ್ ಕರೆಯನ್ನು ಬಳಸಿಕೊಂಡು ಕರ್ನಲ್‌ಗೆ ಲೋಡ್ ಮಾಡಲಾಗುತ್ತದೆ. bpf(2), ಮತ್ತು ನಂತರ, ನಂತರ, ಪ್ರೋಗ್ರಾಂ ಪ್ರಕಾರವನ್ನು ಅವಲಂಬಿಸಿ ಬದಲಾಗುವ ಇತರ ಕಾರ್ಯವಿಧಾನಗಳನ್ನು ಬಳಸಿಕೊಂಡು, ಪ್ರೋಗ್ರಾಂ ಈವೆಂಟ್ ಮೂಲಕ್ಕೆ ಲಗತ್ತಿಸುತ್ತದೆ.

ಇಲ್ಲಿ ಓದುಗರು ಕೇಳಬಹುದು: ಹಾಗೆ ಮಾಡಲು ಸಾಧ್ಯವೇ? ಅಂತಹ ಕೋಡ್ ಅನ್ನು ಕಾರ್ಯಗತಗೊಳಿಸುವ ಸುರಕ್ಷತೆಯನ್ನು ಹೇಗೆ ಖಾತರಿಪಡಿಸಲಾಗುತ್ತದೆ? ವೆರಿಫೈಯರ್ ಎಂದು ಕರೆಯಲ್ಪಡುವ ಬಿಪಿಎಫ್ ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಲೋಡ್ ಮಾಡುವ ಹಂತದಿಂದ ಕಾರ್ಯಗತಗೊಳಿಸುವಿಕೆಯ ಸುರಕ್ಷತೆಯನ್ನು ನಮಗೆ ಖಾತರಿಪಡಿಸಲಾಗುತ್ತದೆ (ಇಂಗ್ಲಿಷ್‌ನಲ್ಲಿ ಈ ಹಂತವನ್ನು ವೆರಿಫೈಯರ್ ಎಂದು ಕರೆಯಲಾಗುತ್ತದೆ ಮತ್ತು ನಾನು ಇಂಗ್ಲಿಷ್ ಪದವನ್ನು ಮುಂದೆ ಬಳಸುತ್ತೇನೆ):

ಚಿಕ್ಕ ಮಕ್ಕಳಿಗಾಗಿ BPF, ಭಾಗ ಒಂದು: ವಿಸ್ತೃತ BPF

ವೆರಿಫೈಯರ್ ಒಂದು ಸ್ಥಿರ ವಿಶ್ಲೇಷಕವಾಗಿದ್ದು, ಪ್ರೋಗ್ರಾಂ ಕರ್ನಲ್‌ನ ಸಾಮಾನ್ಯ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ಅಡ್ಡಿಪಡಿಸುವುದಿಲ್ಲ ಎಂದು ಖಚಿತಪಡಿಸುತ್ತದೆ. ಪ್ರಾಸಂಗಿಕವಾಗಿ, ಪ್ರೋಗ್ರಾಂ ವ್ಯವಸ್ಥೆಯಲ್ಲಿ ಹಸ್ತಕ್ಷೇಪ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ ಎಂದು ಇದರ ಅರ್ಥವಲ್ಲ - ಬಿಪಿಎಫ್ ಪ್ರೋಗ್ರಾಂಗಳು, ಪ್ರಕಾರವನ್ನು ಅವಲಂಬಿಸಿ, ಕರ್ನಲ್ ಮೆಮೊರಿಯ ವಿಭಾಗಗಳನ್ನು ಓದಬಹುದು ಮತ್ತು ಪುನಃ ಬರೆಯಬಹುದು, ಕಾರ್ಯಗಳ ಮೌಲ್ಯಗಳನ್ನು ಹಿಂತಿರುಗಿಸಬಹುದು, ಕತ್ತರಿಸಬಹುದು, ಪೂರಕಗೊಳಿಸಬಹುದು, ಪುನಃ ಬರೆಯಬಹುದು ಮತ್ತು ನೆಟ್‌ವರ್ಕ್ ಪ್ಯಾಕೆಟ್‌ಗಳನ್ನು ಫಾರ್ವರ್ಡ್ ಮಾಡಬಹುದು. ಬಿಪಿಎಫ್ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಚಲಾಯಿಸುವಾಗ ಕರ್ನಲ್ ಕ್ರ್ಯಾಶ್ ಆಗುವುದಿಲ್ಲ ಮತ್ತು ನಿಯಮಗಳ ಪ್ರಕಾರ, ಹೊರಹೋಗುವ ಪ್ಯಾಕೆಟ್ ಡೇಟಾಗೆ ಬರೆಯುವ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿರುವ ಪ್ರೋಗ್ರಾಂ, ಉದಾಹರಣೆಗೆ, ಪ್ಯಾಕೆಟ್‌ನ ಹೊರಗೆ ಕರ್ನಲ್ ಮೆಮೊರಿಯನ್ನು ಪುನಃ ಬರೆಯಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ ಎಂದು ವೆರಿಫೈಯರ್ ಖಚಿತಪಡಿಸುತ್ತದೆ. ಎಲ್ಲಾ ಇತರ ಬಿಪಿಎಫ್ ಘಟಕಗಳೊಂದಿಗೆ ನಾವು ಪರಿಚಯವಾದ ನಂತರ, ಅನುಗುಣವಾದ ವಿಭಾಗದಲ್ಲಿ ವೆರಿಫೈಯರ್ ಅನ್ನು ಸ್ವಲ್ಪ ಹೆಚ್ಚು ವಿವರವಾಗಿ ನೋಡುತ್ತೇವೆ.

ಹಾಗಾದರೆ ನಾವು ಇಲ್ಲಿಯವರೆಗೆ ಏನು ಕಲಿತಿದ್ದೇವೆ? ಬಳಕೆದಾರರು C ನಲ್ಲಿ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಬರೆಯುತ್ತಾರೆ, ಸಿಸ್ಟಮ್ ಕಾಲ್ ಬಳಸಿ ಅದನ್ನು ಕರ್ನಲ್‌ಗೆ ಲೋಡ್ ಮಾಡುತ್ತಾರೆ. bpf(2), ಅಲ್ಲಿ ಅದನ್ನು ಪರಿಶೀಲಕವು ಪರಿಶೀಲಿಸುತ್ತದೆ ಮತ್ತು ಸ್ಥಳೀಯ ಬೈಟ್‌ಕೋಡ್‌ಗೆ ಅನುವಾದಿಸುತ್ತದೆ. ನಂತರ ಅದೇ ಅಥವಾ ಇನ್ನೊಬ್ಬ ಬಳಕೆದಾರರು ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಈವೆಂಟ್ ಮೂಲಕ್ಕೆ ಲಗತ್ತಿಸುತ್ತಾರೆ ಮತ್ತು ಅದು ಕಾರ್ಯಗತಗೊಳಿಸಲು ಪ್ರಾರಂಭಿಸುತ್ತದೆ. ಲೋಡಿಂಗ್ ಮತ್ತು ಲಗತ್ತಿಸುವಿಕೆಯನ್ನು ಬೇರ್ಪಡಿಸುವುದು ಹಲವಾರು ಕಾರಣಗಳಿಗಾಗಿ ಅವಶ್ಯಕವಾಗಿದೆ. ಮೊದಲನೆಯದಾಗಿ, ಪರಿಶೀಲಕವನ್ನು ಚಲಾಯಿಸುವುದು ತುಲನಾತ್ಮಕವಾಗಿ ದುಬಾರಿಯಾಗಿದೆ ಮತ್ತು ಅದೇ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಹಲವಾರು ಬಾರಿ ಲೋಡ್ ಮಾಡುವ ಮೂಲಕ ನಾವು ಕಂಪ್ಯೂಟರ್ ಸಮಯವನ್ನು ವ್ಯರ್ಥ ಮಾಡುತ್ತೇವೆ. ಎರಡನೆಯದಾಗಿ, ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಹೇಗೆ ನಿಖರವಾಗಿ ಲಗತ್ತಿಸಲಾಗಿದೆ ಎಂಬುದು ಅದರ ಪ್ರಕಾರವನ್ನು ಅವಲಂಬಿಸಿರುತ್ತದೆ ಮತ್ತು ಒಂದು ವರ್ಷದ ಹಿಂದೆ ಅಭಿವೃದ್ಧಿಪಡಿಸಲಾದ ಒಂದು "ಸಾರ್ವತ್ರಿಕ" ಇಂಟರ್ಫೇಸ್ ಹೊಸ ರೀತಿಯ ಕಾರ್ಯಕ್ರಮಗಳಿಗೆ ಸೂಕ್ತವಲ್ಲದಿರಬಹುದು. (ಈಗ, ವಾಸ್ತುಶಿಲ್ಪವು ಹೆಚ್ಚು ಪ್ರಬುದ್ಧವಾಗುತ್ತಿರುವಾಗ, ಮಟ್ಟದಲ್ಲಿ ಈ ಇಂಟರ್ಫೇಸ್ ಅನ್ನು ಏಕೀಕರಿಸುವ ಕಲ್ಪನೆ ಇದೆ libbpf.)

ಗಮನ ಸೆಳೆಯುವ ಓದುಗರು ನಾವು ಇನ್ನೂ ಚಿತ್ರಗಳೊಂದಿಗೆ ಮುಗಿಸಿಲ್ಲ ಎಂದು ಗಮನಿಸಬಹುದು. ವಾಸ್ತವವಾಗಿ, ಮೇಲಿನ ಎಲ್ಲವೂ ಕ್ಲಾಸಿಕ್ BPF ಗೆ ಹೋಲಿಸಿದರೆ BPF ಮೂಲಭೂತವಾಗಿ ಚಿತ್ರವನ್ನು ಹೇಗೆ ಬದಲಾಯಿಸುತ್ತದೆ ಎಂಬುದನ್ನು ವಿವರಿಸುವುದಿಲ್ಲ. ಅನ್ವಯಿಸುವಿಕೆಯ ಗಡಿಗಳನ್ನು ಗಮನಾರ್ಹವಾಗಿ ವಿಸ್ತರಿಸುವ ಎರಡು ನಾವೀನ್ಯತೆಗಳೆಂದರೆ ಹಂಚಿಕೆಯ ಮೆಮೊರಿ ಮತ್ತು ಕರ್ನಲ್ ಸಹಾಯಕಗಳನ್ನು ಬಳಸುವ ಸಾಮರ್ಥ್ಯ. BPF ನಲ್ಲಿ, ಹಂಚಿಕೆಯ ಮೆಮೊರಿಯನ್ನು ನಕ್ಷೆಗಳು ಎಂದು ಕರೆಯಲ್ಪಡುವ - ನಿರ್ದಿಷ್ಟ API ನೊಂದಿಗೆ ಹಂಚಿಕೆಯ ಡೇಟಾ ರಚನೆಗಳನ್ನು ಬಳಸಿಕೊಂಡು ಕಾರ್ಯಗತಗೊಳಿಸಲಾಗುತ್ತದೆ. ಅವರು ಈ ಹೆಸರನ್ನು ಪಡೆದರು, ಬಹುಶಃ, ಮೊದಲ ನಕ್ಷೆ ಪ್ರಕಾರವು ಹ್ಯಾಶ್ ಟೇಬಲ್ ಆಗಿರುವುದರಿಂದ. ನಂತರ ಅರೇಗಳು, ಸ್ಥಳೀಯ (ಪ್ರತಿ-ಸಿಪಿಯು) ಹ್ಯಾಶ್ ಕೋಷ್ಟಕಗಳು ಮತ್ತು ಸ್ಥಳೀಯ ಅರೇಗಳು, ಹುಡುಕಾಟ ಮರಗಳು, BPF ಕಾರ್ಯಕ್ರಮಗಳಿಗೆ ಪಾಯಿಂಟರ್‌ಗಳನ್ನು ಹೊಂದಿರುವ ನಕ್ಷೆಗಳು ಮತ್ತು ಇನ್ನೂ ಹೆಚ್ಚಿನವು ಬಂದವು. ಈಗ ನಮಗೆ ಆಸಕ್ತಿಯ ವಿಷಯವೆಂದರೆ BPF ಕಾರ್ಯಕ್ರಮಗಳು ಕರೆಗಳ ನಡುವೆ ಸ್ಥಿತಿಯನ್ನು ಉಳಿಸುವ ಮತ್ತು ಅದನ್ನು ಇತರ ಕಾರ್ಯಕ್ರಮಗಳೊಂದಿಗೆ ಮತ್ತು ಬಳಕೆದಾರ ಸ್ಥಳದೊಂದಿಗೆ ಹಂಚಿಕೊಳ್ಳುವ ಸಾಮರ್ಥ್ಯವನ್ನು ಹೊಂದಿವೆ.

ಸಿಸ್ಟಮ್ ಕರೆಯನ್ನು ಬಳಸಿಕೊಂಡು ಬಳಕೆದಾರ ಪ್ರಕ್ರಿಯೆಗಳಿಂದ ನಕ್ಷೆಗಳನ್ನು ಪ್ರವೇಶಿಸಲಾಗುತ್ತದೆ. bpf(2), ಮತ್ತು ಕರ್ನಲ್‌ನಲ್ಲಿ ಚಾಲನೆಯಲ್ಲಿರುವ BPF ಪ್ರೋಗ್ರಾಂಗಳಿಂದ - ಸಹಾಯಕ ಕಾರ್ಯಗಳನ್ನು ಬಳಸುವುದು. ಇದಲ್ಲದೆ, ಸಹಾಯಕರು ನಕ್ಷೆಗಳೊಂದಿಗೆ ಕೆಲಸ ಮಾಡಲು ಮಾತ್ರವಲ್ಲದೆ, ಇತರ ಕರ್ನಲ್ ಸಾಮರ್ಥ್ಯಗಳನ್ನು ಪ್ರವೇಶಿಸಲು ಸಹ ಅಸ್ತಿತ್ವದಲ್ಲಿದ್ದಾರೆ. ಉದಾಹರಣೆಗೆ, BPF ಪ್ರೋಗ್ರಾಂಗಳು ಪ್ಯಾಕೆಟ್‌ಗಳನ್ನು ಇತರ ಇಂಟರ್ಫೇಸ್‌ಗಳಿಗೆ ಮರುನಿರ್ದೇಶಿಸಲು, perf ಉಪವ್ಯವಸ್ಥೆಯ ಈವೆಂಟ್‌ಗಳನ್ನು ಉತ್ಪಾದಿಸಲು, ಕರ್ನಲ್ ರಚನೆಗಳನ್ನು ಪ್ರವೇಶಿಸಲು ಸಹಾಯಕ ಕಾರ್ಯಗಳನ್ನು ಬಳಸಬಹುದು.

ಚಿಕ್ಕ ಮಕ್ಕಳಿಗಾಗಿ BPF, ಭಾಗ ಒಂದು: ವಿಸ್ತೃತ BPF

ಸಂಕ್ಷಿಪ್ತವಾಗಿ ಹೇಳುವುದಾದರೆ, BPF ಕರ್ನಲ್ ಜಾಗಕ್ಕೆ ಅನಿಯಂತ್ರಿತ, ಅಂದರೆ, ಪರಿಶೀಲಕ-ಪರೀಕ್ಷಿತ, ಬಳಕೆದಾರ ಕೋಡ್ ಅನ್ನು ಲೋಡ್ ಮಾಡುವ ಸಾಮರ್ಥ್ಯವನ್ನು ಒದಗಿಸುತ್ತದೆ. ಈ ಕೋಡ್ ಕರೆಗಳ ನಡುವಿನ ಸ್ಥಿತಿಯನ್ನು ಉಳಿಸಬಹುದು ಮತ್ತು ಬಳಕೆದಾರ ಸ್ಥಳದೊಂದಿಗೆ ಡೇಟಾವನ್ನು ವಿನಿಮಯ ಮಾಡಿಕೊಳ್ಳಬಹುದು ಮತ್ತು ಈ ರೀತಿಯ ಪ್ರೋಗ್ರಾಂಗೆ ಅನುಮತಿಸಲಾದ ಕರ್ನಲ್ ಉಪವ್ಯವಸ್ಥೆಗಳಿಗೆ ಪ್ರವೇಶವನ್ನು ಸಹ ಹೊಂದಿದೆ.

ಇದು ಈಗಾಗಲೇ ಕರ್ನಲ್ ಮಾಡ್ಯೂಲ್‌ಗಳು ಒದಗಿಸಿದ ಸಾಮರ್ಥ್ಯಗಳನ್ನು ಹೋಲುತ್ತದೆ, ಇದಕ್ಕೆ ಹೋಲಿಸಿದರೆ BPF ಕೆಲವು ಪ್ರಯೋಜನಗಳನ್ನು ಹೊಂದಿದೆ (ಖಂಡಿತ, ನೀವು ಇದೇ ರೀತಿಯ ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಮಾತ್ರ ಹೋಲಿಸಬಹುದು, ಉದಾಹರಣೆಗೆ, ಸಿಸ್ಟಮ್ ಟ್ರೇಸಿಂಗ್ - ನೀವು BPF ನಲ್ಲಿ ಅನಿಯಂತ್ರಿತ ಚಾಲಕವನ್ನು ಬರೆಯಲು ಸಾಧ್ಯವಿಲ್ಲ). ನಾವು ಕಡಿಮೆ ಪ್ರವೇಶ ಮಿತಿಯನ್ನು ಗಮನಿಸಬಹುದು (BPF ಬಳಸುವ ಕೆಲವು ಉಪಯುಕ್ತತೆಗಳು ಬಳಕೆದಾರರಿಗೆ ಕರ್ನಲ್ ಪ್ರೋಗ್ರಾಮಿಂಗ್ ಕೌಶಲ್ಯಗಳು ಅಥವಾ ಸಾಮಾನ್ಯವಾಗಿ ಪ್ರೋಗ್ರಾಮಿಂಗ್ ಕೌಶಲ್ಯಗಳಿವೆ ಎಂದು ಭಾವಿಸುವುದಿಲ್ಲ), ರನ್‌ಟೈಮ್ ಸುರಕ್ಷತೆ (ಮಾಡ್ಯೂಲ್‌ಗಳನ್ನು ಬರೆಯುವಾಗ ಅಥವಾ ಪರೀಕ್ಷಿಸುವಾಗ ನೀವು ಸಿಸ್ಟಮ್ ಅನ್ನು ಮುರಿಯದಿದ್ದರೆ ಕಾಮೆಂಟ್‌ಗಳಲ್ಲಿ ನಿಮ್ಮ ಕೈಯನ್ನು ಮೇಲಕ್ಕೆತ್ತಿ), ಪರಮಾಣುತೆ - ಮಾಡ್ಯೂಲ್‌ಗಳನ್ನು ಮರುಲೋಡ್ ಮಾಡುವಾಗ ಡೌನ್‌ಟೈಮ್ ಇರುತ್ತದೆ ಮತ್ತು BPF ಉಪವ್ಯವಸ್ಥೆಯು ಯಾವುದೇ ಈವೆಂಟ್ ಅನ್ನು ತಪ್ಪಿಸಿಕೊಳ್ಳುವುದಿಲ್ಲ ಎಂದು ಖಾತರಿಪಡಿಸುತ್ತದೆ (ನ್ಯಾಯಯುತವಾಗಿ ಹೇಳಬೇಕೆಂದರೆ, ಇದು ಎಲ್ಲಾ ರೀತಿಯ BPF ಪ್ರೋಗ್ರಾಂಗಳಿಗೆ ನಿಜವಲ್ಲ).

ಅಂತಹ ಸಾಮರ್ಥ್ಯಗಳ ಉಪಸ್ಥಿತಿಯು BPF ಅನ್ನು ಕೋರ್ ಅನ್ನು ವಿಸ್ತರಿಸಲು ಸಾರ್ವತ್ರಿಕ ಸಾಧನವನ್ನಾಗಿ ಮಾಡುತ್ತದೆ, ಇದು ಪ್ರಾಯೋಗಿಕವಾಗಿ ದೃಢೀಕರಿಸಲ್ಪಟ್ಟಿದೆ: ಹೆಚ್ಚು ಹೆಚ್ಚು ಹೊಸ ರೀತಿಯ ಕಾರ್ಯಕ್ರಮಗಳನ್ನು BPF ಗೆ ಸೇರಿಸಲಾಗುತ್ತಿದೆ, ಹೆಚ್ಚು ಹೆಚ್ಚು ದೊಡ್ಡ ಕಂಪನಿಗಳು 24x7 ಯುದ್ಧ ಸರ್ವರ್‌ಗಳಲ್ಲಿ BPF ಅನ್ನು ಬಳಸುತ್ತವೆ, ಹೆಚ್ಚು ಹೆಚ್ಚು ಸ್ಟಾರ್ಟ್‌ಅಪ್‌ಗಳು BPF ಆಧಾರಿತ ಪರಿಹಾರಗಳ ಮೇಲೆ ತಮ್ಮ ವ್ಯವಹಾರವನ್ನು ನಿರ್ಮಿಸುತ್ತವೆ. BPF ಅನ್ನು ಎಲ್ಲೆಡೆ ಬಳಸಲಾಗುತ್ತದೆ: DDoS ದಾಳಿಗಳ ವಿರುದ್ಧ ರಕ್ಷಣೆಯಲ್ಲಿ, SDN ಅನ್ನು ರಚಿಸುವುದು (ಉದಾಹರಣೆಗೆ, ಕುಬರ್ನೆಟ್‌ಗಳಿಗಾಗಿ ನೆಟ್‌ವರ್ಕ್‌ಗಳನ್ನು ಕಾರ್ಯಗತಗೊಳಿಸುವುದು), ಮುಖ್ಯ ಸಿಸ್ಟಮ್ ಟ್ರೇಸಿಂಗ್ ಸಾಧನ ಮತ್ತು ಅಂಕಿಅಂಶಗಳ ಸಂಗ್ರಾಹಕವಾಗಿ, ಒಳನುಗ್ಗುವಿಕೆ ಪತ್ತೆ ವ್ಯವಸ್ಥೆಗಳಲ್ಲಿ ಮತ್ತು ಸ್ಯಾಂಡ್‌ಬಾಕ್ಸ್ ವ್ಯವಸ್ಥೆಗಳಲ್ಲಿ, ಇತ್ಯಾದಿ.

ಲೇಖನದ ಅವಲೋಕನ ಭಾಗವನ್ನು ಮುಗಿಸೋಣ ಮತ್ತು ವರ್ಚುವಲ್ ಯಂತ್ರ ಮತ್ತು BPF ಪರಿಸರ ವ್ಯವಸ್ಥೆಯನ್ನು ಹೆಚ್ಚು ವಿವರವಾಗಿ ನೋಡೋಣ.

ವಿಷಯಾಂತರ: ಉಪಯುಕ್ತತೆಗಳು

ಮುಂದಿನ ವಿಭಾಗಗಳಲ್ಲಿನ ಉದಾಹರಣೆಗಳನ್ನು ಚಲಾಯಿಸಲು, ನಿಮಗೆ ಕನಿಷ್ಠ ಹಲವಾರು ಉಪಯುಕ್ತತೆಗಳು ಬೇಕಾಗಬಹುದು, llvm/clang ಬಿಪಿಎಫ್ ಬೆಂಬಲದೊಂದಿಗೆ ಮತ್ತು bpftool. ವಿಭಾಗದಲ್ಲಿ ಅಭಿವೃದ್ಧಿ ಪರಿಕರಗಳು ಕಟ್ಟಡ ಉಪಯುಕ್ತತೆಗಳನ್ನು ನಿರ್ಮಿಸುವ ಸೂಚನೆಗಳನ್ನು ಹಾಗೂ ನಿಮ್ಮ ಕರ್ನಲ್ ಅನ್ನು ನೀವು ಓದಬಹುದು. ನಮ್ಮ ಪ್ರಸ್ತುತಿಯ ಸುಸಂಬದ್ಧತೆಗೆ ಅಡ್ಡಿಯಾಗದಂತೆ ಈ ವಿಭಾಗವನ್ನು ಕೆಳಗೆ ಇರಿಸಲಾಗಿದೆ.

ಬಿಪಿಎಫ್ ವರ್ಚುವಲ್ ಯಂತ್ರದ ರಿಜಿಸ್ಟರ್‌ಗಳು ಮತ್ತು ಕಮಾಂಡ್ ಸಿಸ್ಟಮ್

BPF ವಾಸ್ತುಶಿಲ್ಪ ಮತ್ತು ಆಜ್ಞಾ ವ್ಯವಸ್ಥೆಯನ್ನು, ಕಾರ್ಯಕ್ರಮಗಳನ್ನು C ಯಲ್ಲಿ ಬರೆಯಲಾಗುತ್ತದೆ ಮತ್ತು ಕರ್ನಲ್‌ಗೆ ಲೋಡ್ ಮಾಡಿದ ನಂತರ ಸ್ಥಳೀಯ ಕೋಡ್‌ಗೆ ಅನುವಾದಿಸಲಾಗುತ್ತದೆ ಎಂಬುದನ್ನು ಗಣನೆಗೆ ತೆಗೆದುಕೊಂಡು ಅಭಿವೃದ್ಧಿಪಡಿಸಲಾಗಿದೆ. ಆದ್ದರಿಂದ, ಆಧುನಿಕ ಯಂತ್ರಗಳ ಸಾಮರ್ಥ್ಯಗಳ ಗಣಿತದ ಅರ್ಥದಲ್ಲಿ ಛೇದಕವನ್ನು ಗಮನದಲ್ಲಿಟ್ಟುಕೊಂಡು ರಿಜಿಸ್ಟರ್‌ಗಳ ಸಂಖ್ಯೆ ಮತ್ತು ಆಜ್ಞೆಗಳ ಗುಂಪನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಯಿತು. ಇದರ ಜೊತೆಗೆ, ಕಾರ್ಯಕ್ರಮಗಳ ಮೇಲೆ ವಿವಿಧ ನಿರ್ಬಂಧಗಳನ್ನು ವಿಧಿಸಲಾಯಿತು, ಉದಾಹರಣೆಗೆ, ಇತ್ತೀಚಿನವರೆಗೂ ಚಕ್ರಗಳು ಮತ್ತು ಸಬ್‌ರುಟೀನ್‌ಗಳನ್ನು ಬರೆಯಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ ಮತ್ತು ಸೂಚನೆಗಳ ಸಂಖ್ಯೆಯನ್ನು 4096 ಕ್ಕೆ ಸೀಮಿತಗೊಳಿಸಲಾಗಿದೆ (ಈಗ ಸವಲತ್ತು ಪಡೆದ ಕಾರ್ಯಕ್ರಮಗಳು ಒಂದು ಮಿಲಿಯನ್ ಸೂಚನೆಗಳನ್ನು ಲೋಡ್ ಮಾಡಬಹುದು).

ಬಿಪಿಎಫ್ ಹನ್ನೊಂದು ಬಳಕೆದಾರರು ಪ್ರವೇಶಿಸಬಹುದಾದ 64-ಬಿಟ್ ರಿಜಿಸ್ಟರ್‌ಗಳನ್ನು ಹೊಂದಿದೆ. r0-r10 ಮತ್ತು ಪ್ರೋಗ್ರಾಂ ಕೌಂಟರ್. ನೋಂದಾಯಿಸಿ r10 ಸ್ಟಾಕ್ ಪಾಯಿಂಟರ್ (ಫ್ರೇಮ್ ಪಾಯಿಂಟರ್) ಅನ್ನು ಒಳಗೊಂಡಿದೆ ಮತ್ತು ಓದಲು ಮಾತ್ರ. ಪ್ರೋಗ್ರಾಂಗಳು 512-ಬೈಟ್ ಸ್ಟ್ಯಾಕ್‌ಗೆ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿರುತ್ತವೆ ಮತ್ತು ಕಾರ್ಯಗತಗೊಳಿಸುವಾಗ ನಕ್ಷೆಗಳ ರೂಪದಲ್ಲಿ ಅನಿಯಮಿತ ಪ್ರಮಾಣದ ಹಂಚಿಕೆಯ ಮೆಮೊರಿಯನ್ನು ಹೊಂದಿರುತ್ತವೆ.

ಬಿಪಿಎಫ್ ಪ್ರೋಗ್ರಾಂಗಳು ಪ್ರೋಗ್ರಾಂ ಪ್ರಕಾರವನ್ನು ಅವಲಂಬಿಸಿ ಮತ್ತು ಇತ್ತೀಚೆಗೆ ನಿಯಮಿತ ಕಾರ್ಯಗಳನ್ನು ಅವಲಂಬಿಸಿ ನಿರ್ದಿಷ್ಟ ಕರ್ನಲ್ ಸಹಾಯಕಗಳ ಗುಂಪನ್ನು ಚಲಾಯಿಸಲು ಅನುಮತಿಸಲಾಗಿದೆ. ಕರೆಯಲ್ಪಡುವ ಪ್ರತಿಯೊಂದು ಕಾರ್ಯವು ರಿಜಿಸ್ಟರ್‌ಗಳಲ್ಲಿ ರವಾನಿಸಲಾದ ಐದು ವಾದಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳಬಹುದು. r1-r5, ಮತ್ತು ರಿಟರ್ನ್ ಮೌಲ್ಯವನ್ನು ರವಾನಿಸಲಾಗುತ್ತದೆ r0ಕಾರ್ಯದಿಂದ ಹಿಂತಿರುಗಿದ ನಂತರ ರೆಜಿಸ್ಟರ್‌ಗಳ ವಿಷಯಗಳು r6-r9 ಬದಲಾಗುವುದಿಲ್ಲ.

ಪರಿಣಾಮಕಾರಿ ಕಾರ್ಯಕ್ರಮ ಅನುವಾದಕ್ಕಾಗಿ, ನೋಂದಣಿಗಳು r0-r11 ಪ್ರಸ್ತುತ ವಾಸ್ತುಶಿಲ್ಪದ ABI ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಗಣನೆಗೆ ತೆಗೆದುಕೊಂಡು, ಎಲ್ಲಾ ಬೆಂಬಲಿತ ವಾಸ್ತುಶಿಲ್ಪಗಳನ್ನು ನಿಸ್ಸಂದಿಗ್ಧವಾಗಿ ನೈಜ ನೋಂದಣಿಗಳಿಗೆ ಮ್ಯಾಪ್ ಮಾಡಲಾಗಿದೆ. ಉದಾಹರಣೆಗೆ, x86_64 ನೋಂದಣಿಗಳು r1-r5ಕಾರ್ಯ ನಿಯತಾಂಕಗಳನ್ನು ರವಾನಿಸಲು ಬಳಸಲಾಗುವ , ಅನ್ನು ಇಲ್ಲಿ ಪ್ರದರ್ಶಿಸಲಾಗುತ್ತದೆ rdi, rsi, rdx, rcx, r8, ಇವುಗಳನ್ನು ಕಾರ್ಯಗಳಿಗೆ ನಿಯತಾಂಕಗಳನ್ನು ರವಾನಿಸಲು ಬಳಸಲಾಗುತ್ತದೆ x86_64. ಉದಾಹರಣೆಗೆ, ಎಡಭಾಗದಲ್ಲಿರುವ ಕೋಡ್ ಬಲಭಾಗದಲ್ಲಿರುವ ಕೋಡ್‌ಗೆ ಈ ರೀತಿ ಅನುವಾದಿಸುತ್ತದೆ:

1:  (b7) r1 = 1                    mov    $0x1,%rdi
2:  (b7) r2 = 2                    mov    $0x2,%rsi
3:  (b7) r3 = 3                    mov    $0x3,%rdx
4:  (b7) r4 = 4                    mov    $0x4,%rcx
5:  (b7) r5 = 5                    mov    $0x5,%r8
6:  (85) call pc+1                 callq  0x0000000000001ee8

ನೋಂದಣಿ r0 ಪ್ರೋಗ್ರಾಂ ಕಾರ್ಯಗತಗೊಳಿಸುವಿಕೆಯ ಫಲಿತಾಂಶವನ್ನು ಹಿಂತಿರುಗಿಸಲು ಮತ್ತು ರಿಜಿಸ್ಟರ್‌ನಲ್ಲಿಯೂ ಸಹ ಬಳಸಲಾಗುತ್ತದೆ r1 ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಸಂದರ್ಭಕ್ಕೆ ಪಾಯಿಂಟರ್ ರವಾನಿಸಲಾಗುತ್ತದೆ - ಪ್ರೋಗ್ರಾಂ ಪ್ರಕಾರವನ್ನು ಅವಲಂಬಿಸಿ, ಇದು, ಉದಾಹರಣೆಗೆ, ಒಂದು ರಚನೆಯಾಗಿರಬಹುದು struct xdp_md (XDP ಗಾಗಿ) ಅಥವಾ ರಚನೆ struct __sk_buff (ವಿಭಿನ್ನ ನೆಟ್‌ವರ್ಕ್ ಪ್ರೋಗ್ರಾಂಗಳಿಗೆ) ಅಥವಾ ರಚನೆ struct pt_regs (ವಿವಿಧ ರೀತಿಯ ಟ್ರೇಸಿಂಗ್ ಕಾರ್ಯಕ್ರಮಗಳಿಗೆ), ಇತ್ಯಾದಿ.

ಹಾಗಾಗಿ, ನಮ್ಮಲ್ಲಿ ರಿಜಿಸ್ಟರ್‌ಗಳ ಸೆಟ್, ಕರ್ನಲ್ ಸಹಾಯಕರು, ಒಂದು ಸ್ಟ್ಯಾಕ್, ಸಂದರ್ಭ ಪಾಯಿಂಟರ್ ಮತ್ತು ನಕ್ಷೆಗಳ ರೂಪದಲ್ಲಿ ಹಂಚಿಕೆಯ ಮೆಮೊರಿ ಇತ್ತು. ಇದೆಲ್ಲವೂ ಪ್ರವಾಸಕ್ಕೆ ಸಂಪೂರ್ಣವಾಗಿ ಅಗತ್ಯವಿರಲಿಲ್ಲ, ಆದರೆ...

ವಿವರಣೆಯನ್ನು ಮುಂದುವರಿಸೋಣ ಮತ್ತು ಈ ವಸ್ತುಗಳೊಂದಿಗೆ ಕೆಲಸ ಮಾಡಲು ಆಜ್ಞಾ ವ್ಯವಸ್ಥೆಯ ಬಗ್ಗೆ ಹೇಳೋಣ. ಎಲ್ಲವೂ (ಹೆಚ್ಚುಕಡಿಮೆ ಎಲ್ಲವೂ) ಬಿಪಿಎಫ್ ಸೂಚನೆಗಳು ಸ್ಥಿರ 64-ಬಿಟ್ ಗಾತ್ರವನ್ನು ಹೊಂದಿವೆ. ನೀವು 64-ಬಿಟ್ ಬಿಗ್ ಎಂಡಿಯನ್ ಯಂತ್ರದಲ್ಲಿ ಒಂದೇ ಸೂಚನೆಯನ್ನು ನೋಡಿದರೆ, ನೀವು ನೋಡುತ್ತೀರಿ

ಚಿಕ್ಕ ಮಕ್ಕಳಿಗಾಗಿ BPF, ಭಾಗ ಒಂದು: ವಿಸ್ತೃತ BPF

ಇದು Code - ಇದು ಸೂಚನೆಯ ಎನ್ಕೋಡಿಂಗ್, Dst/Src — ಇವು ಕ್ರಮವಾಗಿ ರಿಸೀವರ್ ಮತ್ತು ಮೂಲದ ಎನ್ಕೋಡಿಂಗ್‌ಗಳಾಗಿವೆ, Off — 16-ಬಿಟ್ ಸಹಿ ಮಾಡಿದ ಆಫ್‌ಸೆಟ್, ಮತ್ತು Imm — ಕೆಲವು ಆಜ್ಞೆಗಳಲ್ಲಿ ಬಳಸಲಾಗುವ 32-ಬಿಟ್ ಸಹಿ ಮಾಡಿದ ಪೂರ್ಣಾಂಕವಾಗಿದೆ (cBPF ನಿಂದ ಸ್ಥಿರ K ಗೆ ಹೋಲುತ್ತದೆ). Code ಎರಡು ವಿಧಗಳಲ್ಲಿ ಒಂದನ್ನು ಹೊಂದಿದೆ:

ಚಿಕ್ಕ ಮಕ್ಕಳಿಗಾಗಿ BPF, ಭಾಗ ಒಂದು: ವಿಸ್ತೃತ BPF

0, 1, 2, 3 ತರಗತಿಗಳ ಸೂಚನಾ ವಿಧಾನಗಳು ಮೆಮೊರಿಯೊಂದಿಗೆ ಕೆಲಸ ಮಾಡುವ ಸೂಚನೆಗಳನ್ನು ವ್ಯಾಖ್ಯಾನಿಸುತ್ತವೆ. ಅವು ಎಂದು ಕರೆಯಲಾಗುತ್ತದೆ, BPF_LD, BPF_LDX, BPF_ST, BPF_STXಕ್ರಮವಾಗಿ. ತರಗತಿಗಳು 4, 7 (BPF_ALU, BPF_ALU64) ALU ಸೂಚನಾ ಗುಂಪನ್ನು ರೂಪಿಸಿ. ತರಗತಿಗಳು 5, 6 (BPF_JMP, BPF_JMP32) ಪರಿವರ್ತನೆ ಸೂಚನೆಗಳನ್ನು ಒಳಗೊಂಡಿದೆ.

ಬಿಪಿಎಫ್ ಕಮಾಂಡ್ ಸಿಸ್ಟಮ್ ಅನ್ನು ಅಧ್ಯಯನ ಮಾಡುವ ಮುಂದಿನ ಯೋಜನೆ ಹೀಗಿದೆ: ಎಲ್ಲಾ ಸೂಚನೆಗಳು ಮತ್ತು ಅವುಗಳ ನಿಯತಾಂಕಗಳನ್ನು ಸೂಕ್ಷ್ಮವಾಗಿ ಪಟ್ಟಿ ಮಾಡುವ ಬದಲು, ನಾವು ಈ ವಿಭಾಗದಲ್ಲಿ ಒಂದೆರಡು ಉದಾಹರಣೆಗಳನ್ನು ವಿಶ್ಲೇಷಿಸುತ್ತೇವೆ ಮತ್ತು ಅವುಗಳಿಂದ ಸೂಚನೆಗಳು ವಾಸ್ತವವಾಗಿ ಹೇಗೆ ರಚನೆಯಾಗಿವೆ ಮತ್ತು ಬಿಪಿಎಫ್‌ಗಾಗಿ ಯಾವುದೇ ಬೈನರಿ ಫೈಲ್ ಅನ್ನು ಹಸ್ತಚಾಲಿತವಾಗಿ ಡಿಸ್ಅಸೆಂಬಲ್ ಮಾಡುವುದು ಹೇಗೆ ಎಂಬುದು ಸ್ಪಷ್ಟವಾಗುತ್ತದೆ. ಲೇಖನದಲ್ಲಿ ವಿಷಯವನ್ನು ಮತ್ತಷ್ಟು ಕ್ರೋಢೀಕರಿಸಲು, ವೆರಿಫೈಯರ್, ಜೆಐಟಿ ಕಂಪೈಲರ್, ಕ್ಲಾಸಿಕ್ ಬಿಪಿಎಫ್‌ನ ಅನುವಾದ, ಹಾಗೆಯೇ ನಕ್ಷೆಗಳನ್ನು ಅಧ್ಯಯನ ಮಾಡುವಾಗ, ಕರೆ ಮಾಡುವ ಕಾರ್ಯಗಳು ಇತ್ಯಾದಿ ವಿಭಾಗಗಳಲ್ಲಿ ನಾವು ಪ್ರತ್ಯೇಕ ಸೂಚನೆಗಳನ್ನು ಎದುರಿಸುತ್ತೇವೆ.

ನಾವು ವೈಯಕ್ತಿಕ ಸೂಚನೆಗಳ ಬಗ್ಗೆ ಮಾತನಾಡುವಾಗ, ನಾವು ಕರ್ನಲ್ ಫೈಲ್‌ಗಳನ್ನು ಉಲ್ಲೇಖಿಸುತ್ತೇವೆ. bpf.h и bpf_common.h, ಇದು BPF ಸೂಚನೆಗಳ ಸಂಖ್ಯಾತ್ಮಕ ಸಂಕೇತಗಳನ್ನು ವ್ಯಾಖ್ಯಾನಿಸುತ್ತದೆ. ವಾಸ್ತುಶಿಲ್ಪವನ್ನು ಅಧ್ಯಯನ ಮಾಡುವಾಗ ಮತ್ತು/ಅಥವಾ ಬೈನರಿಗಳನ್ನು ನೀವೇ ಪಾರ್ಸ್ ಮಾಡುವಾಗ, ಸಂಕೀರ್ಣತೆಯ ಕ್ರಮದಲ್ಲಿ ವಿಂಗಡಿಸಲಾದ ಈ ಕೆಳಗಿನ ಮೂಲಗಳಲ್ಲಿ ನೀವು ಶಬ್ದಾರ್ಥಗಳನ್ನು ಕಾಣಬಹುದು: ಅನಧಿಕೃತ eBPF ವಿಶೇಷಣಗಳು, BPF ಮತ್ತು XDP ಉಲ್ಲೇಖ ಮಾರ್ಗದರ್ಶಿ, ಸೂಚನಾ ಸೆಟ್, ಡಾಕ್ಯುಮೆಂಟೇಶನ್/ನೆಟ್ವರ್ಕಿಂಗ್/filter.txt ಮತ್ತು, ಸಹಜವಾಗಿ, ಲಿನಕ್ಸ್ ಮೂಲ ಕೋಡ್‌ನಲ್ಲಿ - ವೆರಿಫೈಯರ್, ಜೆಐಟಿ, ಬಿಪಿಎಫ್ ಇಂಟರ್ಪ್ರಿಟರ್.

ಉದಾಹರಣೆ: ನಿಮ್ಮ ಮನಸ್ಸಿನಲ್ಲಿ ಬಿಪಿಎಫ್ ಅನ್ನು ಡಿಸ್ಅಸೆಂಬಲ್ ಮಾಡುವುದು

ನಾವು ಒಂದು ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಕಂಪೈಲ್ ಮಾಡುವ ಉದಾಹರಣೆಯನ್ನು ನೋಡೋಣ. readelf-example.c ಮತ್ತು ಫಲಿತಾಂಶದ ಬೈನರಿಯನ್ನು ನೋಡಿ. ನಾವು ಮೂಲ ವಿಷಯಗಳನ್ನು ಬಹಿರಂಗಪಡಿಸುತ್ತೇವೆ. readelf-example.c ಕೆಳಗೆ, ನಾವು ಬೈನರಿ ಕೋಡ್‌ಗಳಿಂದ ಅದರ ತರ್ಕವನ್ನು ಪುನಃಸ್ಥಾಪಿಸಿದ ನಂತರ:

$ clang -target bpf -c readelf-example.c -o readelf-example.o -O2
$ llvm-readelf -x .text readelf-example.o
Hex dump of section '.text':
0x00000000 b7000000 01000000 15010100 00000000 ................
0x00000010 b7000000 02000000 95000000 00000000 ................

ಔಟ್‌ಪುಟ್‌ನಲ್ಲಿ ಮೊದಲ ಕಾಲಮ್ readelf — ಇದು ಒಂದು ಇಂಡೆಂಟೇಶನ್ ಮತ್ತು ನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ನಾಲ್ಕು ಆಜ್ಞೆಗಳನ್ನು ಒಳಗೊಂಡಿದೆ:

Code Dst Src Off  Imm
b7   0   0   0000 01000000
15   0   1   0100 00000000
b7   0   0   0000 02000000
95   0   0   0000 00000000

ಆಜ್ಞಾ ಸಂಕೇತಗಳು ಸಮಾನವಾಗಿವೆ b7, 15, b7 и 95. ಕನಿಷ್ಠ ಮೂರು ಮಹತ್ವದ ಬಿಟ್‌ಗಳು ಸೂಚನಾ ವರ್ಗ ಎಂಬುದನ್ನು ನೆನಪಿಸಿಕೊಳ್ಳಿ. ನಮ್ಮ ಸಂದರ್ಭದಲ್ಲಿ, ನಾಲ್ಕನೇ ಬಿಟ್ ಎಲ್ಲಾ ಸೂಚನೆಗಳಿಗೆ ಖಾಲಿಯಾಗಿದೆ, ಆದ್ದರಿಂದ ಸೂಚನಾ ತರಗತಿಗಳು ಕ್ರಮವಾಗಿ 7, 5, 7, 5 ಆಗಿರುತ್ತವೆ. ವರ್ಗ 7 ಎಂದರೆ BPF_ALU64, ಮತ್ತು 5 ಎಂದರೆ BPF_JMPಎರಡೂ ತರಗತಿಗಳಿಗೆ ಸೂಚನಾ ಸ್ವರೂಪ ಒಂದೇ ಆಗಿರುತ್ತದೆ (ಮೇಲೆ ನೋಡಿ) ಮತ್ತು ನಾವು ನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಈ ರೀತಿ ಪುನಃ ಬರೆಯಬಹುದು (ಅದೇ ಸಮಯದಲ್ಲಿ ನಾವು ಉಳಿದ ಕಾಲಮ್‌ಗಳನ್ನು ಮಾನವ ರೂಪದಲ್ಲಿ ಪುನಃ ಬರೆಯುತ್ತೇವೆ):

Op S  Class   Dst Src Off  Imm
b  0  ALU64   0   0   0    1
1  0  JMP     0   1   1    0
b  0  ALU64   0   0   0    2
9  0  JMP     0   0   0    0

ಕಾರ್ಯಾಚರಣೆ b ವರ್ಗ ALU64 - ಇದು ಬಿಪಿಎಫ್_ಎಂಒವಿ. ಇದು ಗಮ್ಯಸ್ಥಾನ ನೋಂದಣಿಗೆ ಮೌಲ್ಯವನ್ನು ನಿಗದಿಪಡಿಸುತ್ತದೆ. ಬಿಟ್ ಅನ್ನು ಹೊಂದಿಸಿದ್ದರೆ s (ಮೂಲ), ನಂತರ ಮೌಲ್ಯವನ್ನು ಮೂಲ ರಿಜಿಸ್ಟರ್‌ನಿಂದ ತೆಗೆದುಕೊಳ್ಳಲಾಗುತ್ತದೆ ಮತ್ತು ನಮ್ಮ ಸಂದರ್ಭದಲ್ಲಿ ಅದನ್ನು ಹೊಂದಿಸದಿದ್ದರೆ, ಮೌಲ್ಯವನ್ನು ಕ್ಷೇತ್ರದಿಂದ ತೆಗೆದುಕೊಳ್ಳಲಾಗುತ್ತದೆ. Immಹೀಗಾಗಿ, ಮೊದಲ ಮತ್ತು ಮೂರನೇ ಸೂಚನೆಗಳಲ್ಲಿ ನಾವು ಕಾರ್ಯಾಚರಣೆಯನ್ನು ನಿರ್ವಹಿಸುತ್ತೇವೆ r0 = Immಮುಂದೆ, 1 ನೇ ವರ್ಗದ ಕಾರ್ಯಾಚರಣೆ JMP ಬಿಪಿಎಫ್_ಜೆಇಕ್ಯೂ (ಸಮಾನವಾಗಿದ್ದರೆ ಜಿಗಿಯಿರಿ). ನಮ್ಮ ಸಂದರ್ಭದಲ್ಲಿ, ಬಿಟ್‌ನಿಂದ S ಶೂನ್ಯಕ್ಕೆ ಸಮಾನವಾಗಿದ್ದರೆ, ಅದು ಮೂಲ ನೋಂದಣಿಯ ಮೌಲ್ಯವನ್ನು ಕ್ಷೇತ್ರದೊಂದಿಗೆ ಹೋಲಿಸುತ್ತದೆ. Immಮೌಲ್ಯಗಳು ಹೊಂದಿಕೆಯಾದರೆ, ಪರಿವರ್ತನೆ ಸಂಭವಿಸುತ್ತದೆ PC + Offಅಲ್ಲಿ PC, ಎಂದಿನಂತೆ, ಮುಂದಿನ ಸೂಚನೆಯ ವಿಳಾಸವನ್ನು ಹೊಂದಿರುತ್ತದೆ. ಅಂತಿಮವಾಗಿ, ವರ್ಗ 9 ಕಾರ್ಯಾಚರಣೆ JMP BPF_EXITಈ ಸೂಚನೆಯು ಕರ್ನಲ್‌ಗೆ ಹಿಂತಿರುಗುವ ಮೂಲಕ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಕೊನೆಗೊಳಿಸುತ್ತದೆ. r0ನಮ್ಮ ಕೋಷ್ಟಕಕ್ಕೆ ಹೊಸ ಕಾಲಮ್ ಅನ್ನು ಸೇರಿಸೋಣ:

Op    S  Class   Dst Src Off  Imm    Disassm
MOV   0  ALU64   0   0   0    1      r0 = 1
JEQ   0  JMP     0   1   1    0      if (r1 == 0) goto pc+1
MOV   0  ALU64   0   0   0    2      r0 = 2
EXIT  0  JMP     0   0   0    0      exit

ನಾವು ಇದನ್ನು ಹೆಚ್ಚು ಅನುಕೂಲಕರ ರೂಪದಲ್ಲಿ ಪುನಃ ಬರೆಯಬಹುದು:

     r0 = 1
     if (r1 == 0) goto END
     r0 = 2
END:
     exit

ನಾವು ಅದನ್ನು ರಿಜಿಸ್ಟರ್‌ನಲ್ಲಿ ನೆನಪಿಸಿಕೊಂಡರೆ r1 ಪ್ರೋಗ್ರಾಂ ಕರ್ನಲ್‌ನಿಂದ ಮತ್ತು ರಿಜಿಸ್ಟರ್‌ನಲ್ಲಿ ಸಂದರ್ಭಕ್ಕೆ ಪಾಯಿಂಟರ್ ಅನ್ನು ರವಾನಿಸಲಾಗುತ್ತದೆ r0 ಮೌಲ್ಯವನ್ನು ಕರ್ನಲ್‌ಗೆ ಹಿಂತಿರುಗಿಸಿದಾಗ, ಸಂದರ್ಭ ಪಾಯಿಂಟರ್ ಶೂನ್ಯವಾಗಿದ್ದರೆ, ನಾವು 1 ಅನ್ನು ಮತ್ತು ಇಲ್ಲದಿದ್ದರೆ 2 ಅನ್ನು ಹಿಂತಿರುಗಿಸುತ್ತೇವೆ ಎಂದು ನಾವು ನೋಡಬಹುದು. ಮೂಲವನ್ನು ನೋಡುವ ಮೂಲಕ ನಾವು ಸರಿಯಾಗಿದ್ದೇವೆಯೇ ಎಂದು ಪರಿಶೀಲಿಸೋಣ:

$ cat readelf-example.c
int foo(void *ctx)
{
        return ctx ? 2 : 1;
}

ಹೌದು, ಇದು ಅರ್ಥಹೀನ ಕಾರ್ಯಕ್ರಮ, ಆದರೆ ಇದು ಕೇವಲ ನಾಲ್ಕು ಸರಳ ಸೂಚನೆಗಳಾಗಿ ಬದಲಾಗುತ್ತದೆ.

ವಿನಾಯಿತಿ ಉದಾಹರಣೆ: 16-ಬೈಟ್ ಸೂಚನೆ

ಕೆಲವು ಸೂಚನೆಗಳು 64 ಬಿಟ್‌ಗಳಿಗಿಂತ ಹೆಚ್ಚು ತೆಗೆದುಕೊಳ್ಳುತ್ತವೆ ಎಂದು ನಾವು ಮೊದಲೇ ಹೇಳಿದ್ದೇವೆ. ಉದಾಹರಣೆಗೆ, ಇದು ಸೂಚನೆಗೆ ಅನ್ವಯಿಸುತ್ತದೆ lddw (ಕೋಡ್ = 0x18 = BPF_LD | BPF_DW | BPF_IMM) - ಕ್ಷೇತ್ರಗಳಿಂದ ಎರಡು ಪದಗಳನ್ನು ರಿಜಿಸ್ಟರ್‌ಗೆ ಲೋಡ್ ಮಾಡಿ Imm... ವಾಸ್ತವ ಅದು Imm ಗಾತ್ರದಲ್ಲಿ 32 ಬಿಟ್‌ಗಳು ಮತ್ತು ಎರಡು ಪದಗಳು 64 ಬಿಟ್‌ಗಳು, ಆದ್ದರಿಂದ ಒಂದು 64-ಬಿಟ್ ಸೂಚನೆಯಲ್ಲಿ 64-ಬಿಟ್ ತಕ್ಷಣದ ಮೌಲ್ಯವನ್ನು ರಿಜಿಸ್ಟರ್‌ಗೆ ಲೋಡ್ ಮಾಡುವುದು ಸಾಧ್ಯವಿಲ್ಲ. ಇದನ್ನು ಮಾಡಲು, ಕ್ಷೇತ್ರದಲ್ಲಿ 64-ಬಿಟ್ ಮೌಲ್ಯದ ಎರಡನೇ ಭಾಗವನ್ನು ಸಂಗ್ರಹಿಸಲು ಎರಡು ಪಕ್ಕದ ಸೂಚನೆಗಳನ್ನು ಬಳಸಲಾಗುತ್ತದೆ. Imm... ಉದಾಹರಣೆ:

$ cat x64.c
long foo(void *ctx)
{
        return 0x11223344aabbccdd;
}
$ clang -target bpf -c x64.c -o x64.o -O2
$ llvm-readelf -x .text x64.o
Hex dump of section '.text':
0x00000000 18000000 ddccbbaa 00000000 44332211 ............D3".
0x00000010 95000000 00000000                   ........

ಬೈನರಿ ಪ್ರೋಗ್ರಾಂನಲ್ಲಿ ಕೇವಲ ಎರಡು ಸೂಚನೆಗಳಿವೆ:

Binary                                 Disassm
18000000 ddccbbaa 00000000 44332211    r0 = Imm[0]|Imm[1]
95000000 00000000                      exit

ನಾವು ಸೂಚನೆಗಳೊಂದಿಗೆ ಮತ್ತೊಮ್ಮೆ ಭೇಟಿಯಾಗುತ್ತೇವೆ. lddw, ನಾವು ಸ್ಥಳಾಂತರಗಳು ಮತ್ತು ನಕ್ಷೆಗಳೊಂದಿಗೆ ಕೆಲಸ ಮಾಡುವ ಬಗ್ಗೆ ಮಾತನಾಡುವಾಗ.

ಉದಾಹರಣೆ: ಪ್ರಮಾಣಿತ ಪರಿಕರಗಳನ್ನು ಬಳಸಿಕೊಂಡು ಬಿಪಿಎಫ್ ಅನ್ನು ಡಿಸ್ಅಸೆಂಬಲ್ ಮಾಡುವುದು

ಆದ್ದರಿಂದ, ನಾವು BPF ಬೈನರಿ ಕೋಡ್‌ಗಳನ್ನು ಹೇಗೆ ಓದುವುದು ಎಂದು ಕಲಿತಿದ್ದೇವೆ ಮತ್ತು ಅಗತ್ಯವಿದ್ದರೆ ಯಾವುದೇ ಸೂಚನೆಯನ್ನು ಡಿಸ್ಅಸೆಂಬಲ್ ಮಾಡಲು ಸಿದ್ಧರಿದ್ದೇವೆ. ಆದಾಗ್ಯೂ, ಪ್ರಾಯೋಗಿಕವಾಗಿ ಪ್ರಮಾಣಿತ ಪರಿಕರಗಳನ್ನು ಬಳಸಿಕೊಂಡು ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಡಿಸ್ಅಸೆಂಬಲ್ ಮಾಡುವುದು ಹೆಚ್ಚು ಅನುಕೂಲಕರ ಮತ್ತು ವೇಗವಾಗಿದೆ ಎಂದು ಹೇಳುವುದು ಯೋಗ್ಯವಾಗಿದೆ, ಉದಾಹರಣೆಗೆ:

$ llvm-objdump -d x64.o

Disassembly of section .text:

0000000000000000 <foo>:
 0: 18 00 00 00 dd cc bb aa 00 00 00 00 44 33 22 11 r0 = 1234605617868164317 ll
 2: 95 00 00 00 00 00 00 00 exit

ಬಿಪಿಎಫ್ ಆಬ್ಜೆಕ್ಟ್ ಲೈಫ್‌ಸೈಕಲ್, ಬಿಪಿಎಫ್‌ಎಫ್‌ಎಸ್ ಫೈಲ್ ಸಿಸ್ಟಮ್

(ಈ ಉಪವಿಭಾಗದಲ್ಲಿ ವಿವರಿಸಿದ ಕೆಲವು ವಿವರಗಳನ್ನು ನಾನು ಮೊದಲು ಕಲಿತದ್ದು ಉಪವಾಸ ಅಲೆಕ್ಸಿ ಸ್ಟಾರೊವೊಯಿಟೋವ್ ಬಿಪಿಎಫ್ ಬ್ಲಾಗ್.)

BPF ವಸ್ತುಗಳು - ಪ್ರೋಗ್ರಾಂಗಳು ಮತ್ತು ನಕ್ಷೆಗಳು - ಆಜ್ಞೆಗಳನ್ನು ಬಳಸಿಕೊಂಡು ಬಳಕೆದಾರ ಸ್ಥಳದಿಂದ ರಚಿಸಲಾಗಿದೆ. BPF_PROG_LOAD и BPF_MAP_CREATE ಸಿಸ್ಟಮ್ ಕರೆ bpf(2), ಮುಂದಿನ ವಿಭಾಗದಲ್ಲಿ ಇದು ನಿಖರವಾಗಿ ಹೇಗೆ ಸಂಭವಿಸುತ್ತದೆ ಎಂಬುದರ ಕುರಿತು ನಾವು ಮಾತನಾಡುತ್ತೇವೆ. ಈ ಸಂದರ್ಭದಲ್ಲಿ, ಕರ್ನಲ್ ಡೇಟಾ ರಚನೆಗಳನ್ನು ರಚಿಸಲಾಗುತ್ತದೆ ಮತ್ತು ಅವುಗಳಲ್ಲಿ ಪ್ರತಿಯೊಂದಕ್ಕೂ refcount (ಉಲ್ಲೇಖ ಕೌಂಟರ್) ಅನ್ನು ಒಂದಕ್ಕೆ ಹೊಂದಿಸಲಾಗಿದೆ, ಮತ್ತು ವಸ್ತುವನ್ನು ಸೂಚಿಸುವ ಫೈಲ್ ವಿವರಣೆಯನ್ನು ಬಳಕೆದಾರರಿಗೆ ಹಿಂತಿರುಗಿಸಲಾಗುತ್ತದೆ. ವಿವರಣೆಯನ್ನು ಮುಚ್ಚಿದ ನಂತರ, refcount ವಸ್ತುವಿನ ಮೌಲ್ಯವು ಒಂದರಿಂದ ಕಡಿಮೆಯಾಗುತ್ತದೆ ಮತ್ತು ಅದು ಶೂನ್ಯವನ್ನು ತಲುಪಿದಾಗ, ವಸ್ತುವು ನಾಶವಾಗುತ್ತದೆ.

ಪ್ರೋಗ್ರಾಂ ನಕ್ಷೆಗಳನ್ನು ಬಳಸಿದರೆ, ಆಗ refcount ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಲೋಡ್ ಮಾಡಿದ ನಂತರ ಈ ನಕ್ಷೆಗಳನ್ನು ಒಂದರಿಂದ ಹೆಚ್ಚಿಸಲಾಗುತ್ತದೆ, ಅಂದರೆ ಅವುಗಳ ಫೈಲ್ ವಿವರಣೆಗಳನ್ನು ಬಳಕೆದಾರ ಪ್ರಕ್ರಿಯೆಯಿಂದ ಮುಚ್ಚಬಹುದು ಮತ್ತು ಅದೇ ಸಮಯದಲ್ಲಿ refcount ಶೂನ್ಯವಾಗುವುದಿಲ್ಲ:

ಚಿಕ್ಕ ಮಕ್ಕಳಿಗಾಗಿ BPF, ಭಾಗ ಒಂದು: ವಿಸ್ತೃತ BPF

ಪ್ರೋಗ್ರಾಂ ಯಶಸ್ವಿಯಾಗಿ ಲೋಡ್ ಆದ ನಂತರ, ನಾವು ಸಾಮಾನ್ಯವಾಗಿ ಅದನ್ನು ಕೆಲವು ಈವೆಂಟ್ ಜನರೇಟರ್‌ಗೆ ಲಗತ್ತಿಸುತ್ತೇವೆ. ಉದಾಹರಣೆಗೆ, ಒಳಬರುವ ಪ್ಯಾಕೆಟ್‌ಗಳನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ನಾವು ಅದನ್ನು ನೆಟ್‌ವರ್ಕ್ ಇಂಟರ್ಫೇಸ್‌ನಲ್ಲಿ ಇರಿಸಬಹುದು ಅಥವಾ ಕೆಲವು tracepoint ಕರ್ನಲ್‌ನಲ್ಲಿ. ಈ ಹಂತದಲ್ಲಿ, ಉಲ್ಲೇಖ ಕೌಂಟರ್ ಕೂಡ ಒಂದರಿಂದ ಹೆಚ್ಚಾಗುತ್ತದೆ ಮತ್ತು ನಾವು ಲೋಡರ್ ಪ್ರೋಗ್ರಾಂನಲ್ಲಿ ಫೈಲ್ ಡಿಸ್ಕ್ರಿಪ್ಟರ್ ಅನ್ನು ಮುಚ್ಚಲು ಸಾಧ್ಯವಾಗುತ್ತದೆ.

ನಾವು ಈಗ ಲೋಡರ್ ಅನ್ನು ಕೊನೆಗೊಳಿಸಿದರೆ ಏನಾಗುತ್ತದೆ? ಅದು ಈವೆಂಟ್ ಜನರೇಟರ್ (ಹುಕ್) ಪ್ರಕಾರವನ್ನು ಅವಲಂಬಿಸಿರುತ್ತದೆ. ಲೋಡರ್ ಕೊನೆಗೊಂಡ ನಂತರ ಎಲ್ಲಾ ನೆಟ್‌ವರ್ಕ್ ಹುಕ್‌ಗಳು ಅಸ್ತಿತ್ವದಲ್ಲಿರುತ್ತವೆ, ಇವು ಜಾಗತಿಕ ಹುಕ್‌ಗಳು ಎಂದು ಕರೆಯಲ್ಪಡುತ್ತವೆ. ಮತ್ತು, ಉದಾಹರಣೆಗೆ, ಅವುಗಳನ್ನು ರಚಿಸಿದ ಪ್ರಕ್ರಿಯೆಯು ಕೊನೆಗೊಂಡ ನಂತರ ಟ್ರೇಸ್ ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಬಿಡುಗಡೆ ಮಾಡಲಾಗುತ್ತದೆ (ಮತ್ತು ಆದ್ದರಿಂದ ಅವುಗಳನ್ನು ಸ್ಥಳೀಯ ಎಂದು ಕರೆಯಲಾಗುತ್ತದೆ, "ಸ್ಥಳೀಯ ಪ್ರಕ್ರಿಯೆಗೆ"). ತಾಂತ್ರಿಕವಾಗಿ, ಸ್ಥಳೀಯ ಹುಕ್‌ಗಳು ಯಾವಾಗಲೂ ಬಳಕೆದಾರರ ಜಾಗದಲ್ಲಿ ಅನುಗುಣವಾದ ಫೈಲ್ ವಿವರಣೆಯನ್ನು ಹೊಂದಿರುತ್ತವೆ ಮತ್ತು ಆದ್ದರಿಂದ ಪ್ರಕ್ರಿಯೆಯ ಮುಕ್ತಾಯದೊಂದಿಗೆ ಮುಚ್ಚಲಾಗುತ್ತದೆ, ಆದರೆ ಜಾಗತಿಕ ಹುಕ್‌ಗಳು ಹಾಗೆ ಮಾಡುವುದಿಲ್ಲ. ಕೆಳಗಿನ ಚಿತ್ರದಲ್ಲಿ, ಸ್ಥಳೀಯ ಮತ್ತು ಜಾಗತಿಕ ಹುಕ್‌ಗಳ ಸಂದರ್ಭದಲ್ಲಿ ಲೋಡರ್ ಪ್ರೋಗ್ರಾಂನ ಮುಕ್ತಾಯವು ವಸ್ತುಗಳ ಜೀವಿತಾವಧಿಯ ಮೇಲೆ ಹೇಗೆ ಪರಿಣಾಮ ಬೀರುತ್ತದೆ ಎಂಬುದನ್ನು ನಾನು ರೆಡ್ ಕ್ರಾಸ್‌ಗಳೊಂದಿಗೆ ತೋರಿಸಲು ಪ್ರಯತ್ನಿಸುತ್ತೇನೆ.

ಚಿಕ್ಕ ಮಕ್ಕಳಿಗಾಗಿ BPF, ಭಾಗ ಒಂದು: ವಿಸ್ತೃತ BPF

ಸ್ಥಳೀಯ ಮತ್ತು ಜಾಗತಿಕ ಹುಕ್‌ಗಳ ನಡುವೆ ವಿಭಜನೆ ಏಕೆ? ಕೆಲವು ರೀತಿಯ ನೆಟ್‌ವರ್ಕ್ ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಚಲಾಯಿಸುವುದು ಬಳಕೆದಾರ ಸ್ಥಳವಿಲ್ಲದೆಯೂ ಸಹ ಅರ್ಥಪೂರ್ಣವಾಗಿದೆ, ಉದಾಹರಣೆಗೆ, DDoS ವಿರುದ್ಧ ರಕ್ಷಣೆಯನ್ನು ಕಲ್ಪಿಸಿಕೊಳ್ಳಿ - ಲೋಡರ್ ನಿಯಮಗಳನ್ನು ಬರೆಯುತ್ತದೆ ಮತ್ತು BPF ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ನೆಟ್‌ವರ್ಕ್ ಇಂಟರ್ಫೇಸ್‌ಗೆ ಸಂಪರ್ಕಿಸುತ್ತದೆ, ಅದರ ನಂತರ ಲೋಡರ್ ಹೋಗಿ ತನ್ನನ್ನು ತಾನೇ ಕೊಲ್ಲಬಹುದು. ಮತ್ತೊಂದೆಡೆ, ಹತ್ತು ನಿಮಿಷಗಳಲ್ಲಿ ನಿಮ್ಮ ಮೊಣಕಾಲಿನ ಮೇಲೆ ನೀವು ಬರೆದ ಡೀಬಗ್ ಮಾಡುವ ಟ್ರೇಸ್ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಕಲ್ಪಿಸಿಕೊಳ್ಳಿ - ಅದು ಪೂರ್ಣಗೊಂಡ ನಂತರ, ವ್ಯವಸ್ಥೆಯಲ್ಲಿ ಯಾವುದೇ ಕಸ ಉಳಿದಿರಬಾರದು ಎಂದು ನೀವು ಬಯಸುತ್ತೀರಿ ಮತ್ತು ಸ್ಥಳೀಯ ಹುಕ್‌ಗಳು ಇದನ್ನು ಖಾತರಿಪಡಿಸುತ್ತವೆ.

ಮತ್ತೊಂದೆಡೆ, ನೀವು ಕರ್ನಲ್‌ನಲ್ಲಿರುವ ಟ್ರೇಸ್‌ಪಾಯಿಂಟ್‌ಗೆ ಸಂಪರ್ಕ ಸಾಧಿಸಲು ಮತ್ತು ಹಲವು ವರ್ಷಗಳವರೆಗೆ ಅಂಕಿಅಂಶಗಳನ್ನು ಸಂಗ್ರಹಿಸಲು ಬಯಸುತ್ತೀರಿ ಎಂದು ಊಹಿಸಿ. ಈ ಸಂದರ್ಭದಲ್ಲಿ, ನೀವು ಬಳಕೆದಾರ ಭಾಗವನ್ನು ಕೊನೆಗೊಳಿಸಲು ಮತ್ತು ಕಾಲಕಾಲಕ್ಕೆ ಅಂಕಿಅಂಶಗಳಿಗೆ ಹಿಂತಿರುಗಲು ಬಯಸುತ್ತೀರಿ. ಇದು bpf ಫೈಲ್ ಸಿಸ್ಟಮ್‌ನೊಂದಿಗೆ ಸಾಧ್ಯ. ಇದು ಮೆಮೊರಿಯಲ್ಲಿ ಮಾತ್ರ ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಒಂದು ಹುಸಿ-ಫೈಲ್ ಸಿಸ್ಟಮ್ ಆಗಿದೆ, ಇದು BPF ವಸ್ತುಗಳನ್ನು ಉಲ್ಲೇಖಿಸುವ ಫೈಲ್‌ಗಳನ್ನು ರಚಿಸಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ ಮತ್ತು ಹೀಗಾಗಿ refcount ಇದರ ನಂತರ, ಲೋಡರ್ ನಿರ್ಗಮಿಸಬಹುದು ಮತ್ತು ಅದು ರಚಿಸಿದ ವಸ್ತುಗಳು ಜೀವಂತವಾಗಿರುತ್ತವೆ.

ಚಿಕ್ಕ ಮಕ್ಕಳಿಗಾಗಿ BPF, ಭಾಗ ಒಂದು: ವಿಸ್ತೃತ BPF

BPF ಆಬ್ಜೆಕ್ಟ್‌ಗಳನ್ನು ಉಲ್ಲೇಖಿಸುವ bpffs ನಲ್ಲಿ ಫೈಲ್‌ಗಳನ್ನು ರಚಿಸುವುದನ್ನು "ಪಿನ್ನಿಂಗ್" ಎಂದು ಕರೆಯಲಾಗುತ್ತದೆ ("ಒಂದು ಪ್ರಕ್ರಿಯೆಯು BPF ಪ್ರೋಗ್ರಾಂ ಅಥವಾ ನಕ್ಷೆಯನ್ನು ಪಿನ್ ಮಾಡಬಹುದು" ಎಂಬಂತೆ). BPF ಆಬ್ಜೆಕ್ಟ್‌ಗಳಿಗಾಗಿ ಫೈಲ್ ಆಬ್ಜೆಕ್ಟ್‌ಗಳನ್ನು ರಚಿಸುವುದು ಸ್ಥಳೀಯ ವಸ್ತುಗಳ ಜೀವಿತಾವಧಿಯನ್ನು ವಿಸ್ತರಿಸಲು ಮಾತ್ರವಲ್ಲದೆ, ಜಾಗತಿಕ ವಸ್ತುಗಳನ್ನು ಬಳಸುವ ಅನುಕೂಲಕ್ಕಾಗಿಯೂ ಸಹ ಅರ್ಥಪೂರ್ಣವಾಗಿದೆ - ಜಾಗತಿಕ DDoS ಸಂರಕ್ಷಣಾ ಕಾರ್ಯಕ್ರಮದ ಉದಾಹರಣೆಗೆ ಹಿಂತಿರುಗಿ, ನಾವು ಕಾಲಕಾಲಕ್ಕೆ ಅಂಕಿಅಂಶಗಳನ್ನು ನೋಡಲು ಸಾಧ್ಯವಾಗುತ್ತದೆ.

BPF ಫೈಲ್ ಸಿಸ್ಟಮ್ ಅನ್ನು ಸಾಮಾನ್ಯವಾಗಿ ಇಲ್ಲಿ ಅಳವಡಿಸಲಾಗುತ್ತದೆ /sys/fs/bpf, ಆದರೆ ಇದನ್ನು ಸ್ಥಳೀಯವಾಗಿಯೂ ಸಹ ಜೋಡಿಸಬಹುದು, ಉದಾಹರಣೆಗೆ, ಈ ರೀತಿ:

$ mkdir bpf-mountpoint
$ sudo mount -t bpf none bpf-mountpoint

ಫೈಲ್ ಸಿಸ್ಟಮ್‌ನಲ್ಲಿ ಹೆಸರುಗಳನ್ನು ಆಜ್ಞೆಯನ್ನು ಬಳಸಿಕೊಂಡು ರಚಿಸಲಾಗುತ್ತದೆ BPF_OBJ_PIN ಬಿಪಿಎಫ್ ಸಿಸ್ಟಮ್ ಕಾಲ್. ಉದಾಹರಣೆಗೆ, ನಾವು ಕೆಲವು ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ತೆಗೆದುಕೊಂಡು, ಕಂಪೈಲ್ ಮಾಡಿ, ಲೋಡ್ ಮಾಡಿ ಮತ್ತು ಲಗತ್ತಿಸೋಣ. bpffsನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ಉಪಯುಕ್ತವಾದ ಏನನ್ನೂ ಮಾಡುವುದಿಲ್ಲ, ನಾವು ಅದರ ಕೋಡ್ ಅನ್ನು ಮಾತ್ರ ಒದಗಿಸುತ್ತೇವೆ ಇದರಿಂದ ನೀವು ಉದಾಹರಣೆಯನ್ನು ಪುನರುತ್ಪಾದಿಸಬಹುದು:

$ cat test.c
__attribute__((section("xdp"), used))
int test(void *ctx)
{
        return 0;
}

char _license[] __attribute__((section("license"), used)) = "GPL";

ಈ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಕಂಪೈಲ್ ಮಾಡೋಣ ಮತ್ತು ಫೈಲ್ ಸಿಸ್ಟಮ್‌ನ ಸ್ಥಳೀಯ ಪ್ರತಿಯನ್ನು ರಚಿಸೋಣ. bpffs:

$ clang -target bpf -c test.c -o test.o
$ mkdir bpf-mountpoint
$ sudo mount -t bpf none bpf-mountpoint

ಈಗ ಉಪಯುಕ್ತತೆಯನ್ನು ಬಳಸಿಕೊಂಡು ನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಲೋಡ್ ಮಾಡೋಣ. bpftool ಮತ್ತು ಸಂಬಂಧಿತ ಸಿಸ್ಟಮ್ ಕರೆಗಳನ್ನು ನೋಡೋಣ. bpf(2) (ಸ್ಟ್ರೇಸ್ ಔಟ್‌ಪುಟ್‌ನಿಂದ ಕೆಲವು ಅಪ್ರಸ್ತುತ ಸಾಲುಗಳನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ):

$ sudo strace -e bpf bpftool prog load ./test.o bpf-mountpoint/test
bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_XDP, prog_name="test", ...}, 120) = 3
bpf(BPF_OBJ_PIN, {pathname="bpf-mountpoint/test", bpf_fd=3}, 120) = 0

ಇಲ್ಲಿ ನಾವು ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಬಳಸಿ ಡೌನ್‌ಲೋಡ್ ಮಾಡಿದ್ದೇವೆ BPF_PROG_LOAD, ಕರ್ನಲ್‌ನಿಂದ ಫೈಲ್ ಡಿಸ್ಕ್ರಿಪ್ಟರ್ ಅನ್ನು ಸ್ವೀಕರಿಸಿದೆ 3 ಮತ್ತು ಆಜ್ಞೆಯನ್ನು ಬಳಸುವುದು BPF_OBJ_PIN ಈ ಫೈಲ್ ವಿವರಣೆಯನ್ನು ಫೈಲ್ ಆಗಿ ಪಿನ್ ಮಾಡಲಾಗಿದೆ. "bpf-mountpoint/test"ಅದರ ನಂತರ, ಬೂಟ್ಲೋಡರ್ ಪ್ರೋಗ್ರಾಂ bpftool ಕೆಲಸ ಮುಗಿದಿದೆ, ಆದರೆ ನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ಕರ್ನಲ್‌ನಲ್ಲಿಯೇ ಇತ್ತು, ನಾವು ಅದನ್ನು ಯಾವುದೇ ನೆಟ್‌ವರ್ಕ್ ಇಂಟರ್ಫೇಸ್‌ಗೆ ಲಗತ್ತಿಸದಿದ್ದರೂ ಸಹ:

$ sudo bpftool prog | tail -3
783: xdp  name test  tag 5c8ba0cf164cb46c  gpl
        loaded_at 2020-05-05T13:27:08+0000  uid 0
        xlated 24B  jited 41B  memlock 4096B

ನಾವು ಫೈಲ್ ಆಬ್ಜೆಕ್ಟ್ ಅನ್ನು ಸಾಮಾನ್ಯವಾಗಿ ಅಳಿಸಬಹುದು. unlink(2) ಮತ್ತು ಅದರ ನಂತರ ಅನುಗುಣವಾದ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ತೆಗೆದುಹಾಕಲಾಗುತ್ತದೆ:

$ sudo rm ./bpf-mountpoint/test
$ sudo bpftool prog show id 783
Error: get by id (783): No such file or directory

ವಸ್ತುಗಳನ್ನು ತೆಗೆದುಹಾಕುವುದು

ವಸ್ತುಗಳನ್ನು ಅಳಿಸುವ ಬಗ್ಗೆ ಮಾತನಾಡುತ್ತಾ, ನಾವು ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಹುಕ್‌ನಿಂದ (ಈವೆಂಟ್ ಜನರೇಟರ್) ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಿದ ನಂತರ, ಯಾವುದೇ ಹೊಸ ಈವೆಂಟ್ ಅದನ್ನು ಪ್ರಾರಂಭಿಸಲು ಕಾರಣವಾಗುವುದಿಲ್ಲ ಎಂದು ಸ್ಪಷ್ಟಪಡಿಸುವುದು ಅವಶ್ಯಕ, ಆದಾಗ್ಯೂ, ಪ್ರೋಗ್ರಾಂನ ಎಲ್ಲಾ ಪ್ರಸ್ತುತ ನಿದರ್ಶನಗಳು ಸಾಮಾನ್ಯ ಕ್ರಮದಲ್ಲಿ ಕೊನೆಗೊಳ್ಳುತ್ತವೆ.

ಕೆಲವು ರೀತಿಯ BPF ಪ್ರೋಗ್ರಾಂಗಳು ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ತಕ್ಷಣವೇ ಬದಲಾಯಿಸಲು ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ, ಅಂದರೆ ಅನುಕ್ರಮದ ಪರಮಾಣುತ್ವವನ್ನು ಒದಗಿಸುತ್ತದೆ replace = detach old program, attach new programಈ ಸಂದರ್ಭದಲ್ಲಿ, ಪ್ರೋಗ್ರಾಂನ ಹಳೆಯ ಆವೃತ್ತಿಯ ಎಲ್ಲಾ ಸಕ್ರಿಯ ನಿದರ್ಶನಗಳು ತಮ್ಮ ಕೆಲಸವನ್ನು ಪೂರ್ಣಗೊಳಿಸುತ್ತವೆ ಮತ್ತು ಹೊಸ ಪ್ರೋಗ್ರಾಂನಿಂದ ಹೊಸ ಈವೆಂಟ್ ಹ್ಯಾಂಡ್ಲರ್‌ಗಳನ್ನು ರಚಿಸಲಾಗುತ್ತದೆ ಮತ್ತು ಇಲ್ಲಿ "ಪರಮಾಣುತೆ" ಎಂದರೆ ಯಾವುದೇ ಈವೆಂಟ್ ಅನ್ನು ತಪ್ಪಿಸಿಕೊಳ್ಳಲಾಗುವುದಿಲ್ಲ.

ಈವೆಂಟ್ ಮೂಲಗಳಿಗೆ ಕಾರ್ಯಕ್ರಮಗಳನ್ನು ಲಗತ್ತಿಸುವುದು

ಈ ಲೇಖನದಲ್ಲಿ, ನಾವು ಕಾರ್ಯಕ್ರಮಗಳನ್ನು ಈವೆಂಟ್ ಮೂಲಗಳಿಗೆ ಸಂಪರ್ಕಿಸುವುದನ್ನು ಪ್ರತ್ಯೇಕವಾಗಿ ವಿವರಿಸುವುದಿಲ್ಲ, ಏಕೆಂದರೆ ಇದನ್ನು ನಿರ್ದಿಷ್ಟ ರೀತಿಯ ಕಾರ್ಯಕ್ರಮದ ಸಂದರ್ಭದಲ್ಲಿ ಅಧ್ಯಯನ ಮಾಡುವುದು ಅರ್ಥಪೂರ್ಣವಾಗಿದೆ. ನೋಡಿ ಉದಾಹರಣೆ ಕೆಳಗೆ, ಇದರಲ್ಲಿ ನಾವು XDP ನಂತಹ ಪ್ರೋಗ್ರಾಂಗಳು ಹೇಗೆ ಸಂಪರ್ಕಗೊಂಡಿವೆ ಎಂಬುದನ್ನು ತೋರಿಸುತ್ತೇವೆ.

ಬಿಪಿಎಫ್ ಸಿಸ್ಟಮ್ ಕರೆಯನ್ನು ಬಳಸಿಕೊಂಡು ವಸ್ತುಗಳನ್ನು ನಿರ್ವಹಿಸುವುದು

ಬಿಪಿಎಫ್ ಕಾರ್ಯಕ್ರಮಗಳು

ಎಲ್ಲಾ BPF ವಸ್ತುಗಳನ್ನು ಸಿಸ್ಟಮ್ ಕರೆಯನ್ನು ಬಳಸಿಕೊಂಡು ಬಳಕೆದಾರ ಸ್ಥಳದಿಂದ ರಚಿಸಲಾಗುತ್ತದೆ ಮತ್ತು ನಿರ್ವಹಿಸಲಾಗುತ್ತದೆ. bpf, ಇದು ಈ ಕೆಳಗಿನ ಮೂಲಮಾದರಿಯನ್ನು ಹೊಂದಿದೆ:

#include <linux/bpf.h>

int bpf(int cmd, union bpf_attr *attr, unsigned int size);

ತಂಡ ಇಲ್ಲಿದೆ cmd - ಇದು ಪ್ರಕಾರದ ಮೌಲ್ಯಗಳಲ್ಲಿ ಒಂದಾಗಿದೆ enum bpf_cmd, attr — ಒಂದು ನಿರ್ದಿಷ್ಟ ಪ್ರೋಗ್ರಾಂನ ನಿಯತಾಂಕಗಳಿಗೆ ಪಾಯಿಂಟರ್ ಮತ್ತು size — ಪಾಯಿಂಟರ್ ಮೂಲಕ ವಸ್ತುವಿನ ಗಾತ್ರ, ಅಂದರೆ ಸಾಮಾನ್ಯವಾಗಿ ಅದು sizeof(*attr)ಕರ್ನಲ್ 5.8 ರಲ್ಲಿ ಸಿಸ್ಟಮ್ ಕರೆ bpf 34 ವಿಭಿನ್ನ ಆಜ್ಞೆಗಳನ್ನು ಬೆಂಬಲಿಸುತ್ತದೆ, ಮತ್ತು ವ್ಯಾಖ್ಯಾನ union bpf_attr 200 ಸಾಲುಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳುತ್ತದೆ. ಆದರೆ ಇದು ನಮ್ಮನ್ನು ಹೆದರಿಸಬಾರದು, ಏಕೆಂದರೆ ನಾವು ಹಲವಾರು ಲೇಖನಗಳ ಅವಧಿಯಲ್ಲಿ ಆಜ್ಞೆಗಳು ಮತ್ತು ನಿಯತಾಂಕಗಳನ್ನು ತಿಳಿದುಕೊಳ್ಳುತ್ತೇವೆ.

ತಂಡದಿಂದ ಪ್ರಾರಂಭಿಸೋಣ. BPF_PROG_LOAD, ಇದು BPF ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ರಚಿಸುತ್ತದೆ — BPF ಸೂಚನೆಗಳ ಗುಂಪನ್ನು ತೆಗೆದುಕೊಂಡು ಅದನ್ನು ಕರ್ನಲ್‌ಗೆ ಲೋಡ್ ಮಾಡುತ್ತದೆ. ಲೋಡ್ ಆಗುವ ಕ್ಷಣದಲ್ಲಿ, ಪರಿಶೀಲಕವನ್ನು ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತದೆ, ಮತ್ತು ನಂತರ JIT ಕಂಪೈಲರ್, ಮತ್ತು, ಯಶಸ್ವಿ ಕಾರ್ಯಗತಗೊಳಿಸುವಿಕೆಯ ನಂತರ, ಬಳಕೆದಾರರಿಗೆ ಪ್ರೋಗ್ರಾಂನ ಫೈಲ್ ವಿವರಣೆಯನ್ನು ಹಿಂತಿರುಗಿಸಲಾಗುತ್ತದೆ. ಹಿಂದಿನ ವಿಭಾಗದಲ್ಲಿ ಮುಂದೆ ಏನಾಗುತ್ತದೆ ಎಂದು ನಾವು ನೋಡಿದ್ದೇವೆ. ಬಿಪಿಎಫ್ ವಸ್ತುಗಳ ಜೀವನ ಚಕ್ರದ ಬಗ್ಗೆ.

ಈಗ ನಾವು ಸರಳವಾದ BPF ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಲೋಡ್ ಮಾಡುವ ಕಸ್ಟಮ್ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಬರೆಯುತ್ತೇವೆ, ಆದರೆ ಮೊದಲು ನಾವು ಯಾವ ರೀತಿಯ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಲೋಡ್ ಮಾಡಲು ಬಯಸುತ್ತೇವೆ ಎಂಬುದನ್ನು ನಿರ್ಧರಿಸಬೇಕು - ನಾವು ಆಯ್ಕೆ ಮಾಡಬೇಕಾಗುತ್ತದೆ ಟಿಪ್ಪಿ ಮತ್ತು ಈ ಪ್ರಕಾರದೊಳಗೆ ಪರಿಶೀಲಕ ಪರೀಕ್ಷೆಯಲ್ಲಿ ಉತ್ತೀರ್ಣರಾಗುವ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಬರೆಯಿರಿ. ಆದಾಗ್ಯೂ, ಪ್ರಕ್ರಿಯೆಯನ್ನು ಸಂಕೀರ್ಣಗೊಳಿಸದಿರಲು, ಇಲ್ಲಿ ಸಿದ್ಧ ಪರಿಹಾರವಿದೆ: ನಾವು ಪ್ರಕಾರದ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ತೆಗೆದುಕೊಳ್ಳುತ್ತೇವೆ BPF_PROG_TYPE_XDP, ಇದು ಮೌಲ್ಯವನ್ನು ಹಿಂತಿರುಗಿಸುತ್ತದೆ XDP_PASS (ಎಲ್ಲಾ ಪ್ಯಾಕೆಟ್‌ಗಳನ್ನು ಬಿಟ್ಟುಬಿಡಿ). ಬಿಪಿಎಫ್ ಅಸೆಂಬ್ಲರ್‌ನಲ್ಲಿ ಇದು ತುಂಬಾ ಸರಳವಾಗಿ ಕಾಣುತ್ತದೆ:

r0 = 2
exit

ನಾವು ಅದರ ಬಗ್ಗೆ ನಿರ್ಧರಿಸಿದ ನಂತರ, ನಾವು ಅಪ್‌ಲೋಡ್ ಮಾಡುತ್ತೇವೆ, ಅದನ್ನು ಹೇಗೆ ಮಾಡಬೇಕೆಂದು ನಾವು ನಿಮಗೆ ಹೇಳಬಹುದು:

#define _GNU_SOURCE
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/bpf.h>

static inline __u64 ptr_to_u64(const void *ptr)
{
        return (__u64) (unsigned long) ptr;
}

int main(void)
{
    struct bpf_insn insns[] = {
        {
            .code = BPF_ALU64 | BPF_MOV | BPF_K,
            .dst_reg = BPF_REG_0,
            .imm = XDP_PASS
        },
        {
            .code = BPF_JMP | BPF_EXIT
        },
    };

    union bpf_attr attr = {
        .prog_type = BPF_PROG_TYPE_XDP,
        .insns     = ptr_to_u64(insns),
        .insn_cnt  = sizeof(insns)/sizeof(insns[0]),
        .license   = ptr_to_u64("GPL"),
    };

    strncpy(attr.prog_name, "woo", sizeof(attr.prog_name));
    syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));

    for ( ;; )
        pause();
}

ಪ್ರೋಗ್ರಾಂನಲ್ಲಿನ ಆಸಕ್ತಿದಾಯಕ ಘಟನೆಗಳು ಶ್ರೇಣಿಯ ವ್ಯಾಖ್ಯಾನದೊಂದಿಗೆ ಪ್ರಾರಂಭವಾಗುತ್ತವೆ. insns — ಯಂತ್ರ ಸಂಕೇತಗಳಲ್ಲಿ ನಮ್ಮ BPF ಪ್ರೋಗ್ರಾಂ. ಈ ಸಂದರ್ಭದಲ್ಲಿ, BPF ಪ್ರೋಗ್ರಾಂನ ಪ್ರತಿಯೊಂದು ಸೂಚನೆಯನ್ನು ಒಂದು ರಚನೆಯಲ್ಲಿ ಪ್ಯಾಕ್ ಮಾಡಲಾಗುತ್ತದೆ. bpf_insn. ಮೊದಲ ಅಂಶ insns ಸೂಚನೆಗಳನ್ನು ಪಾಲಿಸುತ್ತದೆ r0 = 2, ಎರಡನೆಯದು - exit.

ಹಿಮ್ಮೆಟ್ಟುವಿಕೆ. ಕರ್ನಲ್ ಯಂತ್ರ ಸಂಕೇತಗಳನ್ನು ಬರೆಯಲು ಹೆಚ್ಚು ಅನುಕೂಲಕರ ಮ್ಯಾಕ್ರೋಗಳನ್ನು ವ್ಯಾಖ್ಯಾನಿಸುತ್ತದೆ ಮತ್ತು ಕರ್ನಲ್ ಹೆಡರ್ ಫೈಲ್ ಅನ್ನು ಬಳಸುತ್ತದೆ tools/include/linux/filter.h ನಾವು ಬರೆಯಬಹುದು

struct bpf_insn insns[] = {
    BPF_MOV64_IMM(BPF_REG_0, XDP_PASS),
    BPF_EXIT_INSN()
};

ಆದರೆ ಯಂತ್ರ ಸಂಕೇತದಲ್ಲಿ BPF ಕಾರ್ಯಕ್ರಮಗಳನ್ನು ಬರೆಯುವುದು ಕರ್ನಲ್ ಪರೀಕ್ಷೆಗಳು ಮತ್ತು BPF ಕುರಿತು ಲೇಖನಗಳನ್ನು ಬರೆಯಲು ಮಾತ್ರ ಅಗತ್ಯವಿರುವುದರಿಂದ, ಈ ಮ್ಯಾಕ್ರೋಗಳ ಅನುಪಸ್ಥಿತಿಯು ಡೆವಲಪರ್‌ನ ಜೀವನವನ್ನು ನಿಜವಾಗಿಯೂ ಸಂಕೀರ್ಣಗೊಳಿಸುವುದಿಲ್ಲ.

ನಾವು BPF ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ವ್ಯಾಖ್ಯಾನಿಸಿದ ನಂತರ, ಅದನ್ನು ಕರ್ನಲ್‌ಗೆ ಲೋಡ್ ಮಾಡಲು ಮುಂದುವರಿಯುತ್ತೇವೆ. ನಮ್ಮ ಕನಿಷ್ಠ ನಿಯತಾಂಕಗಳ ಸೆಟ್ attr ಪ್ರೋಗ್ರಾಂ ಪ್ರಕಾರ, ಸೂಚನೆಗಳ ಸೆಟ್ ಮತ್ತು ಸಂಖ್ಯೆ, ಅಗತ್ಯವಿರುವ ಪರವಾನಗಿ ಮತ್ತು ಹೆಸರನ್ನು ಒಳಗೊಂಡಿದೆ "woo"ಬೂಟ್ ಮಾಡಿದ ನಂತರ ಸಿಸ್ಟಂನಲ್ಲಿ ನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಹುಡುಕಲು ನಾವು ಇದನ್ನು ಬಳಸುತ್ತೇವೆ. ಭರವಸೆ ನೀಡಿದಂತೆ, ಸಿಸ್ಟಮ್ ಕರೆಯನ್ನು ಬಳಸಿಕೊಂಡು ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಸಿಸ್ಟಮ್‌ಗೆ ಲೋಡ್ ಮಾಡಲಾಗುತ್ತದೆ. bpf.

ಪ್ರೋಗ್ರಾಂನ ಕೊನೆಯಲ್ಲಿ ನಾವು ಪೇಲೋಡ್ ಅನ್ನು ಅನುಕರಿಸುವ ಅನಂತ ಲೂಪ್‌ಗೆ ಹೋಗುತ್ತೇವೆ. ಅದು ಇಲ್ಲದೆ, ಸಿಸ್ಟಮ್ ಕರೆ ನಮಗೆ ಹಿಂತಿರುಗಿಸಿದ ಫೈಲ್ ಡಿಸ್ಕ್ರಿಪ್ಟರ್ ಅನ್ನು ಮುಚ್ಚಿದಾಗ ಕರ್ನಲ್ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಕೊಲ್ಲುತ್ತದೆ. bpf, ಮತ್ತು ನಾವು ಅದನ್ನು ವ್ಯವಸ್ಥೆಯಲ್ಲಿ ನೋಡುವುದಿಲ್ಲ.

ಸರಿ, ನಾವು ಪರೀಕ್ಷೆಗೆ ಸಿದ್ಧರಿದ್ದೇವೆ. ನಾವು ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ನಿರ್ಮಿಸಿ ಚಲಾಯಿಸೋಣ straceಎಲ್ಲವೂ ಸರಿಯಾಗಿ ಕೆಲಸ ಮಾಡುತ್ತಿದೆಯೇ ಎಂದು ಪರಿಶೀಲಿಸಲು:

$ clang -g -O2 simple-prog.c -o simple-prog

$ sudo strace ./simple-prog
execve("./simple-prog", ["./simple-prog"], 0x7ffc7b553480 /* 13 vars */) = 0
...
bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_XDP, insn_cnt=2, insns=0x7ffe03c4ed50, license="GPL", log_level=0, log_size=0, log_buf=NULL, kern_version=KERNEL_V
ERSION(0, 0, 0), prog_flags=0, prog_name="woo", prog_ifindex=0, expected_attach_type=BPF_CGROUP_INET_INGRESS}, 72) = 3
pause(

ಎಲ್ಲವೂ ಚೆನ್ನಾಗಿದೆ, bpf(2) ನಮಗೆ ವಿವರಣೆ 3 ಅನ್ನು ಹಿಂತಿರುಗಿಸಿದೆ ಮತ್ತು ನಾವು ಅನಂತ ಲೂಪ್‌ಗೆ ಹೋದೆವು pause(). ನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ವ್ಯವಸ್ಥೆಯಲ್ಲಿ ಹುಡುಕಲು ಪ್ರಯತ್ನಿಸೋಣ. ಇದನ್ನು ಮಾಡಲು, ನಾವು ಇನ್ನೊಂದು ಟರ್ಮಿನಲ್‌ಗೆ ಹೋಗಿ ಉಪಯುಕ್ತತೆಯನ್ನು ಬಳಸುತ್ತೇವೆ. bpftool:

# bpftool prog | grep -A3 woo
390: xdp  name woo  tag 3b185187f1855c4c  gpl
        loaded_at 2020-08-31T24:66:44+0000  uid 0
        xlated 16B  jited 40B  memlock 4096B
        pids simple-prog(10381)

ಸಿಸ್ಟಂನಲ್ಲಿ ಒಂದು ಪ್ರೋಗ್ರಾಂ ಲೋಡ್ ಆಗಿರುವುದನ್ನು ನಾವು ನೋಡುತ್ತೇವೆ. woo ಅವರ ಜಾಗತಿಕ ID 390 ಆಗಿದ್ದು ಪ್ರಸ್ತುತ ಪ್ರಗತಿಯಲ್ಲಿದೆ. simple-prog ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಸೂಚಿಸುವ ತೆರೆದ ಫೈಲ್ ವಿವರಣೆ ಇದೆ (ಮತ್ತು simple-prog ಕೆಲಸವನ್ನು ಪೂರ್ಣಗೊಳಿಸುತ್ತದೆ, ನಂತರ woo ಕಣ್ಮರೆಯಾಗುತ್ತದೆ). ನಿರೀಕ್ಷೆಯಂತೆ, ಪ್ರೋಗ್ರಾಂ woo BPF ಆರ್ಕಿಟೆಕ್ಚರ್‌ನಲ್ಲಿ ಬೈನರಿ ಕೋಡ್‌ಗಳ 16 ಬೈಟ್‌ಗಳನ್ನು - ಎರಡು ಸೂಚನೆಗಳನ್ನು - ತೆಗೆದುಕೊಳ್ಳುತ್ತದೆ, ಆದರೆ ಸ್ಥಳೀಯ ರೂಪದಲ್ಲಿ (x86_64) ಇದು ಈಗಾಗಲೇ 40 ಬೈಟ್‌ಗಳನ್ನು ಹೊಂದಿದೆ. ನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಅದರ ಮೂಲ ರೂಪದಲ್ಲಿ ನೋಡೋಣ:

# bpftool prog dump xlated id 390
   0: (b7) r0 = 2
   1: (95) exit

ಆಶ್ಚರ್ಯವೇನಿಲ್ಲ. ಈಗ JIT ಕಂಪೈಲರ್ ನಿಂದ ರಚಿಸಲಾದ ಕೋಡ್ ಅನ್ನು ನೋಡೋಣ:

# bpftool prog dump jited id 390
bpf_prog_3b185187f1855c4c_woo:
   0:   nopl   0x0(%rax,%rax,1)
   5:   push   %rbp
   6:   mov    %rsp,%rbp
   9:   sub    $0x0,%rsp
  10:   push   %rbx
  11:   push   %r13
  13:   push   %r14
  15:   push   %r15
  17:   pushq  $0x0
  19:   mov    $0x2,%eax
  1e:   pop    %rbx
  1f:   pop    %r15
  21:   pop    %r14
  23:   pop    %r13
  25:   pop    %rbx
  26:   leaveq
  27:   retq

ತುಂಬಾ ಪರಿಣಾಮಕಾರಿಯಾಗಿಲ್ಲ exit(2), ಆದರೆ ನ್ಯಾಯವಾಗಿ ಹೇಳಬೇಕೆಂದರೆ, ನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ತುಂಬಾ ಸರಳವಾಗಿದೆ ಮತ್ತು ಕ್ಷುಲ್ಲಕವಲ್ಲದ ಕಾರ್ಯಕ್ರಮಗಳಿಗೆ, JIT ಕಂಪೈಲರ್‌ನಿಂದ ಸೇರಿಸಲಾದ ಪ್ರೊಲಾಗ್ ಮತ್ತು ಎಪಿಲೋಗ್ ಸಹಜವಾಗಿಯೇ ಅವಶ್ಯಕವಾಗಿದೆ.

ನಕ್ಷೆಗಳು

BPF ಪ್ರೋಗ್ರಾಂಗಳು ಇತರ BPF ಪ್ರೋಗ್ರಾಂಗಳು ಮತ್ತು ಬಳಕೆದಾರ-ಸ್ಥಳ ಪ್ರೋಗ್ರಾಂಗಳಿಗೆ ಪ್ರವೇಶಿಸಬಹುದಾದ ರಚನಾತ್ಮಕ ಮೆಮೊರಿ ಪ್ರದೇಶಗಳನ್ನು ಬಳಸಬಹುದು. ಈ ವಸ್ತುಗಳನ್ನು ನಕ್ಷೆಗಳು ಎಂದು ಕರೆಯಲಾಗುತ್ತದೆ, ಮತ್ತು ಈ ವಿಭಾಗದಲ್ಲಿ ಸಿಸ್ಟಮ್ ಕರೆಯನ್ನು ಬಳಸಿಕೊಂಡು ಅವುಗಳನ್ನು ಹೇಗೆ ನಿರ್ವಹಿಸುವುದು ಎಂಬುದನ್ನು ನಾವು ತೋರಿಸುತ್ತೇವೆ. bpf.

ನಕ್ಷೆಗಳ ಸಾಮರ್ಥ್ಯಗಳು ಹಂಚಿಕೆಯ ಮೆಮೊರಿಗೆ ಪ್ರವೇಶಕ್ಕೆ ಸೀಮಿತವಾಗಿಲ್ಲ ಎಂದು ಈಗಿನಿಂದಲೇ ಹೇಳೋಣ. ವಿಶೇಷ ಉದ್ದೇಶದ ನಕ್ಷೆಗಳು ಇವೆ, ಉದಾಹರಣೆಗೆ, BPF ಪ್ರೋಗ್ರಾಂಗಳಿಗೆ ಪಾಯಿಂಟರ್‌ಗಳು ಅಥವಾ ನೆಟ್‌ವರ್ಕ್ ಇಂಟರ್‌ಫೇಸ್‌ಗಳಿಗೆ ಪಾಯಿಂಟರ್‌ಗಳು, ಪರ್ಫ್ ಈವೆಂಟ್‌ಗಳೊಂದಿಗೆ ಕೆಲಸ ಮಾಡಲು ನಕ್ಷೆಗಳು ಇತ್ಯಾದಿಗಳನ್ನು ಒಳಗೊಂಡಿರುತ್ತದೆ. ಓದುಗರನ್ನು ಗೊಂದಲಕ್ಕೀಡಾಗದಂತೆ ನಾವು ಅವುಗಳ ಬಗ್ಗೆ ಇಲ್ಲಿ ಮಾತನಾಡುವುದಿಲ್ಲ. ಹೆಚ್ಚುವರಿಯಾಗಿ, ನಾವು ಸಿಂಕ್ರೊನೈಸೇಶನ್ ಸಮಸ್ಯೆಗಳನ್ನು ನಿರ್ಲಕ್ಷಿಸುತ್ತೇವೆ, ಏಕೆಂದರೆ ಇದು ನಮ್ಮ ಉದಾಹರಣೆಗಳಿಗೆ ಮುಖ್ಯವಲ್ಲ. ಲಭ್ಯವಿರುವ ನಕ್ಷೆ ಪ್ರಕಾರಗಳ ಸಂಪೂರ್ಣ ಪಟ್ಟಿಯನ್ನು ಇಲ್ಲಿ ಕಾಣಬಹುದು <linux/bpf.h>, ಮತ್ತು ಈ ವಿಭಾಗದಲ್ಲಿ ನಾವು ಐತಿಹಾಸಿಕವಾಗಿ ಮೊದಲ ವಿಧವಾದ ಹ್ಯಾಶ್ ಟೇಬಲ್ ಅನ್ನು ಉದಾಹರಣೆಯಾಗಿ ತೆಗೆದುಕೊಳ್ಳುತ್ತೇವೆ BPF_MAP_TYPE_HASH.

ನೀವು C++ ನಲ್ಲಿ ಹ್ಯಾಶ್ ಟೇಬಲ್ ಅನ್ನು ರಚಿಸುತ್ತಿದ್ದರೆ, ನೀವು ಹೀಗೆ ಹೇಳುತ್ತೀರಿ unordered_map<int,long> woo, ರಷ್ಯನ್ ಭಾಷೆಯಲ್ಲಿ ಇದರ ಅರ್ಥ "ನನಗೆ ಟೇಬಲ್ ಬೇಕು woo ಅನಿಯಮಿತ ಗಾತ್ರದ, ಅದರ ಕೀಲಿಗಳು ಪ್ರಕಾರವನ್ನು ಹೊಂದಿವೆ int, ಮತ್ತು ಮೌಲ್ಯಗಳು ಪ್ರಕಾರವಾಗಿದೆ long". ಬಿಪಿಎಫ್ ಹ್ಯಾಶ್ ಟೇಬಲ್ ಅನ್ನು ರಚಿಸಲು ನಾವು ಸರಿಸುಮಾರು ಅದೇ ಕೆಲಸವನ್ನು ಮಾಡಬೇಕಾಗಿದೆ, ಟೇಬಲ್‌ನ ಗರಿಷ್ಠ ಗಾತ್ರವನ್ನು ನಾವು ನಿರ್ದಿಷ್ಟಪಡಿಸಬೇಕಾದ ಹೊಂದಾಣಿಕೆಯೊಂದಿಗೆ, ಮತ್ತು ಕೀಗಳು ಮತ್ತು ಮೌಲ್ಯಗಳ ಪ್ರಕಾರಗಳ ಬದಲಿಗೆ ನಾವು ಅವುಗಳ ಗಾತ್ರಗಳನ್ನು ಬೈಟ್‌ಗಳಲ್ಲಿ ನಿರ್ದಿಷ್ಟಪಡಿಸಬೇಕಾಗಿದೆ. ನಕ್ಷೆಗಳನ್ನು ರಚಿಸಲು, ಆಜ್ಞೆಯನ್ನು ಬಳಸಿ BPF_MAP_CREATE ಸಿಸ್ಟಮ್ ಕರೆ bpf. ನಕ್ಷೆಯನ್ನು ರಚಿಸುವ ಹೆಚ್ಚು ಕಡಿಮೆ ಕನಿಷ್ಠ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ನೋಡೋಣ. BPF ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಲೋಡ್ ಮಾಡಿದ ಹಿಂದಿನ ಪ್ರೋಗ್ರಾಂ ನಂತರ, ಇದು ಸರಳವಾಗಿ ಕಾಣಿಸಬೇಕು:

$ cat simple-map.c
#define _GNU_SOURCE
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/bpf.h>

int main(void)
{
    union bpf_attr attr = {
        .map_type = BPF_MAP_TYPE_HASH,
        .key_size = sizeof(int),
        .value_size = sizeof(int),
        .max_entries = 4,
    };
    strncpy(attr.map_name, "woo", sizeof(attr.map_name));
    syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));

    for ( ;; )
        pause();
}

ಇಲ್ಲಿ ನಾವು ನಿಯತಾಂಕಗಳ ಗುಂಪನ್ನು ವ್ಯಾಖ್ಯಾನಿಸುತ್ತೇವೆ attr, ಇದರಲ್ಲಿ ನಾವು "ನನಗೆ ಕೀಲಿಗಳು ಮತ್ತು ಗಾತ್ರದ ಮೌಲ್ಯಗಳನ್ನು ಹೊಂದಿರುವ ಹ್ಯಾಶ್ ಟೇಬಲ್ ಬೇಕು" ಎಂದು ಹೇಳುತ್ತೇವೆ. sizeof(int), ಇದರಲ್ಲಿ ನಾನು ಗರಿಷ್ಠ ನಾಲ್ಕು ಅಂಶಗಳನ್ನು ಹಾಕಬಲ್ಲೆ." BPF ನಕ್ಷೆಗಳನ್ನು ರಚಿಸುವಾಗ, ನೀವು ಇತರ ನಿಯತಾಂಕಗಳನ್ನು ಸಹ ನಿರ್ದಿಷ್ಟಪಡಿಸಬಹುದು, ಉದಾಹರಣೆಗೆ, ಪ್ರೋಗ್ರಾಂನ ಉದಾಹರಣೆಯಲ್ಲಿರುವಂತೆ, ನಾವು ವಸ್ತುವಿನ ಹೆಸರನ್ನು ಹೀಗೆ ನಿರ್ದಿಷ್ಟಪಡಿಸಿದ್ದೇವೆ "woo".

ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಕಂಪೈಲ್ ಮಾಡಿ ರನ್ ಮಾಡೋಣ:

$ clang -g -O2 simple-map.c -o simple-map
$ sudo strace ./simple-map
execve("./simple-map", ["./simple-map"], 0x7ffd40a27070 /* 14 vars */) = 0
...
bpf(BPF_MAP_CREATE, {map_type=BPF_MAP_TYPE_HASH, key_size=4, value_size=4, max_entries=4, map_name="woo", ...}, 72) = 3
pause(

ಸಿಸ್ಟಮ್ ಕರೆ ಇಲ್ಲಿದೆ bpf(2) ನಕ್ಷೆ ವಿವರಣಾತ್ಮಕ ಸಂಖ್ಯೆಯನ್ನು ನಮಗೆ ಹಿಂತಿರುಗಿಸಿದೆ. 3 ತದನಂತರ ಪ್ರೋಗ್ರಾಂ, ನಿರೀಕ್ಷೆಯಂತೆ, ಸಿಸ್ಟಮ್ ಕರೆಯಲ್ಲಿ ಮುಂದಿನ ಸೂಚನೆಗಳಿಗಾಗಿ ಕಾಯುತ್ತದೆ. pause(2).

ಈಗ ನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಹಿನ್ನೆಲೆಗೆ ಕಳುಹಿಸೋಣ ಅಥವಾ ಇನ್ನೊಂದು ಟರ್ಮಿನಲ್ ಅನ್ನು ತೆರೆಯೋಣ ಮತ್ತು ಉಪಯುಕ್ತತೆಯನ್ನು ಬಳಸಿಕೊಂಡು ನಮ್ಮ ವಸ್ತುವನ್ನು ನೋಡೋಣ. bpftool (ನಮ್ಮ ನಕ್ಷೆಯನ್ನು ಅದರ ಹೆಸರಿನಿಂದ ಇತರ ನಕ್ಷೆಗಳಿಂದ ಪ್ರತ್ಯೇಕಿಸಬಹುದು):

$ sudo bpftool map
...
114: hash  name woo  flags 0x0
        key 4B  value 4B  max_entries 4  memlock 4096B
...

114 ಸಂಖ್ಯೆಯು ನಮ್ಮ ವಸ್ತುವಿನ ಜಾಗತಿಕ ID ಆಗಿದೆ. ಸಿಸ್ಟಂನಲ್ಲಿರುವ ಯಾವುದೇ ಪ್ರೋಗ್ರಾಂ ಈ ID ಯನ್ನು ಬಳಸಿಕೊಂಡು ಆಜ್ಞೆಯನ್ನು ಬಳಸಿಕೊಂಡು ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ನಕ್ಷೆಯನ್ನು ತೆರೆಯಬಹುದು. BPF_MAP_GET_FD_BY_ID ಸಿಸ್ಟಮ್ ಕರೆ bpf.

ಈಗ ನಾವು ನಮ್ಮ ಹ್ಯಾಶ್ ಟೇಬಲ್‌ನೊಂದಿಗೆ ಆಟವಾಡಬಹುದು. ಅದರ ವಿಷಯಗಳನ್ನು ನೋಡೋಣ:

$ sudo bpftool map dump id 114
Found 0 elements

ಅದು ಖಾಲಿಯಾಗಿದೆ. ಅದರಲ್ಲಿ ಒಂದು ಮೌಲ್ಯವನ್ನು ಹಾಕೋಣ. hash[1] = 1:

$ sudo bpftool map update id 114 key 1 0 0 0 value 1 0 0 0

ಮತ್ತೊಮ್ಮೆ ಟೇಬಲ್ ನೋಡೋಣ:

$ sudo bpftool map dump id 114
key: 01 00 00 00  value: 01 00 00 00
Found 1 element

ಹುರ್ರೇ! ನಾವು ಒಂದು ಅಂಶವನ್ನು ಸೇರಿಸುವಲ್ಲಿ ಯಶಸ್ವಿಯಾಗಿದ್ದೇವೆ. ಇದನ್ನು ಮಾಡಲು ನಾವು ಬೈಟ್ ಮಟ್ಟದಲ್ಲಿ ಕೆಲಸ ಮಾಡಬೇಕು ಎಂಬುದನ್ನು ಗಮನಿಸಿ, ಏಕೆಂದರೆ bptftool ಹ್ಯಾಶ್ ಟೇಬಲ್‌ನಲ್ಲಿರುವ ಮೌಲ್ಯಗಳು ಯಾವ ಪ್ರಕಾರದ್ದಾಗಿವೆ ಎಂದು ತಿಳಿದಿಲ್ಲ. (ಇದಕ್ಕೆ BTF ಬಳಸಿ ಈ ಜ್ಞಾನವನ್ನು ನೀಡಬಹುದು, ಆದರೆ ಈಗ ಅದು ವಿಷಯವಲ್ಲ.)

bpftool ಅಂಶಗಳನ್ನು ನಿಖರವಾಗಿ ಹೇಗೆ ಓದುತ್ತದೆ ಮತ್ತು ಸೇರಿಸುತ್ತದೆ? ಹುಡ್ ಅಡಿಯಲ್ಲಿ ನೋಡೋಣ:

$ sudo strace -e bpf bpftool map dump id 114
bpf(BPF_MAP_GET_FD_BY_ID, {map_id=114, next_id=0, open_flags=0}, 120) = 3
bpf(BPF_MAP_GET_NEXT_KEY, {map_fd=3, key=NULL, next_key=0x55856ab65280}, 120) = 0
bpf(BPF_MAP_LOOKUP_ELEM, {map_fd=3, key=0x55856ab65280, value=0x55856ab652a0}, 120) = 0
key: 01 00 00 00  value: 01 00 00 00
bpf(BPF_MAP_GET_NEXT_KEY, {map_fd=3, key=0x55856ab65280, next_key=0x55856ab65280}, 120) = -1 ENOENT

ಮೊದಲು, ನಾವು ಆಜ್ಞೆಯನ್ನು ಬಳಸಿಕೊಂಡು ಅದರ ಜಾಗತಿಕ ID ಯಿಂದ ನಕ್ಷೆಯನ್ನು ತೆರೆದಿದ್ದೇವೆ BPF_MAP_GET_FD_BY_ID и bpf(2) ನಮಗೆ ವಿವರಣೆ 3 ಅನ್ನು ಹಿಂತಿರುಗಿಸಿದೆ. ನಂತರ, ಆಜ್ಞೆಯನ್ನು ಬಳಸಿ BPF_MAP_GET_NEXT_KEY ನಾವು ಕೋಷ್ಟಕದಲ್ಲಿ ಮೊದಲ ಕೀಲಿಯನ್ನು ಪಾಸ್ ಮಾಡುವ ಮೂಲಕ ಕಂಡುಕೊಂಡಿದ್ದೇವೆ NULL "ಹಿಂದಿನ" ಕೀಲಿಗೆ ಸೂಚಕವಾಗಿ. ಕೀಲಿಯನ್ನು ನೀಡಿದರೆ, ನಾವು ಮಾಡಬಹುದು BPF_MAP_LOOKUP_ELEM, ಇದು ಪಾಯಿಂಟರ್‌ಗೆ ಮೌಲ್ಯವನ್ನು ಹಿಂತಿರುಗಿಸುತ್ತದೆ valueಮುಂದಿನ ಹಂತವೆಂದರೆ ನಾವು ಪಾಯಿಂಟರ್ ಅನ್ನು ಪ್ರಸ್ತುತ ಕೀಲಿಗೆ ರವಾನಿಸುವ ಮೂಲಕ ಮುಂದಿನ ಅಂಶವನ್ನು ಕಂಡುಹಿಡಿಯಲು ಪ್ರಯತ್ನಿಸುತ್ತೇವೆ, ಆದರೆ ನಮ್ಮ ಕೋಷ್ಟಕವು ಕೇವಲ ಒಂದು ಅಂಶ ಮತ್ತು ಆಜ್ಞೆಯನ್ನು ಮಾತ್ರ ಹೊಂದಿರುತ್ತದೆ BPF_MAP_GET_NEXT_KEY ಹಿಂದಿರುಗಿಸುತ್ತದೆ ENOENT.

ಸರಿ, ಕೀ 1 ರ ಮೌಲ್ಯವನ್ನು ಬದಲಾಯಿಸೋಣ, ನಮ್ಮ ವ್ಯವಹಾರ ತರ್ಕವು ನಮಗೆ ಬರೆಯಲು ಅಗತ್ಯವಿದೆ ಎಂದು ಹೇಳೋಣ hash[1] = 2:

$ sudo strace -e bpf bpftool map update id 114 key 1 0 0 0 value 2 0 0 0
bpf(BPF_MAP_GET_FD_BY_ID, {map_id=114, next_id=0, open_flags=0}, 120) = 3
bpf(BPF_MAP_UPDATE_ELEM, {map_fd=3, key=0x55dcd72be260, value=0x55dcd72be280, flags=BPF_ANY}, 120) = 0

ನಿರೀಕ್ಷೆಯಂತೆ, ಇದು ತುಂಬಾ ಸರಳವಾಗಿದೆ: ಆಜ್ಞೆ BPF_MAP_GET_FD_BY_ID ನಮ್ಮ ನಕ್ಷೆಯನ್ನು ID ಮತ್ತು ಆಜ್ಞೆಯ ಮೂಲಕ ತೆರೆಯುತ್ತದೆ BPF_MAP_UPDATE_ELEM ಅಂಶವನ್ನು ತಿದ್ದಿ ಬರೆಯುತ್ತದೆ.

ಆದ್ದರಿಂದ, ಒಂದು ಪ್ರೋಗ್ರಾಂನಿಂದ ಹ್ಯಾಶ್ ಟೇಬಲ್ ಅನ್ನು ರಚಿಸಿದ ನಂತರ, ನಾವು ಅದರ ವಿಷಯಗಳನ್ನು ಇನ್ನೊಂದರಿಂದ ಓದಬಹುದು ಮತ್ತು ಬರೆಯಬಹುದು. ನಾವು ಇದನ್ನು ಕಮಾಂಡ್ ಲೈನ್‌ನಿಂದ ಮಾಡಲು ಸಾಧ್ಯವಾದರೆ, ಸಿಸ್ಟಮ್‌ನಲ್ಲಿರುವ ಯಾವುದೇ ಇತರ ಪ್ರೋಗ್ರಾಂ ಕೂಡ ಇದನ್ನು ಮಾಡಬಹುದು ಎಂಬುದನ್ನು ಗಮನಿಸಿ. ಮೇಲೆ ವಿವರಿಸಿದ ಆಜ್ಞೆಗಳ ಜೊತೆಗೆ, ಬಳಕೆದಾರ ಸ್ಥಳದಿಂದ ನಕ್ಷೆಗಳೊಂದಿಗೆ ಕೆಲಸ ಮಾಡಲು ಈ ಕೆಳಗಿನವುಗಳು ಲಭ್ಯವಿದೆ: следующие:

  • BPF_MAP_LOOKUP_ELEM: ಕೀಲಿಯಿಂದ ಮೌಲ್ಯವನ್ನು ಹುಡುಕಿ
  • BPF_MAP_UPDATE_ELEM: ಮೌಲ್ಯವನ್ನು ನವೀಕರಿಸಿ/ರಚಿಸಿ
  • BPF_MAP_DELETE_ELEM: ಅಳಿಸು ಕೀಲಿ
  • BPF_MAP_GET_NEXT_KEY: ಮುಂದಿನ (ಅಥವಾ ಮೊದಲ) ಕೀಲಿಯನ್ನು ಹುಡುಕಿ
  • BPF_MAP_GET_NEXT_ID: ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಎಲ್ಲಾ ನಕ್ಷೆಗಳ ಮೂಲಕ ಹೋಗಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ, ಇದು ಹೇಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ ಎಂಬುದು ಇಲ್ಲಿದೆ bpftool map
  • BPF_MAP_GET_FD_BY_ID: ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ನಕ್ಷೆಯನ್ನು ಅದರ ಜಾಗತಿಕ ID ಯಿಂದ ತೆರೆಯಿರಿ
  • BPF_MAP_LOOKUP_AND_DELETE_ELEM: ವಸ್ತುವಿನ ಮೌಲ್ಯವನ್ನು ಪರಮಾಣುವಾಗಿ ನವೀಕರಿಸಿ ಮತ್ತು ಹಳೆಯದನ್ನು ಹಿಂತಿರುಗಿಸಿ
  • BPF_MAP_FREEZE: ಬಳಕೆದಾರ ಸ್ಥಳದಿಂದ ನಕ್ಷೆಯನ್ನು ಬದಲಾಯಿಸಲಾಗದಂತೆ ಮಾಡಿ (ಈ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಗೊಳಿಸಲಾಗುವುದಿಲ್ಲ)
  • BPF_MAP_LOOKUP_BATCH, BPF_MAP_LOOKUP_AND_DELETE_BATCH, BPF_MAP_UPDATE_BATCH, BPF_MAP_DELETE_BATCH: ಬೃಹತ್ ಕಾರ್ಯಾಚರಣೆಗಳು. ಉದಾಹರಣೆಗೆ, BPF_MAP_LOOKUP_AND_DELETE_BATCH - ನಕ್ಷೆಯಿಂದ ಎಲ್ಲಾ ಮೌಲ್ಯಗಳನ್ನು ಓದಲು ಮತ್ತು ಮರುಹೊಂದಿಸಲು ಇದು ಏಕೈಕ ವಿಶ್ವಾಸಾರ್ಹ ಮಾರ್ಗವಾಗಿದೆ.

ಈ ಎಲ್ಲಾ ಆಜ್ಞೆಗಳು ಎಲ್ಲಾ ನಕ್ಷೆ ಪ್ರಕಾರಗಳಿಗೆ ಕೆಲಸ ಮಾಡುವುದಿಲ್ಲ, ಆದರೆ ಸಾಮಾನ್ಯವಾಗಿ ಬಳಕೆದಾರ ಸ್ಥಳದಿಂದ ಇತರ ನಕ್ಷೆ ಪ್ರಕಾರಗಳೊಂದಿಗೆ ಕೆಲಸ ಮಾಡುವುದು ಹ್ಯಾಶ್ ಕೋಷ್ಟಕಗಳೊಂದಿಗೆ ಕೆಲಸ ಮಾಡುವಂತೆಯೇ ಕಾಣುತ್ತದೆ.

ಕ್ರಮಬದ್ಧತೆಗಾಗಿ, ನಮ್ಮ ಪ್ರಯೋಗಗಳನ್ನು ಹ್ಯಾಶ್ ಟೇಬಲ್‌ನೊಂದಿಗೆ ಮುಗಿಸೋಣ. ನಾವು ನಾಲ್ಕು ಕೀಲಿಗಳನ್ನು ಹೊಂದಿರಬಹುದಾದ ಟೇಬಲ್ ಅನ್ನು ರಚಿಸಿದ್ದೇವೆ ಎಂದು ನೆನಪಿದೆಯೇ? ಇನ್ನೂ ಕೆಲವು ಅಂಶಗಳನ್ನು ಸೇರಿಸೋಣ:

$ sudo bpftool map update id 114 key 2 0 0 0 value 1 0 0 0
$ sudo bpftool map update id 114 key 3 0 0 0 value 1 0 0 0
$ sudo bpftool map update id 114 key 4 0 0 0 value 1 0 0 0

ಇಲ್ಲಿಯವರೆಗೆ ಎಲ್ಲವೂ ಸರಿಯಾಗಿದೆ:

$ sudo bpftool map dump id 114
key: 01 00 00 00  value: 01 00 00 00
key: 02 00 00 00  value: 01 00 00 00
key: 04 00 00 00  value: 01 00 00 00
key: 03 00 00 00  value: 01 00 00 00
Found 4 elements

ಇನ್ನೊಂದನ್ನು ಸೇರಿಸಲು ಪ್ರಯತ್ನಿಸೋಣ:

$ sudo bpftool map update id 114 key 5 0 0 0 value 1 0 0 0
Error: update failed: Argument list too long

ನಿರೀಕ್ಷೆಯಂತೆ, ನಾವು ಯಶಸ್ವಿಯಾಗಲಿಲ್ಲ. ದೋಷವನ್ನು ಹೆಚ್ಚು ವಿವರವಾಗಿ ನೋಡೋಣ:

$ sudo strace -e bpf bpftool map update id 114 key 5 0 0 0 value 1 0 0 0
bpf(BPF_MAP_GET_FD_BY_ID, {map_id=114, next_id=0, open_flags=0}, 120) = 3
bpf(BPF_OBJ_GET_INFO_BY_FD, {info={bpf_fd=3, info_len=80, info=0x7ffe6c626da0}}, 120) = 0
bpf(BPF_MAP_UPDATE_ELEM, {map_fd=3, key=0x56049ded5260, value=0x56049ded5280, flags=BPF_ANY}, 120) = -1 E2BIG (Argument list too long)
Error: update failed: Argument list too long
+++ exited with 255 +++

ಎಲ್ಲವೂ ಚೆನ್ನಾಗಿದೆ: ನಿರೀಕ್ಷೆಯಂತೆ, ತಂಡ BPF_MAP_UPDATE_ELEM ಹೊಸ, ಐದನೇ, ಕೀಲಿಯನ್ನು ರಚಿಸಲು ಪ್ರಯತ್ನಿಸುತ್ತದೆ, ಆದರೆ ಕ್ರ್ಯಾಶ್ ಆಗುತ್ತದೆ E2BIG.

ಹಾಗಾಗಿ, ನಾವು BPF ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ರಚಿಸಬಹುದು ಮತ್ತು ಲೋಡ್ ಮಾಡಬಹುದು ಮತ್ತು ಬಳಕೆದಾರ ಸ್ಥಳದಿಂದ ನಕ್ಷೆಗಳನ್ನು ರಚಿಸಬಹುದು ಮತ್ತು ನಿರ್ವಹಿಸಬಹುದು. ಈಗ BPF ಪ್ರೋಗ್ರಾಂಗಳಿಂದಲೇ ನಕ್ಷೆಗಳನ್ನು ಹೇಗೆ ಬಳಸಬಹುದು ಎಂಬುದನ್ನು ನೋಡುವುದು ತಾರ್ಕಿಕವಾಗಿದೆ. ನಾವು ಇದರ ಬಗ್ಗೆ ಓದಲು ಕಷ್ಟವಾಗುವ ಯಂತ್ರ-ಕೋಡ್ ಮ್ಯಾಕ್ರೋ ಪ್ರೋಗ್ರಾಂಗಳ ಭಾಷೆಯಲ್ಲಿ ಮಾತನಾಡಬಹುದು, ಆದರೆ ವಾಸ್ತವವಾಗಿ BPF ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ವಾಸ್ತವವಾಗಿ ಹೇಗೆ ಬರೆಯಲಾಗುತ್ತದೆ ಮತ್ತು ನಿರ್ವಹಿಸಲಾಗುತ್ತದೆ ಎಂಬುದನ್ನು ತೋರಿಸಲು ಇದು ಸಮಯ - libbpf.

(ಕೆಳಮಟ್ಟದ ಉದಾಹರಣೆಯ ಕೊರತೆಯಿಂದ ಅತೃಪ್ತರಾಗಿರುವ ಓದುಗರಿಗಾಗಿ, ನಕ್ಷೆಗಳನ್ನು ಬಳಸುವ ಕಾರ್ಯಕ್ರಮಗಳು ಮತ್ತು ರಚಿಸಲಾದ ಸಹಾಯಕ ಕಾರ್ಯಗಳನ್ನು ನಾವು ವಿವರವಾಗಿ ಪರಿಶೀಲಿಸುತ್ತೇವೆ) libbpf ಮತ್ತು ಸೂಚನಾ ಮಟ್ಟದಲ್ಲಿ ಏನು ನಡೆಯುತ್ತಿದೆ ಎಂಬುದನ್ನು ನಾವು ನಿಮಗೆ ತಿಳಿಸುತ್ತೇವೆ. ಅತೃಪ್ತ ಓದುಗರಿಗಾಗಿ ತುಂಬಾ, ನಾವು ಸೇರಿಸಿದ್ದೇವೆ ಉದಾಹರಣೆ ಲೇಖನದಲ್ಲಿ ಸೂಕ್ತ ಸ್ಥಳದಲ್ಲಿ.)

libbpf ನೊಂದಿಗೆ BPF ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಬರೆಯುವುದು

ಯಂತ್ರ ಸಂಕೇತಗಳನ್ನು ಬಳಸಿಕೊಂಡು BPF ಕಾರ್ಯಕ್ರಮಗಳನ್ನು ಬರೆಯುವುದು ಮೊದಲಿಗೆ ಮಾತ್ರ ಆಸಕ್ತಿದಾಯಕವಾಗಿರುತ್ತದೆ ಮತ್ತು ನಂತರ ತೃಪ್ತಿ ಬರುತ್ತದೆ. ಈ ಹಂತದಲ್ಲಿ, ನೀವು ನಿಮ್ಮ ಗಮನವನ್ನು ತಿರುಗಿಸಬೇಕಾಗಿದೆ llvm, ಇದು BPF ಆರ್ಕಿಟೆಕ್ಚರ್‌ಗಾಗಿ ಕೋಡ್ ಅನ್ನು ರಚಿಸಲು ಬ್ಯಾಕೆಂಡ್ ಅನ್ನು ಹೊಂದಿದೆ, ಜೊತೆಗೆ ಲೈಬ್ರರಿಯನ್ನು ಸಹ ಹೊಂದಿದೆ. libbpf, ಇದು ನಿಮಗೆ BPF ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಬಳಕೆದಾರ ಭಾಗವನ್ನು ಬರೆಯಲು ಮತ್ತು ರಚಿಸಲಾದ BPF ಪ್ರೋಗ್ರಾಂಗಳ ಕೋಡ್ ಅನ್ನು ಲೋಡ್ ಮಾಡಲು ಅನುಮತಿಸುತ್ತದೆ llvm/clang.

ವಾಸ್ತವವಾಗಿ, ನಾವು ಈ ಮತ್ತು ನಂತರದ ಲೇಖನಗಳಲ್ಲಿ ನೋಡಲಿರುವಂತೆ, libbpf ಅದು ಇಲ್ಲದೆ (ಅಥವಾ ಅಂತಹುದೇ ಪರಿಕರಗಳಿಲ್ಲದೆ) ಸಾಕಷ್ಟು ಕೆಲಸ ಮಾಡುತ್ತದೆ - iproute2, libbcc, libbpf-go, ಇತ್ಯಾದಿ) ಬದುಕುವುದು ಅಸಾಧ್ಯ. ಯೋಜನೆಯ ಮಾರಕ ವೈಶಿಷ್ಟ್ಯಗಳಲ್ಲಿ ಒಂದು libbpf BPF CO-RE (ಒಮ್ಮೆ ಕಂಪೈಲ್ ಮಾಡಿ, ಎಲ್ಲೆಡೆ ರನ್ ಮಾಡಿ) — ​​ಒಂದು ಕರ್ನಲ್‌ನಿಂದ ಇನ್ನೊಂದಕ್ಕೆ ಪೋರ್ಟಬಲ್ ಆಗಿರುವ BPF ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಬರೆಯಲು ನಿಮಗೆ ಅನುಮತಿಸುವ ಒಂದು ಯೋಜನೆಯಾಗಿದ್ದು, ವಿಭಿನ್ನ API ಗಳಲ್ಲಿ ಕಾರ್ಯನಿರ್ವಹಿಸುವ ಸಾಮರ್ಥ್ಯದೊಂದಿಗೆ (ಉದಾಹರಣೆಗೆ, ಕರ್ನಲ್ ರಚನೆಯು ಆವೃತ್ತಿಯಿಂದ ಆವೃತ್ತಿಗೆ ಬದಲಾದಾಗ). CO-RE ನೊಂದಿಗೆ ಕೆಲಸ ಮಾಡಲು ಸಾಧ್ಯವಾಗುವಂತೆ, ನಿಮ್ಮ ಕರ್ನಲ್ ಅನ್ನು BTF ಬೆಂಬಲದೊಂದಿಗೆ ಸಂಕಲಿಸಬೇಕು (ಇದನ್ನು ಹೇಗೆ ಮಾಡಬೇಕೆಂದು ನಾವು ವಿಭಾಗದಲ್ಲಿ ನಿಮಗೆ ಹೇಳುತ್ತೇವೆ ಅಭಿವೃದ್ಧಿ ಪರಿಕರಗಳು. ನಿಮ್ಮ ಕರ್ನಲ್ ಅನ್ನು BTF ನೊಂದಿಗೆ ಸಂಕಲಿಸಲಾಗಿದೆಯೇ ಅಥವಾ ಇಲ್ಲವೇ ಎಂಬುದನ್ನು ನೀವು ತುಂಬಾ ಸರಳವಾಗಿ ಪರಿಶೀಲಿಸಬಹುದು - ಈ ಕೆಳಗಿನ ಫೈಲ್ ಇರುವಿಕೆಯ ಮೂಲಕ:

$ ls -lh /sys/kernel/btf/vmlinux
-r--r--r-- 1 root root 2.6M Jul 29 15:30 /sys/kernel/btf/vmlinux

ಈ ಫೈಲ್ ಕರ್ನಲ್‌ನಲ್ಲಿ ಬಳಸಲಾದ ಎಲ್ಲಾ ರೀತಿಯ ಡೇಟಾದ ಬಗ್ಗೆ ಮಾಹಿತಿಯನ್ನು ಸಂಗ್ರಹಿಸುತ್ತದೆ ಮತ್ತು ನಮ್ಮ ಎಲ್ಲಾ ಉದಾಹರಣೆಗಳಲ್ಲಿ ಇದನ್ನು ಬಳಸಲಾಗುತ್ತದೆ libbpf. ಮುಂದಿನ ಲೇಖನದಲ್ಲಿ ನಾವು CO-RE ಬಗ್ಗೆ ವಿವರವಾಗಿ ಮಾತನಾಡುತ್ತೇವೆ, ಆದರೆ ಇದರಲ್ಲಿ - ನಿಮಗಾಗಿ ಒಂದು ಕೋರ್ ಅನ್ನು ನಿರ್ಮಿಸಿ CONFIG_DEBUG_INFO_BTF.

ಗ್ರಂಥಾಲಯದ libbpf ಡೈರೆಕ್ಟರಿಯಲ್ಲಿಯೇ ವಾಸಿಸುತ್ತದೆ tools/lib/bpf ಕರ್ನಲ್ ಮತ್ತು ಅದರ ಅಭಿವೃದ್ಧಿಯನ್ನು ಮೇಲಿಂಗ್ ಪಟ್ಟಿಯ ಮೂಲಕ ನಡೆಸಲಾಗುತ್ತದೆ. bpf@vger.kernel.orgಆದಾಗ್ಯೂ, ಕರ್ನಲ್‌ನ ಹೊರಗೆ ವಾಸಿಸುವ ಅನ್ವಯಿಕೆಗಳ ಅಗತ್ಯಗಳಿಗಾಗಿ, ಪ್ರತ್ಯೇಕ ಭಂಡಾರವನ್ನು ನಿರ್ವಹಿಸಲಾಗುತ್ತದೆ. https://github.com/libbpf/libbpf ಇದರಲ್ಲಿ ಪರಮಾಣು ಗ್ರಂಥಾಲಯವು ಹೆಚ್ಚು ಕಡಿಮೆ ಇರುವಂತೆಯೇ ಓದಲು ಪ್ರವೇಶಕ್ಕಾಗಿ ಪ್ರತಿಬಿಂಬಿಸಲ್ಪಟ್ಟಿದೆ.

ಈ ವಿಭಾಗದಲ್ಲಿ ನಾವು ಬಳಸುವ ಯೋಜನೆಯನ್ನು ಹೇಗೆ ರಚಿಸಬಹುದು ಎಂಬುದನ್ನು ನೋಡೋಣ libbpf, ನಾವು ಕೆಲವು (ಹೆಚ್ಚು ಕಡಿಮೆ ಅರ್ಥಹೀನ) ಪರೀಕ್ಷಾ ಕಾರ್ಯಕ್ರಮಗಳನ್ನು ಬರೆಯುತ್ತೇವೆ ಮತ್ತು ಅದು ಹೇಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ ಎಂಬುದರ ಕುರಿತು ವಿವರವಾಗಿ ಹೋಗುತ್ತೇವೆ. ಇದು ಮುಂದಿನ ವಿಭಾಗಗಳಲ್ಲಿ BPF ಕಾರ್ಯಕ್ರಮಗಳು ನಕ್ಷೆಗಳು, ಕರ್ನಲ್ ಸಹಾಯಕರು, BTF, ಇತ್ಯಾದಿಗಳೊಂದಿಗೆ ಹೇಗೆ ನಿಖರವಾಗಿ ಸಂವಹನ ನಡೆಸುತ್ತವೆ ಎಂಬುದನ್ನು ಹೆಚ್ಚು ಸುಲಭವಾಗಿ ವಿವರಿಸಲು ನಮಗೆ ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ.

ಸಾಮಾನ್ಯವಾಗಿ ಬಳಸುವ ಯೋಜನೆಗಳು libbpf ಗಿಟ್‌ಹಬ್ ರೆಪೊಸಿಟರಿಯನ್ನು ಗಿಟ್ ಸಬ್‌ ಮಾಡ್ಯೂಲ್ ಆಗಿ ಸೇರಿಸಿ, ನಾವು ಅದೇ ರೀತಿ ಮಾಡುತ್ತೇವೆ:

$ mkdir /tmp/libbpf-example
$ cd /tmp/libbpf-example/
$ git init-db
Initialized empty Git repository in /tmp/libbpf-example/.git/
$ git submodule add https://github.com/libbpf/libbpf.git
Cloning into '/tmp/libbpf-example/libbpf'...
remote: Enumerating objects: 200, done.
remote: Counting objects: 100% (200/200), done.
remote: Compressing objects: 100% (103/103), done.
remote: Total 3354 (delta 101), reused 118 (delta 79), pack-reused 3154
Receiving objects: 100% (3354/3354), 2.05 MiB | 10.22 MiB/s, done.
Resolving deltas: 100% (2176/2176), done.

ಇದನ್ನು ಜೋಡಿಸಲಾಗುತ್ತಿದೆ. libbpf ತುಂಬಾ ಸರಳ:

$ cd libbpf/src
$ mkdir build
$ OBJDIR=build DESTDIR=root make -s install
$ find root
root
root/usr
root/usr/include
root/usr/include/bpf
root/usr/include/bpf/bpf_tracing.h
root/usr/include/bpf/xsk.h
root/usr/include/bpf/libbpf_common.h
root/usr/include/bpf/bpf_endian.h
root/usr/include/bpf/bpf_helpers.h
root/usr/include/bpf/btf.h
root/usr/include/bpf/bpf_helper_defs.h
root/usr/include/bpf/bpf.h
root/usr/include/bpf/libbpf_util.h
root/usr/include/bpf/libbpf.h
root/usr/include/bpf/bpf_core_read.h
root/usr/lib64
root/usr/lib64/libbpf.so.0.1.0
root/usr/lib64/libbpf.so.0
root/usr/lib64/libbpf.a
root/usr/lib64/libbpf.so
root/usr/lib64/pkgconfig
root/usr/lib64/pkgconfig/libbpf.pc

ಈ ವಿಭಾಗದಲ್ಲಿ ನಮ್ಮ ಮುಂದಿನ ಯೋಜನೆ ಹೀಗಿದೆ: ನಾವು ಈ ರೀತಿಯ ಬಿಪಿಎಫ್ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಬರೆಯುತ್ತೇವೆ BPF_PROG_TYPE_XDP, ಹಿಂದಿನ ಉದಾಹರಣೆಯಂತೆಯೇ, ಆದರೆ C ನಲ್ಲಿ, ನಾವು ಅದನ್ನು ಬಳಸಿ ಕಂಪೈಲ್ ಮಾಡುತ್ತೇವೆ clang, ಮತ್ತು ಕರ್ನಲ್‌ಗೆ ಲೋಡ್ ಮಾಡುವ ಸಹಾಯಕ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಬರೆಯಿರಿ. ಮುಂದಿನ ವಿಭಾಗಗಳಲ್ಲಿ, ನಾವು BPF ಪ್ರೋಗ್ರಾಂ ಮತ್ತು ಸಹಾಯಕ ಪ್ರೋಗ್ರಾಂ ಎರಡರ ಸಾಮರ್ಥ್ಯಗಳನ್ನು ವಿಸ್ತರಿಸುತ್ತೇವೆ.

ಉದಾಹರಣೆ: libbpf ಬಳಸಿ ಪೂರ್ಣ ಪ್ರಮಾಣದ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ರಚಿಸುವುದು.

ಪ್ರಾರಂಭಿಸಲು, ನಾವು ಫೈಲ್ ಅನ್ನು ಬಳಸುತ್ತೇವೆ /sys/kernel/btf/vmlinux, ಇದನ್ನು ಮೇಲೆ ಉಲ್ಲೇಖಿಸಲಾಗಿದೆ ಮತ್ತು ಅದರ ಸಮಾನತೆಯನ್ನು ಹೆಡರ್ ಫೈಲ್ ಆಗಿ ರಚಿಸಿ:

$ bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h

ಈ ಫೈಲ್ ನಮ್ಮ ಕರ್ನಲ್‌ನಲ್ಲಿರುವ ಎಲ್ಲಾ ಡೇಟಾ ರಚನೆಗಳನ್ನು ಸಂಗ್ರಹಿಸುತ್ತದೆ, ಉದಾಹರಣೆಗೆ, ಕರ್ನಲ್‌ನಲ್ಲಿ IPv4 ಹೆಡರ್ ಅನ್ನು ಈ ರೀತಿ ವ್ಯಾಖ್ಯಾನಿಸಲಾಗಿದೆ:

$ grep -A 12 'struct iphdr {' vmlinux.h
struct iphdr {
    __u8 ihl: 4;
    __u8 version: 4;
    __u8 tos;
    __be16 tot_len;
    __be16 id;
    __be16 frag_off;
    __u8 ttl;
    __u8 protocol;
    __sum16 check;
    __be32 saddr;
    __be32 daddr;
};

ಈಗ ನಾವು ನಮ್ಮ BPF ಪ್ರೋಗ್ರಾಂ ಅನ್ನು C ನಲ್ಲಿ ಬರೆಯೋಣ:

$ cat xdp-simple.bpf.c
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>

SEC("xdp/simple")
int simple(void *ctx)
{
        return XDP_PASS;
}

char LICENSE[] SEC("license") = "GPL";

ನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ತುಂಬಾ ಸರಳವಾಗಿದ್ದರೂ, ನಾವು ಇನ್ನೂ ಹಲವು ವಿವರಗಳಿಗೆ ಗಮನ ಕೊಡಬೇಕಾಗಿದೆ. ಮೊದಲನೆಯದಾಗಿ, ನಾವು ಸೇರಿಸುವ ಮೊದಲ ಹೆಡರ್ ಫೈಲ್ vmlinux.h, ಇದನ್ನು ನಾವು ಬಳಸಿ ರಚಿಸಿದ್ದೇವೆ bpftool btf dump — ಈಗ ಕರ್ನಲ್ ರಚನೆಗಳು ಹೇಗೆ ಕಾಣುತ್ತವೆ ಎಂಬುದನ್ನು ಕಂಡುಹಿಡಿಯಲು ನಾವು ಕರ್ನಲ್-ಹೆಡರ್ ಪ್ಯಾಕೇಜ್ ಅನ್ನು ಸ್ಥಾಪಿಸುವ ಅಗತ್ಯವಿಲ್ಲ. ಮುಂದಿನ ಹೆಡರ್ ಫೈಲ್ ಲೈಬ್ರರಿಯಿಂದ ನಮಗೆ ಬರುತ್ತದೆ. libbpfಈಗ ನಮಗೆ ಅದು ಮ್ಯಾಕ್ರೋವನ್ನು ವ್ಯಾಖ್ಯಾನಿಸಲು ಮಾತ್ರ ಬೇಕಾಗುತ್ತದೆ. SEC, ಇದು ಚಿಹ್ನೆಯನ್ನು ELF ಆಬ್ಜೆಕ್ಟ್ ಫೈಲ್‌ನ ಸೂಕ್ತ ವಿಭಾಗಕ್ಕೆ ಕಳುಹಿಸುತ್ತದೆ. ನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ವಿಭಾಗದಲ್ಲಿದೆ xdp/simple, ಇಲ್ಲಿ ಸ್ಲ್ಯಾಷ್‌ಗೆ ಮೊದಲು ನಾವು ಪ್ರೋಗ್ರಾಂ ಪ್ರಕಾರವನ್ನು ವ್ಯಾಖ್ಯಾನಿಸುತ್ತೇವೆ BPF ಎನ್ನುವುದು ಒಂದು ಸಂಪ್ರದಾಯವಾಗಿದೆ, ಇದನ್ನು libbpf, ವಿಭಾಗದ ಹೆಸರಿನ ಆಧಾರದ ಮೇಲೆ ಅದು ಚಾಲನೆಯಲ್ಲಿರುವಾಗ ಸರಿಯಾದ ಪ್ರಕಾರವನ್ನು ಬದಲಿಸುತ್ತದೆ bpf(2). ಬಿಪಿಎಫ್ ಕಾರ್ಯಕ್ರಮವು ಸ್ವತಃ C - ತುಂಬಾ ಸರಳ ಮತ್ತು ಒಂದು ಸಾಲನ್ನು ಒಳಗೊಂಡಿದೆ. return XDP_PASS. ಅಂತಿಮವಾಗಿ, ಒಂದು ಪ್ರತ್ಯೇಕ ವಿಭಾಗ "license" ಪರವಾನಗಿಯ ಹೆಸರನ್ನು ಒಳಗೊಂಡಿದೆ.

ನಾವು ನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು llvm/clang, version >= 10.0.0, ಅಥವಾ ಇನ್ನೂ ಉತ್ತಮ, ಹೆಚ್ಚಿನದನ್ನು ಬಳಸಿಕೊಂಡು ಕಂಪೈಲ್ ಮಾಡಬಹುದು (ವಿಭಾಗವನ್ನು ನೋಡಿ ಅಭಿವೃದ್ಧಿ ಪರಿಕರಗಳು):

$ clang --version
clang version 11.0.0 (https://github.com/llvm/llvm-project.git afc287e0abec710398465ee1f86237513f2b5091)
...

$ clang -O2 -g -c -target bpf -I libbpf/src/root/usr/include xdp-simple.bpf.c -o xdp-simple.bpf.o

ಒಂದು ಆಸಕ್ತಿದಾಯಕ ವೈಶಿಷ್ಟ್ಯ: ನಾವು ಗುರಿ ವಾಸ್ತುಶಿಲ್ಪವನ್ನು ನಿರ್ದಿಷ್ಟಪಡಿಸುತ್ತೇವೆ -target bpf ಮತ್ತು ಶೀರ್ಷಿಕೆಗಳಿಗೆ ಮಾರ್ಗ libbpf, ನಾವು ಇತ್ತೀಚೆಗೆ ಸ್ಥಾಪಿಸಿದ್ದೇವೆ. ಅಲ್ಲದೆ, ಅದರ ಬಗ್ಗೆ ಮರೆಯಬೇಡಿ -O2, ಈ ಆಯ್ಕೆಯಿಲ್ಲದೆ ನೀವು ಭವಿಷ್ಯದಲ್ಲಿ ಆಶ್ಚರ್ಯಗಳನ್ನು ನಿರೀಕ್ಷಿಸಬಹುದು. ನಮ್ಮ ಕೋಡ್ ಅನ್ನು ನೋಡೋಣ, ನಮಗೆ ಬೇಕಾದ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಬರೆಯಲು ನಮಗೆ ಸಾಧ್ಯವಾಯಿತು?

$ llvm-objdump --section=xdp/simple --no-show-raw-insn -D xdp-simple.bpf.o

xdp-simple.bpf.o:       file format elf64-bpf

Disassembly of section xdp/simple:

0000000000000000 <simple>:
       0:       r0 = 2
       1:       exit

ಹೌದು, ಅದು ಕೆಲಸ ಮಾಡಿದೆ! ಈಗ, ನಾವು ಪ್ರೋಗ್ರಾಂನೊಂದಿಗೆ ಬೈನರಿ ಫೈಲ್ ಅನ್ನು ಹೊಂದಿದ್ದೇವೆ ಮತ್ತು ಅದನ್ನು ಕರ್ನಲ್‌ಗೆ ಲೋಡ್ ಮಾಡುವ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ರಚಿಸಲು ನಾವು ಬಯಸುತ್ತೇವೆ. ಇದಕ್ಕಾಗಿ, ಲೈಬ್ರರಿ libbpf ನಮಗೆ ಎರಡು ಆಯ್ಕೆಗಳನ್ನು ನೀಡುತ್ತದೆ - ಕೆಳ ಹಂತದ API ಅಥವಾ ಉನ್ನತ ಮಟ್ಟದ API ಅನ್ನು ಬಳಸಲು. ನಾವು ಎರಡನೇ ದಾರಿಯಲ್ಲಿ ಹೋಗುತ್ತೇವೆ, ಏಕೆಂದರೆ ನಾವು BPF ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಅವುಗಳ ನಂತರದ ಅಧ್ಯಯನಕ್ಕಾಗಿ ಕನಿಷ್ಠ ಪ್ರಯತ್ನದಿಂದ ಬರೆಯುವುದು, ಲೋಡ್ ಮಾಡುವುದು ಮತ್ತು ಸಂಪರ್ಕಿಸುವುದು ಹೇಗೆ ಎಂಬುದನ್ನು ಕಲಿಯಲು ಬಯಸುತ್ತೇವೆ.

ಮೊದಲು, ನಾವು ಅದೇ ಉಪಯುಕ್ತತೆಯನ್ನು ಬಳಸಿಕೊಂಡು ನಮ್ಮ ಪ್ರೋಗ್ರಾಂನ "ಅಸ್ಥಿಪಂಜರ"ವನ್ನು ಅದರ ಬೈನರಿಯಿಂದ ಉತ್ಪಾದಿಸಬೇಕಾಗಿದೆ. bpftool — ಬಿಪಿಎಫ್ ಪ್ರಪಂಚದ ಸ್ವಿಸ್ ಚಾಕು (ಬಿಪಿಎಫ್‌ನ ಸೃಷ್ಟಿಕರ್ತರು ಮತ್ತು ನಿರ್ವಾಹಕರಲ್ಲಿ ಒಬ್ಬರಾದ ಡೇನಿಯಲ್ ಬೋರ್ಕ್‌ಮನ್ ಸ್ವಿಸ್ ಆಗಿರುವುದರಿಂದ ಇದನ್ನು ಅಕ್ಷರಶಃ ತೆಗೆದುಕೊಳ್ಳಬಹುದು):

$ bpftool gen skeleton xdp-simple.bpf.o > xdp-simple.skel.h

ಕಡತದಲ್ಲಿ xdp-simple.skel.h ನಮ್ಮ ಪ್ರೋಗ್ರಾಂನ ಬೈನರಿ ಕೋಡ್ ಮತ್ತು ನಮ್ಮ ವಸ್ತುವನ್ನು ನಿರ್ವಹಿಸುವ - ಲೋಡ್ ಮಾಡುವುದು, ಲಗತ್ತಿಸುವುದು, ಅಳಿಸುವ ಕಾರ್ಯಗಳನ್ನು ಒಳಗೊಂಡಿದೆ. ನಮ್ಮ ಸರಳ ಸಂದರ್ಭದಲ್ಲಿ, ಇದು ಅತಿಯಾಗಿ ಕಾಣುತ್ತದೆ, ಆದರೆ ಆಬ್ಜೆಕ್ಟ್ ಫೈಲ್ ಅನೇಕ BPF ಪ್ರೋಗ್ರಾಂಗಳು ಮತ್ತು ನಕ್ಷೆಗಳನ್ನು ಹೊಂದಿರುವ ಸಂದರ್ಭದಲ್ಲಿಯೂ ಇದು ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ ಮತ್ತು ಈ ದೈತ್ಯ ELF ಅನ್ನು ಲೋಡ್ ಮಾಡಲು, ನಾವು ಅಸ್ಥಿಪಂಜರವನ್ನು ರಚಿಸಬೇಕು ಮತ್ತು ಬಳಕೆದಾರ ಅಪ್ಲಿಕೇಶನ್‌ನಿಂದ ಒಂದು ಅಥವಾ ಎರಡು ಕಾರ್ಯಗಳನ್ನು ಕರೆಯಬೇಕು, ಅದನ್ನು ನಾವು ಈಗ ಬರೆಯಲು ಮುಂದುವರಿಯುತ್ತೇವೆ.

ವಾಸ್ತವವಾಗಿ, ನಮ್ಮ ಲೋಡರ್ ಪ್ರೋಗ್ರಾಂ ಕ್ಷುಲ್ಲಕವಾಗಿದೆ:

#include <err.h>
#include <unistd.h>
#include "xdp-simple.skel.h"

int main(int argc, char **argv)
{
    struct xdp_simple_bpf *obj;

    obj = xdp_simple_bpf__open_and_load();
    if (!obj)
        err(1, "failed to open and/or load BPF objectn");

    pause();

    xdp_simple_bpf__destroy(obj);
}

ಇದು struct xdp_simple_bpf ಫೈಲ್‌ನಲ್ಲಿ ವ್ಯಾಖ್ಯಾನಿಸಲಾಗಿದೆ xdp-simple.skel.h ಮತ್ತು ನಮ್ಮ ಆಬ್ಜೆಕ್ಟ್ ಫೈಲ್ ಅನ್ನು ವಿವರಿಸುತ್ತದೆ:

struct xdp_simple_bpf {
    struct bpf_object_skeleton *skeleton;
    struct bpf_object *obj;
    struct {
        struct bpf_program *simple;
    } progs;
    struct {
        struct bpf_link *simple;
    } links;
};

ಕೆಳಮಟ್ಟದ API ನ ಕುರುಹುಗಳನ್ನು ನಾವು ಇಲ್ಲಿ ನೋಡಬಹುದು: ರಚನೆ struct bpf_program *simple и struct bpf_link *simpleಮೊದಲ ರಚನೆಯು ವಿಭಾಗದಲ್ಲಿ ಬರೆಯಲಾದ ನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ನಿರ್ದಿಷ್ಟವಾಗಿ ವಿವರಿಸುತ್ತದೆ xdp/simple, ಮತ್ತು ಎರಡನೆಯದು ಪ್ರೋಗ್ರಾಂ ಈವೆಂಟ್ ಮೂಲಕ್ಕೆ ಹೇಗೆ ಸಂಪರ್ಕಿಸುತ್ತದೆ ಎಂಬುದನ್ನು ವಿವರಿಸುತ್ತದೆ.

ಕಾರ್ಯ xdp_simple_bpf__open_and_load, ELF ವಸ್ತುವನ್ನು ತೆರೆಯುತ್ತದೆ, ಅದನ್ನು ಪಾರ್ಸ್ ಮಾಡುತ್ತದೆ, ಎಲ್ಲಾ ರಚನೆಗಳು ಮತ್ತು ಸಬ್‌ಸ್ಟ್ರಕ್ಚರ್‌ಗಳನ್ನು ರಚಿಸುತ್ತದೆ (ಪ್ರೋಗ್ರಾಂ ಜೊತೆಗೆ, ELF ಇತರ ವಿಭಾಗಗಳನ್ನು ಸಹ ಒಳಗೊಂಡಿದೆ - ಡೇಟಾ, ಓದಲು ಮಾತ್ರ ಡೇಟಾ, ಡೀಬಗ್ ಮಾಹಿತಿ, ಪರವಾನಗಿ, ಇತ್ಯಾದಿ), ಮತ್ತು ನಂತರ ಅದನ್ನು ಸಿಸ್ಟಮ್ ಕರೆಯ ಮೂಲಕ ಕರ್ನಲ್‌ಗೆ ಲೋಡ್ ಮಾಡುತ್ತದೆ. bpf, ಇದನ್ನು ನಾವು ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಕಂಪೈಲ್ ಮಾಡಿ ಚಲಾಯಿಸುವ ಮೂಲಕ ಪರಿಶೀಲಿಸಬಹುದು:

$ clang -O2 -I ./libbpf/src/root/usr/include/ xdp-simple.c -o xdp-simple ./libbpf/src/root/usr/lib64/libbpf.a -lelf -lz

$ sudo strace -e bpf ./xdp-simple
...
bpf(BPF_BTF_LOAD, 0x7ffdb8fd9670, 120)  = 3
bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_XDP, insn_cnt=2, insns=0xdfd580, license="GPL", log_level=0, log_size=0, log_buf=NULL, kern_version=KERNEL_VERSION(5, 8, 0), prog_flags=0, prog_name="simple", prog_ifindex=0, expected_attach_type=0x25 /* BPF_??? */, ...}, 120) = 4

ಈಗ ನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಬಳಸಿಕೊಂಡು ನೋಡೋಣ bpftool. ಅದರ ಐಡಿಯನ್ನು ಕಂಡುಹಿಡಿಯೋಣ:

# bpftool p | grep -A4 simple
463: xdp  name simple  tag 3b185187f1855c4c  gpl
        loaded_at 2020-08-01T01:59:49+0000  uid 0
        xlated 16B  jited 40B  memlock 4096B
        btf_id 185
        pids xdp-simple(16498)

ಮತ್ತು ಡಂಪ್ (ನಾವು ಆಜ್ಞೆಯ ಸಂಕ್ಷಿಪ್ತ ರೂಪವನ್ನು ಬಳಸುತ್ತೇವೆ bpftool prog dump xlated):

# bpftool p d x id 463
int simple(void *ctx):
; return XDP_PASS;
   0: (b7) r0 = 2
   1: (95) exit

ಏನೋ ಹೊಸತು! ಪ್ರೋಗ್ರಾಂ ನಮ್ಮ C ಮೂಲ ಫೈಲ್‌ನ ಭಾಗಗಳನ್ನು ಮುದ್ರಿಸಿದೆ. ಇದನ್ನು ಲೈಬ್ರರಿ ಮಾಡಿದೆ. libbpf, ಇದು ಬೈನರಿಯಲ್ಲಿ ಡೀಬಗ್ ವಿಭಾಗವನ್ನು ಕಂಡುಹಿಡಿದು, ಅದನ್ನು BTF ವಸ್ತುವಾಗಿ ಸಂಕಲಿಸಿ, ಅದನ್ನು ಕರ್ನಲ್‌ಗೆ ಲೋಡ್ ಮಾಡಿತು. BPF_BTF_LOAD, ತದನಂತರ ಆಜ್ಞೆಯೊಂದಿಗೆ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಲೋಡ್ ಮಾಡುವಾಗ ಸ್ವೀಕರಿಸಿದ ಫೈಲ್ ವಿವರಣೆಯನ್ನು ನಿರ್ದಿಷ್ಟಪಡಿಸುತ್ತದೆ BPG_PROG_LOAD.

ಕರ್ನಲ್ ಸಹಾಯಕರು

BPF ಪ್ರೋಗ್ರಾಂಗಳು "ಬಾಹ್ಯ" ಕಾರ್ಯಗಳನ್ನು ಚಲಾಯಿಸಬಹುದು - ಕರ್ನಲ್ ಸಹಾಯಕರು. ಈ ಸಹಾಯಕ ಕಾರ್ಯಗಳು BPF ಪ್ರೋಗ್ರಾಂಗಳು ಕರ್ನಲ್ ರಚನೆಗಳನ್ನು ಪ್ರವೇಶಿಸಲು, ನಕ್ಷೆಗಳನ್ನು ನಿರ್ವಹಿಸಲು ಮತ್ತು "ನೈಜ ಪ್ರಪಂಚ" ದೊಂದಿಗೆ ಸಂವಹನ ನಡೆಸಲು ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ - perf ಈವೆಂಟ್‌ಗಳನ್ನು ರಚಿಸಿ, ಹಾರ್ಡ್‌ವೇರ್ ಅನ್ನು ನಿರ್ವಹಿಸಿ (ಉದಾಹರಣೆಗೆ, ಫಾರ್ವರ್ಡ್ ಪ್ಯಾಕೆಟ್‌ಗಳು), ಇತ್ಯಾದಿ.

ಉದಾಹರಣೆ: bpf_get_smp_processor_id

"ಉದಾಹರಣೆಯಿಂದ ಕಲಿಯಿರಿ" ಮಾದರಿಯ ಸಂದರ್ಭದಲ್ಲಿ, ಸಹಾಯಕ ಕಾರ್ಯಗಳಲ್ಲಿ ಒಂದನ್ನು ನೋಡೋಣ, bpf_get_smp_processor_id(), ಖಚಿತ ಕಡತದಲ್ಲಿ kernel/bpf/helpers.c. ಅದು ಕರೆ ಮಾಡಿದ BPF ಪ್ರೋಗ್ರಾಂ ಚಾಲನೆಯಲ್ಲಿರುವ ಪ್ರೊಸೆಸರ್ ಸಂಖ್ಯೆಯನ್ನು ಹಿಂದಿರುಗಿಸುತ್ತದೆ. ಆದರೆ ಅದರ ಶಬ್ದಾರ್ಥದಲ್ಲಿ ನಮಗೆ ಅಷ್ಟೊಂದು ಆಸಕ್ತಿ ಇಲ್ಲ, ಆದರೆ ಅದರ ಅನುಷ್ಠಾನವು ಒಂದು ಸಾಲನ್ನು ತೆಗೆದುಕೊಳ್ಳುತ್ತದೆ ಎಂಬ ಅಂಶದಲ್ಲಿ ನಾವು ಆಸಕ್ತಿ ಹೊಂದಿಲ್ಲ:

BPF_CALL_0(bpf_get_smp_processor_id)
{
    return smp_processor_id();
}

BPF ಸಹಾಯಕ ಕಾರ್ಯಗಳ ವ್ಯಾಖ್ಯಾನಗಳು ಲಿನಕ್ಸ್ ಸಿಸ್ಟಮ್ ಕರೆಗಳ ವ್ಯಾಖ್ಯಾನಗಳಿಗೆ ಹೋಲುತ್ತವೆ. ಉದಾಹರಣೆಗೆ, ಇಲ್ಲಿ ಯಾವುದೇ ವಾದಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳದ ಕಾರ್ಯವನ್ನು ವ್ಯಾಖ್ಯಾನಿಸಲಾಗಿದೆ. (ಮೂರು ವಾದಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳುವ ಕಾರ್ಯವನ್ನು ಮ್ಯಾಕ್ರೋ ಬಳಸಿ ವ್ಯಾಖ್ಯಾನಿಸಲಾಗಿದೆ BPF_CALL_3. ಗರಿಷ್ಠ ಸಂಖ್ಯೆಯ ವಾದಗಳು ಐದು.) ಆದಾಗ್ಯೂ, ಇದು ವ್ಯಾಖ್ಯಾನದ ಮೊದಲ ಭಾಗ ಮಾತ್ರ. ಎರಡನೇ ಭಾಗವು ಪ್ರಕಾರದ ರಚನೆಯ ವ್ಯಾಖ್ಯಾನವಾಗಿದೆ. struct bpf_func_proto, ಇದು ಪರಿಶೀಲಕರಿಗೆ ಅರ್ಥವಾಗುವಂತಹ ಸಹಾಯಕ ಕಾರ್ಯದ ವಿವರಣೆಯನ್ನು ಒಳಗೊಂಡಿದೆ:

const struct bpf_func_proto bpf_get_smp_processor_id_proto = {
    .func     = bpf_get_smp_processor_id,
    .gpl_only = false,
    .ret_type = RET_INTEGER,
};

ಸಹಾಯಕ ಕಾರ್ಯಗಳನ್ನು ನೋಂದಾಯಿಸಲಾಗುತ್ತಿದೆ

ನಿರ್ದಿಷ್ಟ ಪ್ರಕಾರದ ಬಿಪಿಎಫ್ ಪ್ರೋಗ್ರಾಂಗಳು ಈ ಕಾರ್ಯವನ್ನು ಬಳಸಲು, ಅವರು ಅದನ್ನು ನೋಂದಾಯಿಸಿಕೊಳ್ಳಬೇಕು, ಉದಾಹರಣೆಗೆ ಪ್ರಕಾರಕ್ಕಾಗಿ BPF_PROG_TYPE_XDP ಕರ್ನಲ್‌ನಲ್ಲಿ ಒಂದು ಕಾರ್ಯವನ್ನು ವ್ಯಾಖ್ಯಾನಿಸಲಾಗಿದೆ. xdp_func_proto, ಇದು XDP ಈ ಕಾರ್ಯವನ್ನು ಬೆಂಬಲಿಸುತ್ತದೆಯೇ ಅಥವಾ ಇಲ್ಲವೇ ಎಂಬುದನ್ನು ಸಹಾಯಕ ಕಾರ್ಯದ ID ಯಿಂದ ನಿರ್ಧರಿಸುತ್ತದೆ. ಇದು ಬೆಂಬಲಿಸುತ್ತದೆ:

static const struct bpf_func_proto *
xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
{
    switch (func_id) {
    ...
    case BPF_FUNC_get_smp_processor_id:
        return &bpf_get_smp_processor_id_proto;
    ...
    }
}

ಹೊಸ ಬಿಪಿಎಫ್ ಪ್ರೋಗ್ರಾಂ ಪ್ರಕಾರಗಳನ್ನು ಫೈಲ್‌ನಲ್ಲಿ "ವ್ಯಾಖ್ಯಾನಿಸಲಾಗಿದೆ". include/linux/bpf_types.h ಮ್ಯಾಕ್ರೋ ಬಳಸಿ BPF_PROG_TYPE. ಇವುಗಳನ್ನು ಉಲ್ಲೇಖಗಳಲ್ಲಿ ವ್ಯಾಖ್ಯಾನಿಸಲಾಗಿದೆ ಏಕೆಂದರೆ ಇದು ತಾರ್ಕಿಕ ವ್ಯಾಖ್ಯಾನವಾಗಿದೆ, ಮತ್ತು C ಪದಗಳಲ್ಲಿ, ನಿರ್ದಿಷ್ಟ ರಚನೆಗಳ ಸಂಪೂರ್ಣ ಗುಂಪಿನ ವ್ಯಾಖ್ಯಾನವು ಬೇರೆಡೆ ಸಂಭವಿಸುತ್ತದೆ. ನಿರ್ದಿಷ್ಟವಾಗಿ, ಫೈಲ್‌ನಲ್ಲಿ kernel/bpf/verifier.c ಫೈಲ್‌ನಿಂದ ಎಲ್ಲಾ ವ್ಯಾಖ್ಯಾನಗಳು bpf_types.h ರಚನೆಗಳ ಶ್ರೇಣಿಯನ್ನು ರಚಿಸಲು ಬಳಸಲಾಗುತ್ತದೆ bpf_verifier_ops[]:

static const struct bpf_verifier_ops *const bpf_verifier_ops[] = {
#define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type) 
    [_id] = & _name ## _verifier_ops,
#include <linux/bpf_types.h>
#undef BPF_PROG_TYPE
};

ಅಂದರೆ, ಪ್ರತಿಯೊಂದು ರೀತಿಯ ಬಿಪಿಎಫ್ ಪ್ರೋಗ್ರಾಂಗೆ, ಆ ಪ್ರಕಾರದ ಡೇಟಾ ರಚನೆಗೆ ಪಾಯಿಂಟರ್ ಅನ್ನು ವ್ಯಾಖ್ಯಾನಿಸಲಾಗಿದೆ struct bpf_verifier_ops, ಇದನ್ನು ಮೌಲ್ಯದೊಂದಿಗೆ ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತದೆ _name ## _verifier_ops, ಅಂದರೆ, xdp_verifier_ops ಗೆ xdpರಚನೆ xdp_verifier_ops ನಿರ್ಧರಿಸುತ್ತದೆ ಕಡತದಲ್ಲಿ net/core/filter.c ಕೆಳಗಿನಂತೆ:

const struct bpf_verifier_ops xdp_verifier_ops = {
    .get_func_proto     = xdp_func_proto,
    .is_valid_access    = xdp_is_valid_access,
    .convert_ctx_access = xdp_convert_ctx_access,
    .gen_prologue       = bpf_noop_prologue,
};

ಇಲ್ಲಿ ನಾವು ನಮ್ಮ ಪರಿಚಿತ ಕಾರ್ಯವನ್ನು ನೋಡುತ್ತೇವೆ. xdp_func_proto, ಇದು ಪ್ರತಿ ಬಾರಿ ಕರೆ ಎದುರಾದಾಗ ಪರಿಶೀಲಕದಿಂದ ನಡೆಸಲ್ಪಡುತ್ತದೆ. ಕೆಲವು ರೀತಿಯ ಬಿಪಿಎಫ್ ಪ್ರೋಗ್ರಾಂ ಒಳಗೆ ಕಾರ್ಯಗಳು, ನೋಡಿ verifier.c.

ಒಂದು ಕಾಲ್ಪನಿಕ ಬಿಪಿಎಫ್ ಪ್ರೋಗ್ರಾಂ ಈ ಕಾರ್ಯವನ್ನು ಹೇಗೆ ಬಳಸುತ್ತದೆ ಎಂಬುದನ್ನು ನೋಡೋಣ. bpf_get_smp_processor_idಇದನ್ನು ಮಾಡಲು, ನಾವು ನಮ್ಮ ಹಿಂದಿನ ವಿಭಾಗದಿಂದ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಈ ಕೆಳಗಿನಂತೆ ಪುನಃ ಬರೆಯುತ್ತೇವೆ:

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>

SEC("xdp/simple")
int simple(void *ctx)
{
    if (bpf_get_smp_processor_id() != 0)
        return XDP_DROP;
    return XDP_PASS;
}

char LICENSE[] SEC("license") = "GPL";

ಚಿಹ್ನೆ bpf_get_smp_processor_id ನಿರ್ಧರಿಸುತ್ತದೆ в <bpf/bpf_helper_defs.h> ಗ್ರಂಥಾಲಯಗಳು libbpf ಹೇಗೆ

static u32 (*bpf_get_smp_processor_id)(void) = (void *) 8;

ಅಂದರೆ, bpf_get_smp_processor_id — ಒಂದು ಫಂಕ್ಷನ್ ಪಾಯಿಂಟರ್ ಆಗಿದ್ದು, ಅದರ ಮೌಲ್ಯ 8 ಆಗಿದ್ದು, ಇಲ್ಲಿ 8 ಮೌಲ್ಯವಾಗಿದೆ BPF_FUNC_get_smp_processor_id ಕೌಟುಂಬಿಕತೆ enum bpf_fun_id, ಇದನ್ನು ಫೈಲ್‌ನಲ್ಲಿ ನಮಗಾಗಿ ವ್ಯಾಖ್ಯಾನಿಸಲಾಗಿದೆ vmlinux.h (ಫೈಲ್ bpf_helper_defs.h ಕರ್ನಲ್‌ನಲ್ಲಿ ಇದು ಸ್ಕ್ರಿಪ್ಟ್‌ನಿಂದ ಉತ್ಪತ್ತಿಯಾಗುತ್ತದೆ, ಆದ್ದರಿಂದ "ಮ್ಯಾಜಿಕ್" ಸಂಖ್ಯೆಗಳು ಸರಿ). ಈ ಕಾರ್ಯವು ಯಾವುದೇ ವಾದಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳುವುದಿಲ್ಲ ಮತ್ತು ಪ್ರಕಾರದ ಮೌಲ್ಯವನ್ನು ಹಿಂದಿರುಗಿಸುತ್ತದೆ __u32ನಾವು ಅದನ್ನು ನಮ್ಮ ಪ್ರೋಗ್ರಾಂನಲ್ಲಿ ಚಲಾಯಿಸಿದಾಗ, clang ಸೂಚನೆಯನ್ನು ಉತ್ಪಾದಿಸುತ್ತದೆ BPF_CALL "ಸರಿಯಾದ ರೀತಿಯ". ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಕಂಪೈಲ್ ಮಾಡೋಣ ಮತ್ತು ವಿಭಾಗವನ್ನು ನೋಡೋಣ. xdp/simple:

$ clang -O2 -g -c -target bpf -I libbpf/src/root/usr/include xdp-simple.bpf.c -o xdp-simple.bpf.o
$ llvm-objdump -D --section=xdp/simple xdp-simple.bpf.o

xdp-simple.bpf.o:       file format elf64-bpf

Disassembly of section xdp/simple:

0000000000000000 <simple>:
       0:       85 00 00 00 08 00 00 00 call 8
       1:       bf 01 00 00 00 00 00 00 r1 = r0
       2:       67 01 00 00 20 00 00 00 r1 <<= 32
       3:       77 01 00 00 20 00 00 00 r1 >>= 32
       4:       b7 00 00 00 02 00 00 00 r0 = 2
       5:       15 01 01 00 00 00 00 00 if r1 == 0 goto +1 <LBB0_2>
       6:       b7 00 00 00 01 00 00 00 r0 = 1

0000000000000038 <LBB0_2>:
       7:       95 00 00 00 00 00 00 00 exit

ಮೊದಲ ಸಾಲಿನಲ್ಲಿ ನಾವು ಸೂಚನೆಯನ್ನು ನೋಡುತ್ತೇವೆ call, ನಿಯತಾಂಕ IMM ಅದು 8 ಕ್ಕೆ ಸಮಾನವಾಗಿರುತ್ತದೆ, ಮತ್ತು SRC_REG — ಶೂನ್ಯ. ಪರಿಶೀಲಕವು ಬಳಸುವ ABI ಸಂಪ್ರದಾಯದ ಪ್ರಕಾರ, ಇದು ಸಹಾಯಕ ಕಾರ್ಯ ಸಂಖ್ಯೆ ಎಂಟಕ್ಕೆ ಕರೆಯಾಗಿದೆ. ಇದನ್ನು ಚಲಾಯಿಸಿದ ನಂತರ, ತರ್ಕವು ಸರಳವಾಗಿದೆ. ರಿಜಿಸ್ಟರ್‌ನಿಂದ ಹಿಂತಿರುಗಿಸಲಾದ ಮೌಲ್ಯ r0 ನಕಲಿಸಲಾಗಿದೆ r1 ಮತ್ತು 2,3 ಸಾಲುಗಳಲ್ಲಿ ಅದನ್ನು ಪ್ರಕಾರಕ್ಕೆ ಪರಿವರ್ತಿಸಲಾಗುತ್ತದೆ u32 — ಮೇಲಿನ 32 ಬಿಟ್‌ಗಳನ್ನು ಶೂನ್ಯಗೊಳಿಸಲಾಗಿದೆ. 4,5,6,7 ಸಾಲುಗಳಲ್ಲಿ ನಾವು 2 ಅನ್ನು ಹಿಂತಿರುಗಿಸುತ್ತೇವೆ (XDP_PASS) ಅಥವಾ 1 (XDP_DROP) 0 ಸಾಲಿನ ಸಹಾಯಕ ಕಾರ್ಯವು ಶೂನ್ಯ ಅಥವಾ ಶೂನ್ಯವಲ್ಲದ ಮೌಲ್ಯವನ್ನು ಹಿಂತಿರುಗಿಸಿದೆಯೇ ಎಂಬುದರ ಮೇಲೆ ಅವಲಂಬಿತವಾಗಿರುತ್ತದೆ.

ನಮ್ಮನ್ನು ನಾವು ಪರಿಶೀಲಿಸೋಣ: ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡಿ ಮತ್ತು ಔಟ್‌ಪುಟ್ ಅನ್ನು ನೋಡೋಣ. bpftool prog dump xlated:

$ bpftool gen skeleton xdp-simple.bpf.o > xdp-simple.skel.h
$ clang -O2 -g -I ./libbpf/src/root/usr/include/ -o xdp-simple xdp-simple.c ./libbpf/src/root/usr/lib64/libbpf.a -lelf -lz
$ sudo ./xdp-simple &
[2] 10914

$ sudo bpftool p | grep simple
523: xdp  name simple  tag 44c38a10c657e1b0  gpl
        pids xdp-simple(10915)

$ sudo bpftool p d x id 523
int simple(void *ctx):
; if (bpf_get_smp_processor_id() != 0)
   0: (85) call bpf_get_smp_processor_id#114128
   1: (bf) r1 = r0
   2: (67) r1 <<= 32
   3: (77) r1 >>= 32
   4: (b7) r0 = 2
; }
   5: (15) if r1 == 0x0 goto pc+1
   6: (b7) r0 = 1
   7: (95) exit

ಸರಿ, ಪರಿಶೀಲಕವು ಸರಿಯಾದ ಕರ್ನಲ್-ಸಹಾಯಕವನ್ನು ಕಂಡುಕೊಂಡಿದೆ.

ಉದಾಹರಣೆ: ವಾದಗಳನ್ನು ರವಾನಿಸುವುದು ಮತ್ತು ಅಂತಿಮವಾಗಿ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಚಲಾಯಿಸುವುದು!

ಎಲ್ಲಾ ರನ್‌ಟೈಮ್ ಸಹಾಯಕ ಕಾರ್ಯಗಳು ಮೂಲಮಾದರಿಯನ್ನು ಹೊಂದಿರುತ್ತವೆ.

u64 fn(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)

ಸಹಾಯಕ ಕಾರ್ಯಗಳಿಗೆ ನಿಯತಾಂಕಗಳನ್ನು ರಿಜಿಸ್ಟರ್‌ಗಳಲ್ಲಿ ರವಾನಿಸಲಾಗುತ್ತದೆ. r1-r5, ಮತ್ತು ಮೌಲ್ಯವನ್ನು ರಿಜಿಸ್ಟರ್‌ನಲ್ಲಿ ಹಿಂತಿರುಗಿಸಲಾಗುತ್ತದೆ r0ಐದು ವಾದಗಳಿಗಿಂತ ಹೆಚ್ಚು ತೆಗೆದುಕೊಳ್ಳುವ ಯಾವುದೇ ಕಾರ್ಯಗಳಿಲ್ಲ, ಮತ್ತು ಭವಿಷ್ಯದಲ್ಲಿ ಅವುಗಳಿಗೆ ಬೆಂಬಲವನ್ನು ಸೇರಿಸುವ ಯಾವುದೇ ಯೋಜನೆಗಳಿಲ್ಲ.

ಹೊಸ ಕರ್ನಲ್ ಸಹಾಯಕ ಮತ್ತು BPF ನಿಯತಾಂಕಗಳನ್ನು ಹೇಗೆ ರವಾನಿಸುತ್ತದೆ ಎಂಬುದನ್ನು ನೋಡೋಣ. ಪುನಃ ಬರೆಯೋಣ. xdp-simple.bpf.c ಈ ಕೆಳಗಿನಂತೆ (ಉಳಿದ ಸಾಲುಗಳು ಬದಲಾಗದೆ ಉಳಿದಿವೆ):

SEC("xdp/simple")
int simple(void *ctx)
{
    bpf_printk("running on CPU%un", bpf_get_smp_processor_id());
    return XDP_PASS;
}

ನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ಅದು ಚಾಲನೆಯಲ್ಲಿರುವ CPU ಸಂಖ್ಯೆಯನ್ನು ಮುದ್ರಿಸುತ್ತದೆ. ಅದನ್ನು ಕಂಪೈಲ್ ಮಾಡಿ ಕೋಡ್ ಅನ್ನು ನೋಡೋಣ:

$ llvm-objdump -D --section=xdp/simple --no-show-raw-insn xdp-simple.bpf.o

0000000000000000 <simple>:
       0:       r1 = 10
       1:       *(u16 *)(r10 - 8) = r1
       2:       r1 = 8441246879787806319 ll
       4:       *(u64 *)(r10 - 16) = r1
       5:       r1 = 2334956330918245746 ll
       7:       *(u64 *)(r10 - 24) = r1
       8:       call 8
       9:       r1 = r10
      10:       r1 += -24
      11:       r2 = 18
      12:       r3 = r0
      13:       call 6
      14:       r0 = 2
      15:       exit

0-7 ಸಾಲುಗಳಲ್ಲಿ ನಾವು ಸ್ಟ್ಯಾಕ್‌ಗೆ ಒಂದು ಸ್ಟ್ರಿಂಗ್ ಬರೆಯುತ್ತೇವೆ. running on CPU%un, ಮತ್ತು ನಂತರ 8 ನೇ ಸಾಲಿನಲ್ಲಿ ನಾವು ಪರಿಚಿತವಾದದನ್ನು ಪ್ರಾರಂಭಿಸುತ್ತೇವೆ bpf_get_smp_processor_id9-12 ಸಾಲುಗಳಲ್ಲಿ ನಾವು ಸಹಾಯಕ ವಾದಗಳನ್ನು ಸಿದ್ಧಪಡಿಸುತ್ತೇವೆ. bpf_printk - ನೋಂದಣಿಗಳು r1, r2, r3. ಮೂರು ಇವೆ, ಎರಡಲ್ಲ ಏಕೆ? ಏಕೆಂದರೆ bpf_printkಇದು ಮ್ಯಾಕ್ರೋ ಹೊದಿಕೆ. ನಿಜವಾದ ಸಹಾಯಕನ ಸುತ್ತ bpf_trace_printk, ಇದು ಫಾರ್ಮ್ಯಾಟ್ ಸ್ಟ್ರಿಂಗ್‌ನ ಗಾತ್ರವನ್ನು ರವಾನಿಸುವ ಅಗತ್ಯವಿದೆ.

ಈಗ ಒಂದೆರಡು ಸಾಲುಗಳನ್ನು ಸೇರಿಸೋಣ xdp-simple.cಆದ್ದರಿಂದ ನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ಇಂಟರ್ಫೇಸ್‌ಗೆ ಸಂಪರ್ಕಗೊಳ್ಳುತ್ತದೆ lo ಮತ್ತು ಅದು ನಿಜವಾಗಿಯೂ ಪ್ರಾರಂಭವಾಯಿತು!

$ cat xdp-simple.c
#include <linux/if_link.h>
#include <err.h>
#include <unistd.h>
#include "xdp-simple.skel.h"

int main(int argc, char **argv)
{
    __u32 flags = XDP_FLAGS_SKB_MODE;
    struct xdp_simple_bpf *obj;

    obj = xdp_simple_bpf__open_and_load();
    if (!obj)
        err(1, "failed to open and/or load BPF objectn");

    bpf_set_link_xdp_fd(1, -1, flags);
    bpf_set_link_xdp_fd(1, bpf_program__fd(obj->progs.simple), flags);

cleanup:
    xdp_simple_bpf__destroy(obj);
}

ಇಲ್ಲಿ ನಾವು ಫಂಕ್ಷನ್ ಅನ್ನು ಬಳಸುತ್ತೇವೆ. bpf_set_link_xdp_fd, ಇದು XDP ನಂತಹ BPF ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ನೆಟ್‌ವರ್ಕ್ ಇಂಟರ್ಫೇಸ್‌ಗಳಿಗೆ ಸಂಪರ್ಕಿಸುತ್ತದೆ. ನಾವು ಇಂಟರ್ಫೇಸ್ ಸಂಖ್ಯೆಯನ್ನು ಹಾರ್ಡ್‌ಕೋಡ್ ಮಾಡಿದ್ದೇವೆ lo, ಅದು ಯಾವಾಗಲೂ 1 ಆಗಿರುತ್ತದೆ. ಹಳೆಯ ಪ್ರೋಗ್ರಾಂ ಲಗತ್ತಿಸಿದ್ದರೆ ಅದನ್ನು ಮೊದಲು ಬೇರ್ಪಡಿಸಲು ನಾವು ಕಾರ್ಯವನ್ನು ಎರಡು ಬಾರಿ ರನ್ ಮಾಡುತ್ತೇವೆ. ಗಮನಿಸಿ: ನಾವು ಇನ್ನು ಮುಂದೆ ಕರೆ ಮಾಡುವ ಅಗತ್ಯವಿಲ್ಲ. pause ಅಥವಾ ಅನಂತ ಲೂಪ್: ನಮ್ಮ ಬೂಟ್‌ಲೋಡರ್ ಪ್ರೋಗ್ರಾಂ ಕೊನೆಗೊಳ್ಳುತ್ತದೆ, ಆದರೆ ಬಿಪಿಎಫ್ ಪ್ರೋಗ್ರಾಂ ಈವೆಂಟ್ ಮೂಲಕ್ಕೆ ಸಂಪರ್ಕಗೊಂಡಿರುವುದರಿಂದ ಅದನ್ನು ಕೊಲ್ಲಲಾಗುವುದಿಲ್ಲ. ಯಶಸ್ವಿಯಾಗಿ ಲೋಡ್ ಮಾಡಿ ಸಂಪರ್ಕಗೊಂಡ ನಂತರ, ಪ್ರೋಗ್ರಾಂ ಬರುವ ಪ್ರತಿಯೊಂದು ನೆಟ್‌ವರ್ಕ್ ಪ್ಯಾಕೆಟ್‌ಗೆ ರನ್ ಆಗುತ್ತದೆ. lo.

ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡೋಣ ಮತ್ತು ಇಂಟರ್ಫೇಸ್ ಅನ್ನು ನೋಡೋಣ. lo:

$ sudo ./xdp-simple
$ sudo bpftool p | grep simple
669: xdp  name simple  tag 4fca62e77ccb43d6  gpl
$ ip l show dev lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 xdpgeneric qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    prog/xdp id 669

ನಾವು ಡೌನ್‌ಲೋಡ್ ಮಾಡಿದ ಪ್ರೋಗ್ರಾಂ ID 669 ಅನ್ನು ಹೊಂದಿದೆ ಮತ್ತು ಇಂಟರ್ಫೇಸ್‌ನಲ್ಲಿ ನಾವು ಅದೇ ID ಅನ್ನು ನೋಡುತ್ತೇವೆ. lo. ನಾವು ಒಂದೆರಡು ಪ್ಯಾಕೇಜ್‌ಗಳನ್ನು ಕಳುಹಿಸುತ್ತೇವೆ 127.0.0.1 (ವಿನಂತಿ + ಪ್ರತ್ಯುತ್ತರ):

$ ping -c1 localhost

ಮತ್ತು ಈಗ ಡೀಬಗ್ ವರ್ಚುವಲ್ ಫೈಲ್‌ನ ವಿಷಯಗಳನ್ನು ನೋಡೋಣ. /sys/kernel/debug/tracing/trace_pipe, ಯಾವುದರಲ್ಲಿ bpf_printk ತನ್ನ ಸಂದೇಶಗಳನ್ನು ಬರೆಯುತ್ತಾನೆ:

# cat /sys/kernel/debug/tracing/trace_pipe
ping-13937 [000] d.s1 442015.377014: bpf_trace_printk: running on CPU0
ping-13937 [000] d.s1 442015.377027: bpf_trace_printk: running on CPU0

ಎರಡು ಪ್ಯಾಕೇಜ್‌ಗಳು ಕಂಡುಬಂದವು lo ಮತ್ತು CPU0 ನಲ್ಲಿ ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲಾಗಿದೆ - ನಮ್ಮ ಮೊದಲ ಪೂರ್ಣ ಪ್ರಮಾಣದ ಅರ್ಥಹೀನ BPF ಪ್ರೋಗ್ರಾಂ ಕೆಲಸ ಮಾಡಿದೆ!

ಗಮನಿಸಬೇಕಾದ ಅಂಶವೆಂದರೆ bpf_printk ಇದು ಡೀಬಗ್ ಫೈಲ್‌ಗೆ ಬರೆಯುವುದು ವ್ಯರ್ಥವಲ್ಲ: ಉತ್ಪಾದನೆಯಲ್ಲಿ ಬಳಸಲು ಇದು ಅತ್ಯುತ್ತಮ ಸಹಾಯಕವಲ್ಲ, ಆದರೆ ನಮ್ಮ ಗುರಿಯಾಗಿತ್ತು ಸರಳವಾದದ್ದನ್ನು ತೋರಿಸುವುದು.

ಬಿಪಿಎಫ್ ಕಾರ್ಯಕ್ರಮಗಳಿಂದ ನಕ್ಷೆಗಳನ್ನು ಪ್ರವೇಶಿಸುವುದು

ಉದಾಹರಣೆ: ನಾವು ಬಿಪಿಎಫ್ ಪ್ರೋಗ್ರಾಂನಿಂದ ನಕ್ಷೆಯನ್ನು ಬಳಸುತ್ತೇವೆ.

ಹಿಂದಿನ ವಿಭಾಗಗಳಲ್ಲಿ ನಾವು ಬಳಕೆದಾರ ಸ್ಥಳದಿಂದ ನಕ್ಷೆಗಳನ್ನು ಹೇಗೆ ರಚಿಸುವುದು ಮತ್ತು ಬಳಸುವುದು ಎಂಬುದನ್ನು ಕಲಿತಿದ್ದೇವೆ ಮತ್ತು ಈಗ ಕರ್ನಲ್ ಭಾಗವನ್ನು ನೋಡೋಣ. ಎಂದಿನಂತೆ, ಒಂದು ಉದಾಹರಣೆಯೊಂದಿಗೆ ಪ್ರಾರಂಭಿಸೋಣ. ನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಪುನಃ ಬರೆಯೋಣ. xdp-simple.bpf.c ಕೆಳಗಿನಂತೆ:

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>

struct {
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __uint(max_entries, 8);
    __type(key, u32);
    __type(value, u64);
} woo SEC(".maps");

SEC("xdp/simple")
int simple(void *ctx)
{
    u32 key = bpf_get_smp_processor_id();
    u32 *val;

    val = bpf_map_lookup_elem(&woo, &key);
    if (!val)
        return XDP_ABORTED;

    *val += 1;

    return XDP_PASS;
}

char LICENSE[] SEC("license") = "GPL";

ನಾವು ಕಾರ್ಯಕ್ರಮದ ಆರಂಭಕ್ಕೆ ನಕ್ಷೆಯ ವ್ಯಾಖ್ಯಾನವನ್ನು ಸೇರಿಸಿದ್ದೇವೆ. woo: ಇದು 8 ಅಂಶಗಳ ಒಂದು ಶ್ರೇಣಿಯಾಗಿದ್ದು ಅದು ಪ್ರಕಾರದ ಮೌಲ್ಯಗಳನ್ನು ಸಂಗ್ರಹಿಸುತ್ತದೆ u64 (C ನಲ್ಲಿ ನಾವು ಅಂತಹ ಶ್ರೇಣಿಯನ್ನು ಹೀಗೆ ವ್ಯಾಖ್ಯಾನಿಸುತ್ತೇವೆ u64 woo[8]). ಕಾರ್ಯಕ್ರಮದಲ್ಲಿ "xdp/simple" ನಾವು ಪ್ರಸ್ತುತ ಪ್ರೊಸೆಸರ್ ಸಂಖ್ಯೆಯನ್ನು ವೇರಿಯೇಬಲ್ ಆಗಿ ಪಡೆಯುತ್ತೇವೆ. key ತದನಂತರ ಸಹಾಯಕ ಕಾರ್ಯವನ್ನು ಬಳಸಿ bpf_map_lookup_element ನಾವು ಶ್ರೇಣಿಯಲ್ಲಿನ ಅನುಗುಣವಾದ ನಮೂದುಗೆ ಪಾಯಿಂಟರ್ ಅನ್ನು ಪಡೆಯುತ್ತೇವೆ, ಅದನ್ನು ನಾವು ಒಂದರಿಂದ ಹೆಚ್ಚಿಸುತ್ತೇವೆ. ರಷ್ಯನ್ ಭಾಷೆಗೆ ಅನುವಾದಿಸಲಾಗಿದೆ: ಒಳಬರುವ ಪ್ಯಾಕೆಟ್‌ಗಳನ್ನು ಯಾವ CPU ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲಾಗಿದೆ ಎಂಬುದರ ಕುರಿತು ನಾವು ಅಂಕಿಅಂಶಗಳನ್ನು ಲೆಕ್ಕ ಹಾಕುತ್ತೇವೆ. ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಚಲಾಯಿಸಲು ಪ್ರಯತ್ನಿಸೋಣ:

$ clang -O2 -g -c -target bpf -I libbpf/src/root/usr/include xdp-simple.bpf.c -o xdp-simple.bpf.o
$ bpftool gen skeleton xdp-simple.bpf.o > xdp-simple.skel.h
$ clang -O2 -g -I ./libbpf/src/root/usr/include/ -o xdp-simple xdp-simple.c ./libbpf/src/root/usr/lib64/libbpf.a -lelf -lz
$ sudo ./xdp-simple

ಅವಳು ಅದಕ್ಕೆ ಅಂಟಿಕೊಂಡಿದ್ದಾಳೆಯೇ ಎಂದು ಪರಿಶೀಲಿಸೋಣ. lo ಮತ್ತು ನಾವು ಕೆಲವು ಪ್ಯಾಕೇಜ್‌ಗಳನ್ನು ಕಳುಹಿಸುತ್ತೇವೆ:

$ ip l show dev lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 xdpgeneric qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    prog/xdp id 108

$ for s in `seq 234`; do sudo ping -f -c 100 127.0.0.1 >/dev/null 2>&1; done

ಈಗ ರಚನೆಯ ವಿಷಯಗಳನ್ನು ನೋಡೋಣ:

$ sudo bpftool map dump name woo
[
    { "key": 0, "value": 0 },
    { "key": 1, "value": 400 },
    { "key": 2, "value": 0 },
    { "key": 3, "value": 0 },
    { "key": 4, "value": 0 },
    { "key": 5, "value": 0 },
    { "key": 6, "value": 0 },
    { "key": 7, "value": 46400 }
]

ಬಹುತೇಕ ಎಲ್ಲಾ ಪ್ರಕ್ರಿಯೆಗಳನ್ನು CPU7 ನಲ್ಲಿ ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲಾಗಿದೆ. ನಮಗೆ ಇದು ಅಪ್ರಸ್ತುತವಾಗುತ್ತದೆ, ಮುಖ್ಯ ವಿಷಯವೆಂದರೆ ಪ್ರೋಗ್ರಾಂ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ ಮತ್ತು BPF ಪ್ರೋಗ್ರಾಂಗಳಿಂದ ನಕ್ಷೆಗಳನ್ನು ಹೇಗೆ ಪ್ರವೇಶಿಸುವುದು ಎಂದು ನಾವು ಅರ್ಥಮಾಡಿಕೊಂಡಿದ್ದೇವೆ - ಬಳಸಿ хелперов bpf_mp_*.

ಅತೀಂದ್ರಿಯ ಪಾಯಿಂಟರ್

ಹಾಗಾಗಿ, ನಾವು ಈ ರೀತಿಯ ಕರೆಗಳನ್ನು ಬಳಸಿಕೊಂಡು BPF ಪ್ರೋಗ್ರಾಂನಿಂದ ನಕ್ಷೆಯನ್ನು ಪ್ರವೇಶಿಸಬಹುದು

val = bpf_map_lookup_elem(&woo, &key);

ಸಹಾಯಕ ಕಾರ್ಯವು ಈ ರೀತಿ ಕಾಣುತ್ತದೆ.

void *bpf_map_lookup_elem(struct bpf_map *map, const void *key)

ಆದರೆ ನಾವು ಒಂದು ಪಾಯಿಂಟರ್ ಅನ್ನು ಹಾದು ಹೋಗುತ್ತೇವೆ &woo ಹೆಸರಿಸದ ರಚನೆಗೆ struct { ... }...

ನಾವು ಪ್ರೋಗ್ರಾಂನ ಅಸೆಂಬ್ಲರ್ ಅನ್ನು ನೋಡಿದರೆ, ನಮಗೆ ಮೌಲ್ಯವು ಕಂಡುಬರುತ್ತದೆ &woo ವಾಸ್ತವವಾಗಿ ವ್ಯಾಖ್ಯಾನಿಸಲಾಗಿಲ್ಲ (ಸಾಲು 4):

llvm-objdump -D --section xdp/simple xdp-simple.bpf.o

xdp-simple.bpf.o:       file format elf64-bpf

Disassembly of section xdp/simple:

0000000000000000 <simple>:
       0:       85 00 00 00 08 00 00 00 call 8
       1:       63 0a fc ff 00 00 00 00 *(u32 *)(r10 - 4) = r0
       2:       bf a2 00 00 00 00 00 00 r2 = r10
       3:       07 02 00 00 fc ff ff ff r2 += -4
       4:       18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll
       6:       85 00 00 00 01 00 00 00 call 1
...

ಮತ್ತು ಸ್ಥಳಾಂತರಗಳಲ್ಲಿ ಒಳಗೊಂಡಿದೆ:

$ llvm-readelf -r xdp-simple.bpf.o | head -4

Relocation section '.relxdp/simple' at offset 0xe18 contains 1 entries:
    Offset             Info             Type               Symbol's Value  Symbol's Name
0000000000000020  0000002700000001 R_BPF_64_64            0000000000000000 woo

ಆದರೆ ನಾವು ಈಗಾಗಲೇ ಲೋಡ್ ಆಗಿರುವ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ನೋಡಿದರೆ, ಸರಿಯಾದ ನಕ್ಷೆಗೆ (ಸಾಲು 4) ಪಾಯಿಂಟರ್ ಅನ್ನು ನಾವು ನೋಡುತ್ತೇವೆ:

$ sudo bpftool prog dump x name simple
int simple(void *ctx):
   0: (85) call bpf_get_smp_processor_id#114128
   1: (63) *(u32 *)(r10 -4) = r0
   2: (bf) r2 = r10
   3: (07) r2 += -4
   4: (18) r1 = map[id:64]
...

ಹೀಗಾಗಿ, ನಮ್ಮ ಬೂಟ್‌ಲೋಡರ್ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಪ್ರಾರಂಭಿಸುವ ಕ್ಷಣದಲ್ಲಿ, ಲಿಂಕ್ ಇದೆ ಎಂದು ನಾವು ತೀರ್ಮಾನಿಸಬಹುದು &woo ಯಾವುದೋ ಗ್ರಂಥಾಲಯದಿಂದ ಬದಲಾಯಿಸಲಾಯಿತು libbpf. ಮೊದಲು, ಔಟ್‌ಪುಟ್ ಅನ್ನು ನೋಡೋಣ. strace:

$ sudo strace -e bpf ./xdp-simple
...
bpf(BPF_MAP_CREATE, {map_type=BPF_MAP_TYPE_ARRAY, key_size=4, value_size=8, max_entries=8, map_name="woo", ...}, 120) = 4
bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_XDP, prog_name="simple", ...}, 120) = 5

ನಾವು ಅದನ್ನು ನೋಡುತ್ತೇವೆ libbpf ನಕ್ಷೆಯನ್ನು ರಚಿಸಲಾಗಿದೆ woo ತದನಂತರ ನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಲೋಡ್ ಮಾಡಿದೆ simple. ನಾವು ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಹೇಗೆ ಲೋಡ್ ಮಾಡುತ್ತೇವೆ ಎಂಬುದನ್ನು ಹತ್ತಿರದಿಂದ ನೋಡೋಣ:

  • ನಾವು ಕರೆಯುತ್ತೇವೆ xdp_simple_bpf__open_and_load ಫೈಲ್‌ನಿಂದ xdp-simple.skel.h
  • ಯಾವುದು ಕಾರಣವಾಗುತ್ತದೆ xdp_simple_bpf__load ಫೈಲ್‌ನಿಂದ xdp-simple.skel.h
  • ಯಾವುದು ಕಾರಣವಾಗುತ್ತದೆ bpf_object__load_skeleton ಫೈಲ್‌ನಿಂದ libbpf/src/libbpf.c
  • ಯಾವುದು ಕಾರಣವಾಗುತ್ತದೆ bpf_object__load_xattr ನಿಂದ libbpf/src/libbpf.c

ಕೊನೆಯ ಕಾರ್ಯ, ಇತರ ವಿಷಯಗಳ ಜೊತೆಗೆ, ಕರೆಯುತ್ತದೆ bpf_object__create_maps, ಇದು ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ನಕ್ಷೆಗಳನ್ನು ರಚಿಸುತ್ತದೆ ಅಥವಾ ತೆರೆಯುತ್ತದೆ, ಅವುಗಳನ್ನು ಫೈಲ್ ಡಿಸ್ಕ್ರಿಪ್ಟರ್‌ಗಳಾಗಿ ಪರಿವರ್ತಿಸುತ್ತದೆ. (ಇಲ್ಲಿ ನಾವು ನೋಡುತ್ತೇವೆ BPF_MAP_CREATE ಔಟ್‌ಪುಟ್‌ನಲ್ಲಿ strace.) ನಂತರ ಕಾರ್ಯವನ್ನು ಕರೆಯಲಾಗುತ್ತದೆ bpf_object__relocate ಮತ್ತು ನಾವು ನೋಡಿದ್ದನ್ನು ನೆನಪಿಸಿಕೊಳ್ಳುವುದರಿಂದ ಇದು ನಮಗೆ ಆಸಕ್ತಿಯನ್ನುಂಟುಮಾಡುತ್ತದೆ. woo ಸ್ಥಳಾಂತರ ಕೋಷ್ಟಕದಲ್ಲಿ. ಅದನ್ನು ಅನ್ವೇಷಿಸುತ್ತಾ, ನಾವು ಅಂತಿಮವಾಗಿ ಕಾರ್ಯಕ್ಕೆ ಹೋಗುತ್ತೇವೆ. bpf_program__relocate, ಇದು ಮತ್ತು ನಕ್ಷೆ ಸ್ಥಳಾಂತರಗಳೊಂದಿಗೆ ವ್ಯವಹರಿಸುತ್ತದೆ:

case RELO_LD64:
    insn[0].src_reg = BPF_PSEUDO_MAP_FD;
    insn[0].imm = obj->maps[relo->map_idx].fd;
    break;

ಆದ್ದರಿಂದ, ನಾವು ನಮ್ಮ ಸೂಚನೆಯನ್ನು ತೆಗೆದುಕೊಳ್ಳುತ್ತೇವೆ

18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll

ಮತ್ತು ಅದರಲ್ಲಿರುವ ಮೂಲ ರಿಜಿಸ್ಟರ್ ಅನ್ನು ಬದಲಾಯಿಸಿ BPF_PSEUDO_MAP_FD, ಮತ್ತು ನಮ್ಮ ನಕ್ಷೆಯ ಫೈಲ್ ಡಿಸ್ಕ್ರಿಪ್ಟರ್‌ಗೆ ಮೊದಲ IMM ಮತ್ತು, ಅದು ಸಮಾನವಾಗಿದ್ದರೆ, ಉದಾಹರಣೆಗೆ, 0xdeadbeef, ನಂತರ ಪರಿಣಾಮವಾಗಿ ನಾವು ಸೂಚನೆಯನ್ನು ಪಡೆಯುತ್ತೇವೆ

18 11 00 00 ef eb ad de 00 00 00 00 00 00 00 00 r1 = 0 ll

ನಕ್ಷೆಯ ಮಾಹಿತಿಯನ್ನು ನಿರ್ದಿಷ್ಟ ಲೋಡ್ ಮಾಡಲಾದ BPF ಪ್ರೋಗ್ರಾಂಗೆ ವರ್ಗಾಯಿಸುವುದು ಹೀಗೆ. ನಕ್ಷೆಯನ್ನು ಇದನ್ನು ಬಳಸಿಯೂ ರಚಿಸಬಹುದು BPF_MAP_CREATE, ಮತ್ತು ID ಬಳಸಿ ತೆರೆಯಲಾಗಿದೆ BPF_MAP_GET_FD_BY_ID.

ಒಟ್ಟು, ಬಳಸುವಾಗ libbpf ಅಲ್ಗಾರಿದಮ್ ಈ ಕೆಳಗಿನಂತಿರುತ್ತದೆ:

  • ಸಂಕಲನದ ಸಮಯದಲ್ಲಿ, ನಕ್ಷೆಗಳ ಉಲ್ಲೇಖಗಳಿಗಾಗಿ ಸ್ಥಳಾಂತರ ಕೋಷ್ಟಕದಲ್ಲಿ ನಮೂದುಗಳನ್ನು ರಚಿಸಲಾಗುತ್ತದೆ.
  • libbpf ELF ಆಬ್ಜೆಕ್ಟ್ ಲೈಬ್ರರಿಯನ್ನು ತೆರೆಯುತ್ತದೆ, ಬಳಸಿದ ಎಲ್ಲಾ ನಕ್ಷೆಗಳನ್ನು ಹುಡುಕುತ್ತದೆ ಮತ್ತು ಅವುಗಳಿಗೆ ಫೈಲ್ ಡಿಸ್ಕ್ರಿಪ್ಟರ್‌ಗಳನ್ನು ರಚಿಸುತ್ತದೆ.
  • ಸೂಚನೆಯ ಭಾಗವಾಗಿ ಫೈಲ್ ಡಿಸ್ಕ್ರಿಪ್ಟರ್‌ಗಳನ್ನು ಕರ್ನಲ್‌ಗೆ ಲೋಡ್ ಮಾಡಲಾಗುತ್ತದೆ. LD64

ನೀವು ಊಹಿಸುವಂತೆ, ಅಷ್ಟೆ ಅಲ್ಲ, ಮತ್ತು ನಾವು ಕರ್ನಲ್ ಅನ್ನು ನೋಡಬೇಕಾಗುತ್ತದೆ. ಅದೃಷ್ಟವಶಾತ್, ನಮಗೆ ಒಂದು ಸುಳಿವು ಸಿಕ್ಕಿದೆ - ನಾವು ಮೌಲ್ಯವನ್ನು ಬರೆದಿದ್ದೇವೆ. BPF_PSEUDO_MAP_FD ಮೂಲ ರಿಜಿಸ್ಟರ್‌ಗೆ ಮತ್ತು ನಾವು ಅದನ್ನು ಹೂಳಬಹುದು, ಅದು ನಮ್ಮನ್ನು ಪವಿತ್ರ ಪವಿತ್ರ ಸ್ಥಳಕ್ಕೆ ಕರೆದೊಯ್ಯುತ್ತದೆ - kernel/bpf/verifier.c, ಇಲ್ಲಿ ವಿಶಿಷ್ಟ ಹೆಸರಿನ ಕಾರ್ಯವು ಫೈಲ್ ಡಿಸ್ಕ್ರಿಪ್ಟರ್ ಅನ್ನು ಪ್ರಕಾರದ ರಚನೆಯ ವಿಳಾಸದೊಂದಿಗೆ ಬದಲಾಯಿಸುತ್ತದೆ struct bpf_map:

static int replace_map_fd_with_map_ptr(struct bpf_verifier_env *env) {
    ...

    f = fdget(insn[0].imm);
    map = __bpf_map_get(f);
    if (insn->src_reg == BPF_PSEUDO_MAP_FD) {
        addr = (unsigned long)map;
    }
    insn[0].imm = (u32)addr;
    insn[1].imm = addr >> 32;

(ಪೂರ್ಣ ಕೋಡ್ ಅನ್ನು ಕಾಣಬಹುದು ಲಿಂಕ್). ಆದ್ದರಿಂದ ನಾವು ನಮ್ಮ ಅಲ್ಗಾರಿದಮ್ ಅನ್ನು ಪೂರಕಗೊಳಿಸಬಹುದು:

  • ಪ್ರೋಗ್ರಾಂ ಲೋಡ್ ಆಗುವಾಗ, ಪರಿಶೀಲಕವು ನಕ್ಷೆಯ ಬಳಕೆಯ ನಿಖರತೆಯನ್ನು ಪರಿಶೀಲಿಸುತ್ತದೆ ಮತ್ತು ಅನುಗುಣವಾದ ರಚನೆಯ ವಿಳಾಸವನ್ನು ಬರೆಯುತ್ತದೆ. struct bpf_map

ELF ಬೈನರಿಯನ್ನು ಲೋಡ್ ಮಾಡುವಾಗ ಬಳಸಿ libbpf ಇನ್ನೂ ಅನೇಕ ಘಟನೆಗಳು ನಡೆಯುತ್ತಿವೆ, ಆದರೆ ನಾವು ಇದನ್ನು ಇತರ ಲೇಖನಗಳಲ್ಲಿ ಚರ್ಚಿಸುತ್ತೇವೆ.

libbpf ಇಲ್ಲದೆ ಪ್ರೋಗ್ರಾಂಗಳು ಮತ್ತು ನಕ್ಷೆಗಳನ್ನು ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ

ಭರವಸೆ ನೀಡಿದಂತೆ, ಸಹಾಯವಿಲ್ಲದೆ ನಕ್ಷೆಗಳನ್ನು ಬಳಸುವ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಹೇಗೆ ರಚಿಸುವುದು ಮತ್ತು ಲೋಡ್ ಮಾಡುವುದು ಎಂದು ತಿಳಿಯಲು ಬಯಸುವ ಓದುಗರಿಗೆ ಒಂದು ಉದಾಹರಣೆ ಇಲ್ಲಿದೆ. libbpfನೀವು ಅವಲಂಬನೆಗಳನ್ನು ನಿರ್ಮಿಸಲು ಸಾಧ್ಯವಾಗದ ಅಥವಾ ಪ್ರತಿ ಬಿಟ್ ಅನ್ನು ಉಳಿಸಲು ಸಾಧ್ಯವಾಗದ ವಾತಾವರಣದಲ್ಲಿ ಕೆಲಸ ಮಾಡುತ್ತಿರುವಾಗ ಅಥವಾ ಒಂದು ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಬರೆಯುವಾಗ ಇದು ಉಪಯುಕ್ತವಾಗಿರುತ್ತದೆ. ply, ಇದು ಬಿಪಿಎಫ್ ಬೈನರಿ ಕೋಡ್ ಅನ್ನು ತಕ್ಷಣವೇ ಉತ್ಪಾದಿಸುತ್ತದೆ.

ತರ್ಕವನ್ನು ಅನುಸರಿಸಲು ಸುಲಭವಾಗುವಂತೆ, ಈ ಉದ್ದೇಶಗಳಿಗಾಗಿ ನಾವು ನಮ್ಮ ಉದಾಹರಣೆಯನ್ನು ಪುನಃ ಬರೆಯುತ್ತೇವೆ. xdp-simpleಈ ಉದಾಹರಣೆಯಲ್ಲಿ ಪರಿಗಣಿಸಲಾದ ಪ್ರೋಗ್ರಾಂನ ಸಂಪೂರ್ಣ ಮತ್ತು ಸ್ವಲ್ಪ ವಿಸ್ತೃತ ಕೋಡ್ ಅನ್ನು ಇಲ್ಲಿ ಕಾಣಬಹುದು ಸಾರಾಂಶ.

ನಮ್ಮ ಅರ್ಜಿಯ ತರ್ಕ ಹೀಗಿದೆ:

  • ಪ್ರಕಾರದ ನಕ್ಷೆಯನ್ನು ರಚಿಸಿ BPF_MAP_TYPE_ARRAY ಆಜ್ಞೆಯನ್ನು ಬಳಸಿ BPF_MAP_CREATE,
  • ಈ ನಕ್ಷೆಯನ್ನು ಬಳಸುವ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ರಚಿಸಿ,
  • ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಇಂಟರ್ಫೇಸ್‌ಗೆ ಸಂಪರ್ಕಪಡಿಸಿ lo,

ಇದು ಮಾನವ ಎಂದು ಅನುವಾದಿಸುತ್ತದೆ

int main(void)
{
    int map_fd, prog_fd;

    map_fd = map_create();
    if (map_fd < 0)
        err(1, "bpf: BPF_MAP_CREATE");

    prog_fd = prog_load(map_fd);
    if (prog_fd < 0)
        err(1, "bpf: BPF_PROG_LOAD");

    xdp_attach(1, prog_fd);
}

ಇದು map_create ಸಿಸ್ಟಮ್ ಕರೆಯ ಬಗ್ಗೆ ಮೊದಲ ಉದಾಹರಣೆಯಲ್ಲಿ ನಾವು ಮಾಡಿದಂತೆಯೇ ನಕ್ಷೆಯನ್ನು ರಚಿಸುತ್ತದೆ. bpf — “ಕರ್ನಲ್, ದಯವಿಟ್ಟು ನನಗೆ 8 ಅಂಶಗಳ ಶ್ರೇಣಿಯ ರೂಪದಲ್ಲಿ ಹೊಸ ನಕ್ಷೆಯನ್ನು ಮಾಡಿ __u64 ಮತ್ತು ಫೈಲ್ ವಿವರಣೆಯನ್ನು ನನಗೆ ಹಿಂತಿರುಗಿ ಕೊಡಿ":

static int map_create()
{
    union bpf_attr attr;

    memset(&attr, 0, sizeof(attr));
    attr.map_type = BPF_MAP_TYPE_ARRAY,
    attr.key_size = sizeof(__u32),
    attr.value_size = sizeof(__u64),
    attr.max_entries = 8,
    strncpy(attr.map_name, "woo", sizeof(attr.map_name));
    return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
}

ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಸರಳವಾಗಿ ಲೋಡ್ ಮಾಡಲಾಗಿದೆ:

static int prog_load(int map_fd)
{
    union bpf_attr attr;
    struct bpf_insn insns[] = {
        ...
    };

    memset(&attr, 0, sizeof(attr));
    attr.prog_type = BPF_PROG_TYPE_XDP;
    attr.insns     = ptr_to_u64(insns);
    attr.insn_cnt  = sizeof(insns)/sizeof(insns[0]);
    attr.license   = ptr_to_u64("GPL");
    strncpy(attr.prog_name, "woo", sizeof(attr.prog_name));
    return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
}

ಕ್ಲಿಷ್ಟಕರ ಭಾಗ prog_load — ಇದು ನಮ್ಮ ಬಿಪಿಎಫ್ ಪ್ರೋಗ್ರಾಂನ ರಚನೆಗಳ ಒಂದು ಶ್ರೇಣಿಯ ವ್ಯಾಖ್ಯಾನವಾಗಿದೆ. struct bpf_insn insns[]ಆದರೆ ನಾವು C ನಲ್ಲಿ ಇರುವ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಬಳಸುತ್ತಿರುವುದರಿಂದ, ನಾವು ಸ್ವಲ್ಪ ಮೋಸ ಮಾಡಬಹುದು:

$ llvm-objdump -D --section xdp/simple xdp-simple.bpf.o

0000000000000000 <simple>:
       0:       85 00 00 00 08 00 00 00 call 8
       1:       63 0a fc ff 00 00 00 00 *(u32 *)(r10 - 4) = r0
       2:       bf a2 00 00 00 00 00 00 r2 = r10
       3:       07 02 00 00 fc ff ff ff r2 += -4
       4:       18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll
       6:       85 00 00 00 01 00 00 00 call 1
       7:       b7 01 00 00 00 00 00 00 r1 = 0
       8:       15 00 04 00 00 00 00 00 if r0 == 0 goto +4 <LBB0_2>
       9:       61 01 00 00 00 00 00 00 r1 = *(u32 *)(r0 + 0)
      10:       07 01 00 00 01 00 00 00 r1 += 1
      11:       63 10 00 00 00 00 00 00 *(u32 *)(r0 + 0) = r1
      12:       b7 01 00 00 02 00 00 00 r1 = 2

0000000000000068 <LBB0_2>:
      13:       bf 10 00 00 00 00 00 00 r0 = r1
      14:       95 00 00 00 00 00 00 00 exit

ಒಟ್ಟಾರೆಯಾಗಿ, ನಾವು 14 ಸೂಚನೆಗಳನ್ನು ರಚನೆಗಳ ರೂಪದಲ್ಲಿ ಬರೆಯಬೇಕಾಗಿದೆ, ಉದಾಹರಣೆಗೆ struct bpf_insn (ಸಲಹೆ: ಮೇಲಿನಿಂದ ಡಂಪ್ ತೆಗೆದುಕೊಳ್ಳಿ, ಸೂಚನೆಗಳ ವಿಭಾಗವನ್ನು ಮತ್ತೆ ಓದಿ, ತೆರೆಯಿರಿ linux/bpf.h и linux/bpf_common.h ಮತ್ತು ನಿರ್ಧರಿಸಲು ಪ್ರಯತ್ನಿಸಿ struct bpf_insn insns[] ಸ್ವಂತವಾಗಿ):

struct bpf_insn insns[] = {
    /* 85 00 00 00 08 00 00 00 call 8 */
    {
        .code = BPF_JMP | BPF_CALL,
        .imm = 8,
    },

    /* 63 0a fc ff 00 00 00 00 *(u32 *)(r10 - 4) = r0 */
    {
        .code = BPF_MEM | BPF_STX,
        .off = -4,
        .src_reg = BPF_REG_0,
        .dst_reg = BPF_REG_10,
    },

    /* bf a2 00 00 00 00 00 00 r2 = r10 */
    {
        .code = BPF_ALU64 | BPF_MOV | BPF_X,
        .src_reg = BPF_REG_10,
        .dst_reg = BPF_REG_2,
    },

    /* 07 02 00 00 fc ff ff ff r2 += -4 */
    {
        .code = BPF_ALU64 | BPF_ADD | BPF_K,
        .dst_reg = BPF_REG_2,
        .imm = -4,
    },

    /* 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll */
    {
        .code = BPF_LD | BPF_DW | BPF_IMM,
        .src_reg = BPF_PSEUDO_MAP_FD,
        .dst_reg = BPF_REG_1,
        .imm = map_fd,
    },
    { }, /* placeholder */

    /* 85 00 00 00 01 00 00 00 call 1 */
    {
        .code = BPF_JMP | BPF_CALL,
        .imm = 1,
    },

    /* b7 01 00 00 00 00 00 00 r1 = 0 */
    {
        .code = BPF_ALU64 | BPF_MOV | BPF_K,
        .dst_reg = BPF_REG_1,
        .imm = 0,
    },

    /* 15 00 04 00 00 00 00 00 if r0 == 0 goto +4 <LBB0_2> */
    {
        .code = BPF_JMP | BPF_JEQ | BPF_K,
        .off = 4,
        .src_reg = BPF_REG_0,
        .imm = 0,
    },

    /* 61 01 00 00 00 00 00 00 r1 = *(u32 *)(r0 + 0) */
    {
        .code = BPF_MEM | BPF_LDX,
        .off = 0,
        .src_reg = BPF_REG_0,
        .dst_reg = BPF_REG_1,
    },

    /* 07 01 00 00 01 00 00 00 r1 += 1 */
    {
        .code = BPF_ALU64 | BPF_ADD | BPF_K,
        .dst_reg = BPF_REG_1,
        .imm = 1,
    },

    /* 63 10 00 00 00 00 00 00 *(u32 *)(r0 + 0) = r1 */
    {
        .code = BPF_MEM | BPF_STX,
        .src_reg = BPF_REG_1,
        .dst_reg = BPF_REG_0,
    },

    /* b7 01 00 00 02 00 00 00 r1 = 2 */
    {
        .code = BPF_ALU64 | BPF_MOV | BPF_K,
        .dst_reg = BPF_REG_1,
        .imm = 2,
    },

    /* <LBB0_2>: bf 10 00 00 00 00 00 00 r0 = r1 */
    {
        .code = BPF_ALU64 | BPF_MOV | BPF_X,
        .src_reg = BPF_REG_1,
        .dst_reg = BPF_REG_0,
    },

    /* 95 00 00 00 00 00 00 00 exit */
    {
        .code = BPF_JMP | BPF_EXIT
    },
};

ಸ್ವತಃ ಬರೆಯದವರಿಗೆ ಒಂದು ವ್ಯಾಯಾಮ - ಹುಡುಕಿ map_fd.

ನಮ್ಮ ಕಾರ್ಯಕ್ರಮದ ಇನ್ನೂ ಒಂದು ಬಹಿರಂಗಪಡಿಸದ ಭಾಗವಿದೆ - xdp_attachದುರದೃಷ್ಟವಶಾತ್, XDP ನಂತಹ ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಸಿಸ್ಟಮ್ ಕರೆಯನ್ನು ಬಳಸಿಕೊಂಡು ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. bpf. BPF ಮತ್ತು XDP ಗಳನ್ನು ರಚಿಸಿದ ಜನರು ಲಿನಕ್ಸ್ ನೆಟ್‌ವರ್ಕಿಂಗ್ ಸಮುದಾಯದವರು, ಅಂದರೆ ಅವರು ಅವರಿಗೆ ಹೆಚ್ಚು ಪರಿಚಿತವಾದದ್ದನ್ನು ಬಳಸಿದರು (ಆದರೆ ಸಾಮಾನ್ಯ ಜನರು) ಕೋರ್ ಜೊತೆ ಸಂವಹನ ನಡೆಸಲು ಇಂಟರ್ಫೇಸ್: ನೆಟ್‌ಲಿಂಕ್ ಸಾಕೆಟ್‌ಗಳು, ಇದನ್ನೂ ನೋಡಿ ಆರ್‌ಎಫ್‌ಸಿ 3549. ಕಾರ್ಯಗತಗೊಳಿಸಲು ಸುಲಭವಾದ ಮಾರ್ಗ xdp_attach - ಇದು ಕೋಡ್ ಅನ್ನು ನಕಲಿಸುತ್ತಿದೆ libbpf, ಅಂದರೆ, ಫೈಲ್‌ನಿಂದ netlink.c, ಅದನ್ನೇ ನಾವು ಮಾಡಿದ್ದೇವೆ, ಅದನ್ನು ಸ್ವಲ್ಪ ಕಡಿಮೆ ಮಾಡಿದ್ದೇವೆ:

ನೆಟ್‌ಲಿಂಕ್ ಸಾಕೆಟ್‌ಗಳ ಜಗತ್ತಿಗೆ ಸ್ವಾಗತ.

ಈ ರೀತಿಯ ನೆಟ್‌ಲಿಂಕ್ ಸಾಕೆಟ್ ತೆರೆಯಿರಿ NETLINK_ROUTE:

int netlink_open(__u32 *nl_pid)
{
    struct sockaddr_nl sa;
    socklen_t addrlen;
    int one = 1, ret;
    int sock;

    memset(&sa, 0, sizeof(sa));
    sa.nl_family = AF_NETLINK;

    sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    if (sock < 0)
        err(1, "socket");

    if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK, &one, sizeof(one)) < 0)
        warnx("netlink error reporting not supported");

    if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0)
        err(1, "bind");

    addrlen = sizeof(sa);
    if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0)
        err(1, "getsockname");

    *nl_pid = sa.nl_pid;
    return sock;
}

ಅಂತಹ ಸಾಕೆಟ್‌ನಿಂದ ನಾವು ಓದುತ್ತೇವೆ:

static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq)
{
    bool multipart = true;
    struct nlmsgerr *errm;
    struct nlmsghdr *nh;
    char buf[4096];
    int len, ret;

    while (multipart) {
        multipart = false;
        len = recv(sock, buf, sizeof(buf), 0);
        if (len < 0)
            err(1, "recv");

        if (len == 0)
            break;

        for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len);
                nh = NLMSG_NEXT(nh, len)) {
            if (nh->nlmsg_pid != nl_pid)
                errx(1, "wrong pid");
            if (nh->nlmsg_seq != seq)
                errx(1, "INVSEQ");
            if (nh->nlmsg_flags & NLM_F_MULTI)
                multipart = true;
            switch (nh->nlmsg_type) {
                case NLMSG_ERROR:
                    errm = (struct nlmsgerr *)NLMSG_DATA(nh);
                    if (!errm->error)
                        continue;
                    ret = errm->error;
                    // libbpf_nla_dump_errormsg(nh); too many code to copy...
                    goto done;
                case NLMSG_DONE:
                    return 0;
                default:
                    break;
            }
        }
    }
    ret = 0;
done:
    return ret;
}

ಕೊನೆಯದಾಗಿ, ಸಾಕೆಟ್ ಅನ್ನು ತೆರೆಯುವ ಮತ್ತು ಅದಕ್ಕೆ ಫೈಲ್ ಡಿಸ್ಕ್ರಿಪ್ಟರ್ ಅನ್ನು ಹೊಂದಿರುವ ವಿಶೇಷ ಸಂದೇಶವನ್ನು ಕಳುಹಿಸುವ ನಮ್ಮ ಕಾರ್ಯ ಇಲ್ಲಿದೆ:

static int xdp_attach(int ifindex, int prog_fd)
{
    int sock, seq = 0, ret;
    struct nlattr *nla, *nla_xdp;
    struct {
        struct nlmsghdr  nh;
        struct ifinfomsg ifinfo;
        char             attrbuf[64];
    } req;
    __u32 nl_pid = 0;

    sock = netlink_open(&nl_pid);
    if (sock < 0)
        return sock;

    memset(&req, 0, sizeof(req));
    req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
    req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
    req.nh.nlmsg_type = RTM_SETLINK;
    req.nh.nlmsg_pid = 0;
    req.nh.nlmsg_seq = ++seq;
    req.ifinfo.ifi_family = AF_UNSPEC;
    req.ifinfo.ifi_index = ifindex;

    /* started nested attribute for XDP */
    nla = (struct nlattr *)(((char *)&req)
            + NLMSG_ALIGN(req.nh.nlmsg_len));
    nla->nla_type = NLA_F_NESTED | IFLA_XDP;
    nla->nla_len = NLA_HDRLEN;

    /* add XDP fd */
    nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
    nla_xdp->nla_type = IFLA_XDP_FD;
    nla_xdp->nla_len = NLA_HDRLEN + sizeof(int);
    memcpy((char *)nla_xdp + NLA_HDRLEN, &prog_fd, sizeof(prog_fd));
    nla->nla_len += nla_xdp->nla_len;

    /* if user passed in any flags, add those too */
    __u32 flags = XDP_FLAGS_SKB_MODE;
    nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
    nla_xdp->nla_type = IFLA_XDP_FLAGS;
    nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags);
    memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags));
    nla->nla_len += nla_xdp->nla_len;

    req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len);

    if (send(sock, &req, req.nh.nlmsg_len, 0) < 0)
        err(1, "send");
    ret = bpf_netlink_recv(sock, nl_pid, seq);

cleanup:
    close(sock);
    return ret;
}

ಆದ್ದರಿಂದ, ಪರೀಕ್ಷೆಗೆ ಎಲ್ಲವೂ ಸಿದ್ಧವಾಗಿದೆ:

$ cc nolibbpf.c -o nolibbpf
$ sudo strace -e bpf ./nolibbpf
bpf(BPF_MAP_CREATE, {map_type=BPF_MAP_TYPE_ARRAY, map_name="woo", ...}, 72) = 3
bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_XDP, insn_cnt=15, prog_name="woo", ...}, 72) = 4
+++ exited with 0 +++

ನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ಸಂಪರ್ಕಗೊಂಡಿದೆಯೇ ಎಂದು ನೋಡೋಣ lo:

$ ip l show dev lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 xdpgeneric qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    prog/xdp id 160

ಪಿಂಗ್‌ಗಳನ್ನು ಕಳುಹಿಸೋಣ ಮತ್ತು ನಕ್ಷೆಯನ್ನು ನೋಡೋಣ:

$ for s in `seq 234`; do sudo ping -f -c 100 127.0.0.1 >/dev/null 2>&1; done
$ sudo bpftool m dump name woo
key: 00 00 00 00  value: 90 01 00 00 00 00 00 00
key: 01 00 00 00  value: 00 00 00 00 00 00 00 00
key: 02 00 00 00  value: 00 00 00 00 00 00 00 00
key: 03 00 00 00  value: 00 00 00 00 00 00 00 00
key: 04 00 00 00  value: 00 00 00 00 00 00 00 00
key: 05 00 00 00  value: 00 00 00 00 00 00 00 00
key: 06 00 00 00  value: 40 b5 00 00 00 00 00 00
key: 07 00 00 00  value: 00 00 00 00 00 00 00 00
Found 8 elements

ಹುರ್ರೇ, ಎಲ್ಲವೂ ಕೆಲಸ ಮಾಡುತ್ತದೆ. ಗಮನಿಸಿ, ನಮ್ಮ ನಕ್ಷೆಯನ್ನು ಮತ್ತೆ ಬೈಟ್‌ಗಳಾಗಿ ಪ್ರದರ್ಶಿಸಲಾಗಿದೆ. ಇದು ಏಕೆಂದರೆ, ಭಿನ್ನವಾಗಿ libbpf ನಾವು ಪ್ರಕಾರದ ಮಾಹಿತಿಯನ್ನು (BTF) ಲೋಡ್ ಮಾಡಿಲ್ಲ. ಆದರೆ ಮುಂದಿನ ಬಾರಿ ಅದರ ಬಗ್ಗೆ ಹೆಚ್ಚು ವಿವರವಾಗಿ ಮಾತನಾಡುತ್ತೇವೆ.

ಅಭಿವೃದ್ಧಿ ಪರಿಕರಗಳು

ಈ ವಿಭಾಗದಲ್ಲಿ ನಾವು BPF ಡೆವಲಪರ್‌ಗೆ ಕನಿಷ್ಠ ಟೂಲ್‌ಕಿಟ್ ಅನ್ನು ನೋಡುತ್ತೇವೆ.

ಸಾಮಾನ್ಯವಾಗಿ ಹೇಳುವುದಾದರೆ, ಬಿಪಿಎಫ್ ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಅಭಿವೃದ್ಧಿಪಡಿಸಲು ನಿಮಗೆ ವಿಶೇಷವಾದ ಏನೂ ಅಗತ್ಯವಿಲ್ಲ - ಬಿಪಿಎಫ್ ಯಾವುದೇ ಯೋಗ್ಯ ವಿತರಣಾ ಕರ್ನಲ್‌ನಲ್ಲಿ ಚಲಿಸುತ್ತದೆ ಮತ್ತು ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಬಳಸಿ ನಿರ್ಮಿಸಲಾಗುತ್ತದೆ clang, ಇದನ್ನು ಪ್ಯಾಕೇಜ್‌ನಿಂದ ಸ್ಥಾಪಿಸಬಹುದು. ಆದಾಗ್ಯೂ, BPF ಅಭಿವೃದ್ಧಿಯ ಪ್ರಕ್ರಿಯೆಯಲ್ಲಿರುವುದರಿಂದ, ಕೋರ್ ಮತ್ತು ಪರಿಕರಗಳು ನಿರಂತರವಾಗಿ ಬದಲಾಗುತ್ತಿವೆ, 2019 ರ ದಶಕದಿಂದ ಹಳೆಯ-ಶೈಲಿಯ ವಿಧಾನಗಳನ್ನು ಬಳಸಿಕೊಂಡು ನೀವು BPF ಕಾರ್ಯಕ್ರಮಗಳನ್ನು ಬರೆಯಲು ಬಯಸದಿದ್ದರೆ, ನೀವು ನಿರ್ಮಿಸಬೇಕಾಗುತ್ತದೆ

  • llvm/clang
  • pahole
  • ನಿಮ್ಮ ಮೂಲ
  • bpftool

(ಉಲ್ಲೇಖಕ್ಕಾಗಿ: ಈ ವಿಭಾಗ ಮತ್ತು ಲೇಖನದಲ್ಲಿನ ಎಲ್ಲಾ ಉದಾಹರಣೆಗಳನ್ನು ಡೆಬಿಯನ್ 10 ರಲ್ಲಿ ನಡೆಸಲಾಗಿದೆ.)

llvm/ಕ್ಲಾಂಗ್

BPF LLVM ಸ್ನೇಹಿಯಾಗಿದೆ ಮತ್ತು BPF ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಈಗ gcc ನೊಂದಿಗೆ ಸಂಕಲಿಸಬಹುದಾದರೂ, ಪ್ರಸ್ತುತ ಎಲ್ಲಾ ಅಭಿವೃದ್ಧಿಯನ್ನು LLVM ಗಾಗಿ ಮಾಡಲಾಗುತ್ತದೆ. ಆದ್ದರಿಂದ ಮೊದಲು ನಾವು ಪ್ರಸ್ತುತ ಆವೃತ್ತಿಯನ್ನು ನಿರ್ಮಿಸುತ್ತೇವೆ. clang ಗಿಟ್ ನಿಂದ:

$ sudo apt install ninja-build
$ git clone --depth 1 https://github.com/llvm/llvm-project.git
$ mkdir -p llvm-project/llvm/build/install
$ cd llvm-project/llvm/build
$ cmake .. -G "Ninja" -DLLVM_TARGETS_TO_BUILD="BPF;X86" 
                      -DLLVM_ENABLE_PROJECTS="clang" 
                      -DBUILD_SHARED_LIBS=OFF 
                      -DCMAKE_BUILD_TYPE=Release 
                      -DLLVM_BUILD_RUNTIME=OFF
$ time ninja
... много времени спустя
$

ಈಗ ನಾವು ಎಲ್ಲವನ್ನೂ ಸರಿಯಾಗಿ ಜೋಡಿಸಲಾಗಿದೆಯೇ ಎಂದು ಪರಿಶೀಲಿಸಬಹುದು:

$ ./bin/llc --version
LLVM (http://llvm.org/):
  LLVM version 11.0.0git
  Optimized build.
  Default target: x86_64-unknown-linux-gnu
  Host CPU: znver1

  Registered Targets:
    bpf    - BPF (host endian)
    bpfeb  - BPF (big endian)
    bpfel  - BPF (little endian)
    x86    - 32-bit X86: Pentium-Pro and above
    x86-64 - 64-bit X86: EM64T and AMD64

(ಅಸೆಂಬ್ಲಿ ಸೂಚನೆಗಳು clang ನಾನು ತೆಗೆದುಕೊಂಡದ್ದು ಬಿಪಿಎಫ್_ಡೆವೆಲ್_ಕ್ಯೂಎ.)

ನಾವು ಈಗಷ್ಟೇ ನಿರ್ಮಿಸಿದ ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಸ್ಥಾಪಿಸುವುದಿಲ್ಲ, ಬದಲಿಗೆ ಅವುಗಳನ್ನು ಸೇರಿಸುತ್ತೇವೆ PATH, ಉದಾಹರಣೆಗೆ:

export PATH="`pwd`/bin:$PATH"

(ಇದನ್ನು ಸೇರಿಸಬಹುದು .bashrc ಅಥವಾ ಪ್ರತ್ಯೇಕ ಫೈಲ್‌ನಲ್ಲಿ. ವೈಯಕ್ತಿಕವಾಗಿ, ನಾನು ಅಂತಹ ವಿಷಯಗಳನ್ನು ಸೇರಿಸುತ್ತೇನೆ ~/bin/activate-llvm.sh ಮತ್ತು ಅಗತ್ಯವಿದ್ದಾಗ ನಾನು ಅದನ್ನು ಮಾಡುತ್ತೇನೆ . activate-llvm.sh.)

ಪಹೋಲ್ ಮತ್ತು ಬಿಟಿಎಫ್

ಉಪಯುಕ್ತತೆ pahole BTF ಸ್ವರೂಪದಲ್ಲಿ ಡೀಬಗ್ ಮಾಹಿತಿಯನ್ನು ರಚಿಸಲು ಕರ್ನಲ್ ಅನ್ನು ನಿರ್ಮಿಸುವಾಗ ಬಳಸಲಾಗುತ್ತದೆ. ಈ ಲೇಖನದಲ್ಲಿ ನಾವು BTF ತಂತ್ರಜ್ಞಾನದ ವಿವರಗಳಿಗೆ ಹೋಗುವುದಿಲ್ಲ, ಅದು ಅನುಕೂಲಕರವಾಗಿದೆ ಮತ್ತು ನಾವು ಅದನ್ನು ಬಳಸಲು ಬಯಸುತ್ತೇವೆ ಎಂಬ ಅಂಶವನ್ನು ಹೊರತುಪಡಿಸಿ. ಆದ್ದರಿಂದ ನೀವು ನಿಮ್ಮ ಸ್ವಂತ ಕರ್ನಲ್ ಅನ್ನು ನಿರ್ಮಿಸಲು ಹೋದರೆ, ಮೊದಲು ನಿರ್ಮಿಸಿ pahole (ಇಲ್ಲದೆ pahole ಈ ಆಯ್ಕೆಯೊಂದಿಗೆ ನೀವು ಕರ್ನಲ್ ಅನ್ನು ನಿರ್ಮಿಸಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ. CONFIG_DEBUG_INFO_BTF:

$ git clone https://git.kernel.org/pub/scm/devel/pahole/pahole.git
$ cd pahole/
$ sudo apt install cmake
$ mkdir build
$ cd build/
$ cmake -D__LIB=lib ..
$ make
$ sudo make install
$ which pahole
/usr/local/bin/pahole

ಬಿಪಿಎಫ್ ಪ್ರಯೋಗಗಳಿಗೆ ಕೋರ್‌ಗಳು

BPF ನ ಸಾಮರ್ಥ್ಯಗಳನ್ನು ಅನ್ವೇಷಿಸುವಾಗ, ನೀವು ನಿಮ್ಮ ಸ್ವಂತ ಕರ್ನಲ್ ಅನ್ನು ನಿರ್ಮಿಸಲು ಬಯಸುತ್ತೀರಿ. ಇದು ನಿಜವಾಗಿಯೂ ಅಗತ್ಯವಿಲ್ಲ, ಏಕೆಂದರೆ ನೀವು ವಿತರಣಾ ಕರ್ನಲ್‌ನಲ್ಲಿ BPF ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ನಿರ್ಮಿಸಬಹುದು ಮತ್ತು ಲೋಡ್ ಮಾಡಬಹುದು, ಆದರೆ ನಿಮ್ಮ ಸ್ವಂತ ಕರ್ನಲ್ ಹೊಂದಿರುವುದು ತಿಂಗಳುಗಳಲ್ಲಿ ಉತ್ತಮ ಸಂದರ್ಭದಲ್ಲಿ ನಿಮ್ಮ ವಿತರಣೆಯಲ್ಲಿ ಕಾಣಿಸಿಕೊಳ್ಳುವ ಹೊಸ BPF ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಬಳಸಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ ಅಥವಾ ಕೆಲವು ಡೀಬಗ್ ಮಾಡುವ ಪರಿಕರಗಳಂತೆ, ನಿರೀಕ್ಷಿತ ಭವಿಷ್ಯದಲ್ಲಿ ಪ್ಯಾಕ್ ಮಾಡಲಾಗುವುದಿಲ್ಲ. ಅಲ್ಲದೆ, ನಿಮ್ಮ ಸ್ವಂತ ಕರ್ನಲ್ ಕೋಡ್‌ನೊಂದಿಗೆ ಪ್ರಯೋಗಿಸಲು ನಿಮಗೆ ಮುಖ್ಯವೆಂದು ಭಾವಿಸುತ್ತದೆ.

ಕರ್ನಲ್ ಅನ್ನು ನಿರ್ಮಿಸಲು ನಿಮಗೆ ಮೊದಲು ಕರ್ನಲ್ ಅಗತ್ಯವಿದೆ, ಮತ್ತು ಎರಡನೆಯದಾಗಿ ಕರ್ನಲ್ ಕಾನ್ಫಿಗರೇಶನ್ ಫೈಲ್ ಅಗತ್ಯವಿದೆ. BPF ನ ಪ್ರಯೋಗಗಳಿಗಾಗಿ ನಾವು ಸಾಮಾನ್ಯವಾದ ವೆನಿಲ್ಲಾ ಕರ್ನಲ್ ಅಥವಾ ಡೆವಲಪರ್ ಕರ್ನಲ್‌ಗಳಲ್ಲಿ ಒಂದು. ಐತಿಹಾಸಿಕವಾಗಿ, BPF ಅಭಿವೃದ್ಧಿಯು ಲಿನಕ್ಸ್ ನೆಟ್‌ವರ್ಕಿಂಗ್ ಸಮುದಾಯದಲ್ಲಿ ನಡೆಯುತ್ತಿದೆ, ಮತ್ತು ಆದ್ದರಿಂದ ಎಲ್ಲಾ ಬದಲಾವಣೆಗಳು ಬೇಗ ಅಥವಾ ನಂತರ ಲಿನಕ್ಸ್ ನೆಟ್‌ವರ್ಕಿಂಗ್ ನಿರ್ವಾಹಕ ಡೇವಿಡ್ ಮಿಲ್ಲರ್ ಮೂಲಕ ಹೋಗುತ್ತವೆ. ಅವುಗಳ ಸ್ವರೂಪವನ್ನು ಅವಲಂಬಿಸಿ - ಪರಿಹಾರಗಳು ಅಥವಾ ಹೊಸ ವೈಶಿಷ್ಟ್ಯಗಳು - ನೆಟ್‌ವರ್ಕ್ ಬದಲಾವಣೆಗಳು ಎರಡು ಕರ್ನಲ್‌ಗಳಲ್ಲಿ ಒಂದರಲ್ಲಿ ಕೊನೆಗೊಳ್ಳುತ್ತವೆ - net ಅಥವಾ net-next. ಬಿಪಿಎಫ್‌ಗೆ ಬದಲಾವಣೆಗಳನ್ನು ಅದೇ ರೀತಿಯಲ್ಲಿ ವಿತರಿಸಲಾಗುತ್ತದೆ bpf и bpf-next, ಇವುಗಳನ್ನು ಕ್ರಮವಾಗಿ ನೆಟ್ ಮತ್ತು ನೆಟ್-ನೆಕ್ಸ್ಟ್ ಆಗಿ ಒಟ್ಟುಗೂಡಿಸಲಾಗುತ್ತದೆ. ಹೆಚ್ಚಿನ ವಿವರಗಳಿಗಾಗಿ, ನೋಡಿ ಬಿಪಿಎಫ್_ಡೆವೆಲ್_ಕ್ಯೂಎ и ನೆಟ್‌ಡೆವ್-ಪದೇ ಪದೇ ಕೇಳಲಾಗುವ ಪ್ರಶ್ನೆಗಳು. ಆದ್ದರಿಂದ ನಿಮ್ಮ ಅಭಿರುಚಿ ಮತ್ತು ನೀವು ಪರೀಕ್ಷಿಸುತ್ತಿರುವ ವ್ಯವಸ್ಥೆಯ ಸ್ಥಿರತೆಯ ಅಗತ್ಯಗಳನ್ನು ಆಧರಿಸಿ ಕರ್ನಲ್ ಅನ್ನು ಆರಿಸಿ (*-next ಪಟ್ಟಿ ಮಾಡಲಾದವುಗಳಲ್ಲಿ ನ್ಯೂಕ್ಲಿಯಸ್‌ಗಳು ಅತ್ಯಂತ ಅಸ್ಥಿರವಾಗಿವೆ).

ಕರ್ನಲ್ ಕಾನ್ಫಿಗರೇಶನ್ ಫೈಲ್‌ಗಳನ್ನು ಹೇಗೆ ನಿರ್ವಹಿಸುವುದು ಎಂಬುದನ್ನು ಒಳಗೊಳ್ಳುವುದು ಈ ಲೇಖನದ ವ್ಯಾಪ್ತಿಯನ್ನು ಮೀರಿದೆ - ಇದನ್ನು ಹೇಗೆ ಮಾಡಬೇಕೆಂದು ನಿಮಗೆ ಈಗಾಗಲೇ ತಿಳಿದಿದೆ ಎಂದು ಭಾವಿಸಲಾಗಿದೆ ಅಥವಾ ಕಲಿಯಲು ಸಿದ್ಧ ಆದಾಗ್ಯೂ, ಈ ಕೆಳಗಿನ ಸೂಚನೆಗಳು ನಿಮ್ಮನ್ನು BPF ಬೆಂಬಲದೊಂದಿಗೆ ಚಲಾಯಿಸಲು ಹೆಚ್ಚು ಕಡಿಮೆ ಸಾಕಾಗುತ್ತದೆ.

ಮೇಲೆ ತಿಳಿಸಿದ ಕರ್ನಲ್‌ಗಳಲ್ಲಿ ಒಂದನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡಿ:

$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git
$ cd bpf-next

ಕನಿಷ್ಠ ಕೆಲಸ ಮಾಡುವ ಕರ್ನಲ್ ಸಂರಚನೆಯನ್ನು ನಿರ್ಮಿಸಿ:

$ cp /boot/config-`uname -r` .config
$ make localmodconfig

ಫೈಲ್‌ನಲ್ಲಿ ಬಿಪಿಎಫ್ ಆಯ್ಕೆಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ .config ಆಯ್ಕೆಯಿಂದ (ಹೆಚ್ಚಾಗಿ, ಸ್ವತಃ CONFIG_BPF systemd ಬಳಸುವುದರಿಂದ, ಈಗಾಗಲೇ ಸಕ್ರಿಯಗೊಳಿಸಲಾಗುತ್ತದೆ). ಈ ಲೇಖನಕ್ಕಾಗಿ ಬಳಸಲಾದ ಕರ್ನಲ್‌ನಿಂದ ಆಯ್ಕೆಗಳ ಪಟ್ಟಿ ಇಲ್ಲಿದೆ:

CONFIG_CGROUP_BPF=y
CONFIG_BPF=y
CONFIG_BPF_LSM=y
CONFIG_BPF_SYSCALL=y
CONFIG_ARCH_WANT_DEFAULT_BPF_JIT=y
CONFIG_BPF_JIT_ALWAYS_ON=y
CONFIG_BPF_JIT_DEFAULT_ON=y
CONFIG_IPV6_SEG6_BPF=y
# CONFIG_NETFILTER_XT_MATCH_BPF is not set
# CONFIG_BPFILTER is not set
CONFIG_NET_CLS_BPF=y
CONFIG_NET_ACT_BPF=y
CONFIG_BPF_JIT=y
CONFIG_BPF_STREAM_PARSER=y
CONFIG_LWTUNNEL_BPF=y
CONFIG_HAVE_EBPF_JIT=y
CONFIG_BPF_EVENTS=y
CONFIG_BPF_KPROBE_OVERRIDE=y
CONFIG_DEBUG_INFO_BTF=y

ಮುಂದೆ ನಾವು ಮಾಡ್ಯೂಲ್‌ಗಳು ಮತ್ತು ಕರ್ನಲ್ ಅನ್ನು ಸುಲಭವಾಗಿ ನಿರ್ಮಿಸಬಹುದು ಮತ್ತು ಸ್ಥಾಪಿಸಬಹುದು (ಅಂದಹಾಗೆ, ನೀವು ಈಗಷ್ಟೇ ನಿರ್ಮಿಸಿದ ಒಂದನ್ನು ಬಳಸಿಕೊಂಡು ನೀವು ಕರ್ನಲ್ ಅನ್ನು ನಿರ್ಮಿಸಬಹುದು) clang, ಸೇರಿಸಲಾಗುತ್ತಿದೆ CC=clang):

$ make -s -j $(getconf _NPROCESSORS_ONLN)
$ sudo make modules_install
$ sudo make install

ಮತ್ತು ಹೊಸ ಕರ್ನಲ್‌ನೊಂದಿಗೆ ರೀಬೂಟ್ ಮಾಡಿ (ನಾನು ಇದಕ್ಕಾಗಿ ಬಳಸುತ್ತೇನೆ kexec ಪ್ಯಾಕೇಜ್‌ನಿಂದ kexec-tools):

v=5.8.0-rc6+ # если вы пересобираете текущее ядро, то можно делать v=`uname -r`
sudo kexec -l -t bzImage /boot/vmlinuz-$v --initrd=/boot/initrd.img-$v --reuse-cmdline &&
sudo kexec -e

ಬಿಪಿಎಫ್‌ಟೂಲ್

ಲೇಖನದಲ್ಲಿ ಹೆಚ್ಚಾಗಿ ಬಳಸುವ ಉಪಯುಕ್ತತೆಯು ಉಪಯುಕ್ತತೆಯಾಗಿದೆ bpftool, ಲಿನಕ್ಸ್ ಕರ್ನಲ್‌ನೊಂದಿಗೆ ಸರಬರಾಜು ಮಾಡಲಾಗಿದೆ. ಇದನ್ನು BPF ಡೆವಲಪರ್‌ಗಳಿಗಾಗಿ BPF ಡೆವಲಪರ್‌ಗಳು ಬರೆಯುತ್ತಾರೆ ಮತ್ತು ನಿರ್ವಹಿಸುತ್ತಾರೆ ಮತ್ತು ಎಲ್ಲಾ ರೀತಿಯ BPF ವಸ್ತುಗಳನ್ನು ನಿರ್ವಹಿಸಲು ಬಳಸಬಹುದು - ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಲೋಡ್ ಮಾಡುವುದು, ನಕ್ಷೆಗಳನ್ನು ರಚಿಸುವುದು ಮತ್ತು ಮಾರ್ಪಡಿಸುವುದು, BPF ಪರಿಸರ ವ್ಯವಸ್ಥೆಯ ಜೀವನವನ್ನು ಅನ್ವೇಷಿಸುವುದು ಇತ್ಯಾದಿ. ಮ್ಯಾನ್ ಪುಟಗಳಿಗಾಗಿ ಮೂಲ ಕೋಡ್ ರೂಪದಲ್ಲಿ ದಾಖಲಾತಿಯನ್ನು ಕಾಣಬಹುದು. ಮೂಲದಲ್ಲಿ ಅಥವಾ, ಈಗಾಗಲೇ ಸಂಕಲಿಸಲಾಗಿದೆ, ನಿವ್ವಳದಲ್ಲಿ.

ಈ ಬರಹದ ಸಮಯದಲ್ಲಿ bpftool RHEL, ಫೆಡೋರಾ ಮತ್ತು ಉಬುಂಟುಗಳಿಗೆ ಮಾತ್ರ ಸಿದ್ಧವಾಗಿ ಬರುತ್ತದೆ (ಉದಾಹರಣೆಗೆ, ನೋಡಿ, ಈ ಥ್ರೆಡ್, ಇದು ಪ್ಯಾಕೇಜಿಂಗ್‌ನ ಅಪೂರ್ಣ ಕಥೆಯನ್ನು ಹೇಳುತ್ತದೆ bpftool ಡೆಬಿಯನ್ ಭಾಷೆಯಲ್ಲಿ). ಆದರೆ ನೀವು ಈಗಾಗಲೇ ನಿಮ್ಮ ಕರ್ನಲ್ ಅನ್ನು ಕಂಪೈಲ್ ಮಾಡಿದ್ದರೆ, ನೀವು ಕಂಪೈಲ್ ಮಾಡಬಹುದು bpftool ಇದು ಪೈನಷ್ಟು ಸುಲಭ:

$ cd ${linux}/tools/bpf/bpftool
# ... пропишите пути к последнему clang, как рассказано выше
$ make -s

Auto-detecting system features:
...                        libbfd: [ on  ]
...        disassembler-four-args: [ on  ]
...                          zlib: [ on  ]
...                        libcap: [ on  ]
...               clang-bpf-co-re: [ on  ]

Auto-detecting system features:
...                        libelf: [ on  ]
...                          zlib: [ on  ]
...                           bpf: [ on  ]

$

(ಇಲ್ಲಿ ${linux} — ಇದು ನಿಮ್ಮ ಕರ್ನಲ್ ಡೈರೆಕ್ಟರಿ.) ಈ ಆಜ್ಞೆಗಳನ್ನು ಚಲಾಯಿಸಿದ ನಂತರ bpftool ಡೈರೆಕ್ಟರಿಯಲ್ಲಿ ಸಂಗ್ರಹಿಸಲಾಗುತ್ತದೆ ${linux}/tools/bpf/bpftool ಮತ್ತು ಅದನ್ನು ಮಾರ್ಗದಲ್ಲಿ ಬರೆಯಬಹುದು (ಮೊದಲನೆಯದಾಗಿ ಬಳಕೆದಾರರಿಗೆ root) ಅಥವಾ ಅದನ್ನು ನಕಲಿಸಿ /usr/local/sbin.

ಸಂಗ್ರಹಿಸಿ bpftool ಎರಡನೆಯದರೊಂದಿಗೆ ಉತ್ತಮ clang, ಮೇಲೆ ವಿವರಿಸಿದಂತೆ ಜೋಡಿಸಲಾಗಿದೆ, ಮತ್ತು ಅದನ್ನು ಸರಿಯಾಗಿ ಜೋಡಿಸಲಾಗಿದೆಯೇ ಎಂದು ಪರಿಶೀಲಿಸಿ, ಉದಾಹರಣೆಗೆ, ಆಜ್ಞೆಯನ್ನು ಬಳಸಿ

$ sudo bpftool feature probe kernel
Scanning system configuration...
bpf() syscall for unprivileged users is enabled
JIT compiler is enabled
JIT compiler hardening is disabled
JIT compiler kallsyms exports are enabled for root
...

ಇದು ನಿಮ್ಮ ಕರ್ನಲ್‌ನಲ್ಲಿ ಯಾವ BPF ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ ಎಂಬುದನ್ನು ತೋರಿಸುತ್ತದೆ.

ಅಂದಹಾಗೆ, ಹಿಂದಿನ ಆಜ್ಞೆಯನ್ನು ಹೀಗೆ ಚಲಾಯಿಸಬಹುದು

# bpftool f p k

ಪ್ಯಾಕೇಜ್‌ನ ಉಪಯುಕ್ತತೆಗಳಂತೆಯೇ ಇದನ್ನು ಮಾಡಲಾಗುತ್ತದೆ. iproute2, ಉದಾಹರಣೆಗೆ, ನಾವು ಹೇಳಬಹುದಾದಲ್ಲಿ ip a s eth0 вместо ip addr show dev eth0.

ತೀರ್ಮಾನಕ್ಕೆ

BPF ನಿಮಗೆ ಕರ್ನಲ್‌ನ ಕಾರ್ಯವನ್ನು ಪರಿಣಾಮಕಾರಿಯಾಗಿ ಅಳೆಯಲು ಮತ್ತು ಬದಲಾಯಿಸಲು ಒಂದು ಚಿಗಟವನ್ನು ಶೂ ಮಾಡಲು ಅನುಮತಿಸುತ್ತದೆ. UNIX ನ ಅತ್ಯುತ್ತಮ ಸಂಪ್ರದಾಯಗಳಲ್ಲಿ ಈ ವ್ಯವಸ್ಥೆಯು ಅತ್ಯಂತ ಯಶಸ್ವಿಯಾಗಿದೆ: ಕರ್ನಲ್ ಅನ್ನು (ಮರು) ಪ್ರೋಗ್ರಾಮಿಂಗ್ ಮಾಡಲು ಅನುಮತಿಸುವ ಒಂದು ಸರಳ ಕಾರ್ಯವಿಧಾನವು ಹೆಚ್ಚಿನ ಸಂಖ್ಯೆಯ ಜನರು ಮತ್ತು ಸಂಸ್ಥೆಗಳಿಗೆ ಪ್ರಯೋಗ ಮಾಡಲು ಅವಕಾಶ ಮಾಡಿಕೊಟ್ಟಿತು. ಮತ್ತು, ಪ್ರಯೋಗಗಳು, ಹಾಗೆಯೇ BPF ಮೂಲಸೌಕರ್ಯದ ಅಭಿವೃದ್ಧಿಯು ಪೂರ್ಣವಾಗಿಲ್ಲದಿದ್ದರೂ, ವ್ಯವಸ್ಥೆಯು ಈಗಾಗಲೇ ಸ್ಥಿರವಾದ ABI ಅನ್ನು ಹೊಂದಿದ್ದು, ಇದು ನಿಮಗೆ ವಿಶ್ವಾಸಾರ್ಹ ಮತ್ತು ಮುಖ್ಯವಾಗಿ, ಪರಿಣಾಮಕಾರಿ ವ್ಯವಹಾರ ತರ್ಕವನ್ನು ನಿರ್ಮಿಸಲು ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ.

ನನ್ನ ಅಭಿಪ್ರಾಯದಲ್ಲಿ ತಂತ್ರಜ್ಞಾನವು ತುಂಬಾ ಜನಪ್ರಿಯವಾಗಿದೆ ಏಕೆಂದರೆ, ಒಂದೆಡೆ, ಇದನ್ನು ಬಳಸಬಹುದು ಎಂಬುದನ್ನು ನಾನು ಗಮನಿಸಲು ಬಯಸುತ್ತೇನೆ ಆಡಲು (ಯಂತ್ರದ ವಾಸ್ತುಶಿಲ್ಪವನ್ನು ಒಂದು ಸಂಜೆಯಲ್ಲಿ ಹೆಚ್ಚು ಕಡಿಮೆ ಅರ್ಥಮಾಡಿಕೊಳ್ಳಬಹುದು), ಮತ್ತು ಮತ್ತೊಂದೆಡೆ, ಅದು ಕಾಣಿಸಿಕೊಳ್ಳುವ ಮೊದಲು ಪರಿಹರಿಸಲಾಗದ ಸಮಸ್ಯೆಗಳನ್ನು (ಸುಂದರವಾಗಿ) ಪರಿಹರಿಸಲು. ಈ ಎರಡು ಘಟಕಗಳು ಒಟ್ಟಾಗಿ ಜನರನ್ನು ಪ್ರಯೋಗ ಮತ್ತು ಕನಸು ಕಾಣುವಂತೆ ಮಾಡುತ್ತದೆ, ಇದು ಹೆಚ್ಚು ಹೆಚ್ಚು ಹೊಸ ನವೀನ ಪರಿಹಾರಗಳ ಹೊರಹೊಮ್ಮುವಿಕೆಗೆ ಕಾರಣವಾಗುತ್ತದೆ.

ಈ ಲೇಖನವು, ನಿರ್ದಿಷ್ಟವಾಗಿ ಚಿಕ್ಕದಲ್ಲದಿದ್ದರೂ, BPF ಪ್ರಪಂಚದ ಪರಿಚಯ ಮಾತ್ರ ಮತ್ತು "ಸುಧಾರಿತ" ವೈಶಿಷ್ಟ್ಯಗಳು ಮತ್ತು ವಾಸ್ತುಶಿಲ್ಪದ ಪ್ರಮುಖ ಭಾಗಗಳನ್ನು ಒಳಗೊಂಡಿಲ್ಲ. ಮುಂದಿನ ಯೋಜನೆ ಸರಿಸುಮಾರು ಈ ಕೆಳಗಿನಂತಿರುತ್ತದೆ: ಮುಂದಿನ ಲೇಖನವು BPF ಪ್ರೋಗ್ರಾಂ ಪ್ರಕಾರಗಳ ಅವಲೋಕನವಾಗಿರುತ್ತದೆ (5.8 ಕರ್ನಲ್‌ನಲ್ಲಿ 30 ಪ್ರೋಗ್ರಾಂ ಪ್ರಕಾರಗಳನ್ನು ಬೆಂಬಲಿಸಲಾಗುತ್ತದೆ), ನಂತರ ನಾವು ಅಂತಿಮವಾಗಿ ಕರ್ನಲ್ ಟ್ರೇಸಿಂಗ್ ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಉದಾಹರಣೆಯಾಗಿ ಬಳಸಿಕೊಂಡು ನಿಜವಾದ BPF ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಹೇಗೆ ಬರೆಯುವುದು ಎಂದು ನೋಡೋಣ, ನಂತರ BPF ಆರ್ಕಿಟೆಕ್ಚರ್ ಕುರಿತು ಹೆಚ್ಚು ಆಳವಾದ ಕೋರ್ಸ್‌ಗೆ ಸಮಯ ಬರುತ್ತದೆ ಮತ್ತು ನಂತರ - ನೆಟ್‌ವರ್ಕ್ ಮತ್ತು ಭದ್ರತಾ BPF ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಉದಾಹರಣೆಗಳಿಗಾಗಿ.

ಈ ಸರಣಿಯಲ್ಲಿನ ಹಿಂದಿನ ಲೇಖನಗಳು

  1. ಚಿಕ್ಕ ಮಕ್ಕಳಿಗೆ BPF, ಭಾಗ ಶೂನ್ಯ: ಕ್ಲಾಸಿಕ್ BPF

ಲಿಂಕ್‌ಗಳು

  1. BPF ಮತ್ತು XDP ಉಲ್ಲೇಖ ಮಾರ್ಗದರ್ಶಿ — ಸಿಲಿಯಮ್‌ನಿಂದ ಬಿಪಿಎಫ್ ದಸ್ತಾವೇಜನ್ನು, ಅಥವಾ ಹೆಚ್ಚು ನಿಖರವಾಗಿ ಬಿಪಿಎಫ್‌ನ ಸೃಷ್ಟಿಕರ್ತರು ಮತ್ತು ನಿರ್ವಾಹಕರಲ್ಲಿ ಒಬ್ಬರಾದ ಡೇನಿಯಲ್ ಬೋರ್ಕ್‌ಮನ್ ಅವರಿಂದ. ಇದು ಮೊದಲ ಗಂಭೀರ ವಿವರಣೆಗಳಲ್ಲಿ ಒಂದಾಗಿದೆ, ಇದು ಇತರರಿಗಿಂತ ಭಿನ್ನವಾಗಿದೆ ಏಕೆಂದರೆ ಡೇನಿಯಲ್ ತಾನು ಏನು ಬರೆಯುತ್ತಿದ್ದೇನೆಂದು ನಿಖರವಾಗಿ ತಿಳಿದಿದ್ದಾನೆ ಮತ್ತು ಅಲ್ಲಿ ಯಾವುದೇ ಪ್ರಮಾದಗಳಿಲ್ಲ. ನಿರ್ದಿಷ್ಟವಾಗಿ ಹೇಳುವುದಾದರೆ, ಪ್ರಸಿದ್ಧ ಉಪಯುಕ್ತತೆಯನ್ನು ಬಳಸಿಕೊಂಡು ಎಕ್ಸ್‌ಡಿಪಿ ಮತ್ತು ಟಿಸಿ ಪ್ರಕಾರಗಳ ಬಿಪಿಎಫ್ ಕಾರ್ಯಕ್ರಮಗಳೊಂದಿಗೆ ಹೇಗೆ ಕೆಲಸ ಮಾಡಬೇಕೆಂದು ಈ ಡಾಕ್ಯುಮೆಂಟ್ ವಿವರಿಸುತ್ತದೆ. ip ಪ್ಯಾಕೇಜ್‌ನಿಂದ iproute2.

  2. ಡಾಕ್ಯುಮೆಂಟೇಶನ್/ನೆಟ್ವರ್ಕಿಂಗ್/filter.txt — ಕ್ಲಾಸಿಕ್ ಮತ್ತು ನಂತರದ ವಿಸ್ತೃತ BPF ಗಾಗಿ ದಸ್ತಾವೇಜನ್ನು ಹೊಂದಿರುವ ಮೂಲ ಫೈಲ್. ನೀವು ವಾಸ್ತುಶಿಲ್ಪದ ಅಸೆಂಬ್ಲರ್ ಮತ್ತು ತಾಂತ್ರಿಕ ವಿವರಗಳನ್ನು ಪರಿಶೀಲಿಸಲು ಬಯಸಿದರೆ ಉಪಯುಕ್ತ ಓದುವಿಕೆ.

  3. ಫೇಸ್‌ಬುಕ್‌ನಿಂದ ಬಿಪಿಎಫ್ ಬಗ್ಗೆ ಬ್ಲಾಗ್. ವಿರಳವಾಗಿ ನವೀಕರಿಸಲಾಗಿದೆ, ಆದರೆ ನಿಖರವಾಗಿ, ಅಲೆಕ್ಸಿ ಸ್ಟಾರೊವೊಯಿಟೋವ್ (ಇಬಿಪಿಎಫ್ ಲೇಖಕ) ಮತ್ತು ಆಂಡ್ರಿ ನಕ್ರಿಕೊ (ನಿರ್ವಾಹಕ) ಅಲ್ಲಿ ಬರೆಯುತ್ತಾರೆ. libbpf).

  4. bpftool ನ ರಹಸ್ಯಗಳು. bpftool ಬಳಸುವ ಉದಾಹರಣೆಗಳು ಮತ್ತು ರಹಸ್ಯಗಳೊಂದಿಗೆ ಕ್ವೆಂಟಿನ್ ಮಾನೆಟ್ ಅವರಿಂದ ಆಸಕ್ತಿದಾಯಕ ಟ್ವಿಟರ್ ಥ್ರೆಡ್.

  5. ಬಿಪಿಎಫ್ ಅನ್ನು ಅನ್ವೇಷಿಸಿ: ಓದುವ ಸಾಮಗ್ರಿಗಳ ಪಟ್ಟಿ. ಕ್ವೆಂಟಿನ್ ಮಾನೆಟ್ ಅವರ ಬಿಪಿಎಫ್ ದಸ್ತಾವೇಜೀಕರಣದ ಲಿಂಕ್‌ಗಳ ದೈತ್ಯ (ಮತ್ತು ಇನ್ನೂ ನಿರ್ವಹಿಸಲ್ಪಡುತ್ತಿದೆ) ಪಟ್ಟಿ.

ಮೂಲ: www.habr.com