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

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

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

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

ಲೇಖನದ ಸಾರಾಂಶ

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

BPF ವರ್ಚುವಲ್ ಯಂತ್ರದ ರಿಜಿಸ್ಟರ್‌ಗಳು ಮತ್ತು ಕಮಾಂಡ್ ಸಿಸ್ಟಮ್. ಒಟ್ಟಾರೆಯಾಗಿ ವಾಸ್ತುಶಿಲ್ಪದ ಕಲ್ಪನೆಯನ್ನು ಈಗಾಗಲೇ ಹೊಂದಿದ್ದು, ನಾವು BPF ವರ್ಚುವಲ್ ಯಂತ್ರದ ರಚನೆಯನ್ನು ವಿವರಿಸುತ್ತೇವೆ.

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

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

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

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

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

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

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

ಬಿಪಿಎಫ್ ಆರ್ಕಿಟೆಕ್ಚರ್ ಪರಿಚಯ

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

BPF ವರ್ಚುವಲ್ ಮೆಷಿನ್ ರಿಜಿಸ್ಟರ್‌ಗಳು ಮತ್ತು ಸೂಚನಾ ವ್ಯವಸ್ಥೆ

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

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

BPF ಪ್ರೋಗ್ರಾಮ್‌ಗಳು ನಿರ್ದಿಷ್ಟವಾದ ಪ್ರೋಗ್ರಾಂ-ರೀತಿಯ ಕರ್ನಲ್ ಸಹಾಯಕರನ್ನು ಮತ್ತು ಇತ್ತೀಚೆಗೆ, ನಿಯಮಿತ ಕಾರ್ಯಗಳನ್ನು ಚಲಾಯಿಸಲು ಅನುಮತಿಸಲಾಗಿದೆ. ಕರೆಯಲ್ಪಡುವ ಪ್ರತಿಯೊಂದು ಕಾರ್ಯವು ಐದು ಆರ್ಗ್ಯುಮೆಂಟ್‌ಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳಬಹುದು, ರೆಜಿಸ್ಟರ್‌ಗಳಲ್ಲಿ ರವಾನಿಸಲಾಗಿದೆ 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 (ವಿವಿಧ ರೀತಿಯ ಟ್ರೇಸಿಂಗ್ ಕಾರ್ಯಕ್ರಮಗಳಿಗಾಗಿ), ಇತ್ಯಾದಿ.

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

ವಿವರಣೆಯನ್ನು ಮುಂದುವರಿಸೋಣ ಮತ್ತು ಈ ವಸ್ತುಗಳೊಂದಿಗೆ ಕೆಲಸ ಮಾಡಲು ಕಮಾಂಡ್ ಸಿಸ್ಟಮ್ ಬಗ್ಗೆ ಮಾತನಾಡೋಣ. ಎಲ್ಲಾ (ಹೆಚ್ಚುಕಡಿಮೆ ಎಲ್ಲವೂ) BPF ಸೂಚನೆಗಳು ಸ್ಥಿರವಾದ 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 ಗಾಗಿ ಯಾವುದೇ ಬೈನರಿ ಫೈಲ್ ಅನ್ನು ಹಸ್ತಚಾಲಿತವಾಗಿ ಡಿಸ್ಅಸೆಂಬಲ್ ಮಾಡಿ. ನಂತರ ಲೇಖನದಲ್ಲಿ ವಿಷಯವನ್ನು ಕ್ರೋಢೀಕರಿಸಲು, ವೆರಿಫೈಯರ್, ಜೆಐಟಿ ಕಂಪೈಲರ್, ಕ್ಲಾಸಿಕ್ ಬಿಪಿಎಫ್ ಅನುವಾದ, ಹಾಗೆಯೇ ನಕ್ಷೆಗಳನ್ನು ಅಧ್ಯಯನ ಮಾಡುವಾಗ, ಕರೆ ಮಾಡುವ ಕಾರ್ಯಗಳು ಇತ್ಯಾದಿಗಳ ಕುರಿತು ವಿಭಾಗಗಳಲ್ಲಿನ ವೈಯಕ್ತಿಕ ಸೂಚನೆಗಳನ್ನು ನಾವು ಭೇಟಿ ಮಾಡುತ್ತೇವೆ.

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

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

ನಾವು ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಕಂಪೈಲ್ ಮಾಡುವ ಉದಾಹರಣೆಯನ್ನು ನೋಡೋಣ 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 - ಇದು BPF_MOV. ಇದು ಗಮ್ಯಸ್ಥಾನ ರಿಜಿಸ್ಟರ್‌ಗೆ ಮೌಲ್ಯವನ್ನು ನಿಯೋಜಿಸುತ್ತದೆ. ಬಿಟ್ ಹೊಂದಿಸಿದ್ದರೆ s (ಮೂಲ), ನಂತರ ಮೌಲ್ಯವನ್ನು ಮೂಲ ರಿಜಿಸ್ಟರ್‌ನಿಂದ ತೆಗೆದುಕೊಳ್ಳಲಾಗುತ್ತದೆ ಮತ್ತು ನಮ್ಮ ಸಂದರ್ಭದಲ್ಲಿ, ಅದನ್ನು ಹೊಂದಿಸದಿದ್ದರೆ, ನಂತರ ಮೌಲ್ಯವನ್ನು ಕ್ಷೇತ್ರದಿಂದ ತೆಗೆದುಕೊಳ್ಳಲಾಗುತ್ತದೆ Imm. ಆದ್ದರಿಂದ ಮೊದಲ ಮತ್ತು ಮೂರನೇ ಸೂಚನೆಗಳಲ್ಲಿ ನಾವು ಕಾರ್ಯಾಚರಣೆಯನ್ನು ನಿರ್ವಹಿಸುತ್ತೇವೆ r0 = Imm. ಇದಲ್ಲದೆ, JMP ವರ್ಗ 1 ಕಾರ್ಯಾಚರಣೆಯಾಗಿದೆ BPF_JEQ (ಸಮಾನವಾಗಿದ್ದರೆ ಜಿಗಿಯಿರಿ). ನಮ್ಮ ಸಂದರ್ಭದಲ್ಲಿ, ಬಿಟ್ ರಿಂದ S ಶೂನ್ಯವಾಗಿದೆ, ಇದು ಕ್ಷೇತ್ರದೊಂದಿಗೆ ಮೂಲ ರಿಜಿಸ್ಟರ್‌ನ ಮೌಲ್ಯವನ್ನು ಹೋಲಿಸುತ್ತದೆ Imm. ಮೌಲ್ಯಗಳು ಹೊಂದಿಕೆಯಾದರೆ, ಪರಿವರ್ತನೆಯು ಸಂಭವಿಸುತ್ತದೆ PC + Offಅಲ್ಲಿ PC, ಎಂದಿನಂತೆ, ಮುಂದಿನ ಸೂಚನೆಯ ವಿಳಾಸವನ್ನು ಒಳಗೊಂಡಿದೆ. ಅಂತಿಮವಾಗಿ, JMP ಕ್ಲಾಸ್ 9 ಆಪರೇಷನ್ ಆಗಿದೆ 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 ಅನ್ನು ಡಿಸ್ಅಸೆಂಬಲ್ ಮಾಡುವುದು

ಆದ್ದರಿಂದ, ನಾವು 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 ವಸ್ತುಗಳ ಜೀವನಚಕ್ರ, bpffs ಫೈಲ್ ಸಿಸ್ಟಮ್

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

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 BPF ಸಿಸ್ಟಮ್ ಕರೆ. ವಿವರಿಸಲು, ನಾವು ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ತೆಗೆದುಕೊಳ್ಳೋಣ, ಅದನ್ನು ಕಂಪೈಲ್ ಮಾಡಿ, ಅದನ್ನು ಅಪ್ಲೋಡ್ ಮಾಡಿ ಮತ್ತು ಅದನ್ನು ಪಿನ್ ಮಾಡೋಣ 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 ಕಾರ್ಯಕ್ರಮಗಳು

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

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 ಹ್ಯಾಶ್ ಟೇಬಲ್ ಅನ್ನು ರಚಿಸಲು, ನಾವು ಟೇಬಲ್‌ನ ಗರಿಷ್ಠ ಗಾತ್ರವನ್ನು ನಿರ್ದಿಷ್ಟಪಡಿಸುವುದನ್ನು ಹೊರತುಪಡಿಸಿ, ನಾವು ಒಂದೇ ರೀತಿಯ ಕೆಲಸವನ್ನು ಮಾಡಬೇಕಾಗಿದೆ ಮತ್ತು ಕೀಗಳು ಮತ್ತು ಮೌಲ್ಯಗಳ ಪ್ರಕಾರಗಳನ್ನು ನಿರ್ದಿಷ್ಟಪಡಿಸುವ ಬದಲು, ನಾವು ಅವುಗಳ ಗಾತ್ರಗಳನ್ನು ಬೈಟ್‌ಗಳಲ್ಲಿ ನಿರ್ದಿಷ್ಟಪಡಿಸಬೇಕಾಗಿದೆ. . ನಕ್ಷೆಗಳನ್ನು ರಚಿಸಲು ಆಜ್ಞೆಯನ್ನು ಬಳಸಿ 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 ಆಗಿದೆ. ಆಜ್ಞೆಯನ್ನು ಬಳಸಿಕೊಂಡು ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ನಕ್ಷೆಯನ್ನು ತೆರೆಯಲು ಸಿಸ್ಟಮ್‌ನಲ್ಲಿನ ಯಾವುದೇ ಪ್ರೋಗ್ರಾಂ ಈ ಐಡಿಯನ್ನು ಬಳಸಬಹುದು 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 (ಒಮ್ಮೆ ಕಂಪೈಲ್ ಮಾಡಿ, ಎಲ್ಲೆಡೆ ರನ್ ಮಾಡಿ) - ವಿಭಿನ್ನ API ಗಳಲ್ಲಿ ಚಲಿಸುವ ಸಾಮರ್ಥ್ಯದೊಂದಿಗೆ (ಉದಾಹರಣೆಗೆ, ಆವೃತ್ತಿಯಿಂದ ಕರ್ನಲ್ ರಚನೆಯು ಬದಲಾದಾಗ, ಒಂದು ಕರ್ನಲ್‌ನಿಂದ ಇನ್ನೊಂದಕ್ಕೆ ಪೋರ್ಟಬಲ್ ಆಗಿರುವ BPF ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಬರೆಯಲು ನಿಮಗೆ ಅನುಮತಿಸುವ ಯೋಜನೆಯಾಗಿದೆ. ಆವೃತ್ತಿಗೆ). 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 ಕರ್ನಲ್ ಮತ್ತು ಅದರ ಅಭಿವೃದ್ಧಿಯನ್ನು ಮೇಲಿಂಗ್ ಪಟ್ಟಿಯ ಮೂಲಕ ಕೈಗೊಳ್ಳಲಾಗುತ್ತದೆ [email protected]. ಆದಾಗ್ಯೂ, ಕರ್ನಲ್‌ನ ಹೊರಗೆ ವಾಸಿಸುವ ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಅಗತ್ಯಗಳಿಗಾಗಿ ಪ್ರತ್ಯೇಕ ರೆಪೊಸಿಟರಿಯನ್ನು ನಿರ್ವಹಿಸಲಾಗುತ್ತದೆ https://github.com/libbpf/libbpf ಇದರಲ್ಲಿ ಕರ್ನಲ್ ಲೈಬ್ರರಿಯನ್ನು ಹೆಚ್ಚು ಕಡಿಮೆ ಓದಲು ಪ್ರತಿಬಿಂಬಿಸಲಾಗಿದೆ.

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

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

$ 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 ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಬರೆಯುತ್ತೇವೆ 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). BPF ಪ್ರೋಗ್ರಾಂ ಸ್ವತಃ ಆಗಿದೆ C - ತುಂಬಾ ಸರಳ ಮತ್ತು ಒಂದು ಸಾಲನ್ನು ಒಳಗೊಂಡಿದೆ return XDP_PASS. ಅಂತಿಮವಾಗಿ, ಪ್ರತ್ಯೇಕ ವಿಭಾಗ "license" ಪರವಾನಗಿಯ ಹೆಸರನ್ನು ಒಳಗೊಂಡಿದೆ.

ನಾವು llvm/clang, ಆವೃತ್ತಿ >= 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 - BPF ಪ್ರಪಂಚದ ಸ್ವಿಸ್ ಚಾಕು (ಇದನ್ನು ಅಕ್ಷರಶಃ ತೆಗೆದುಕೊಳ್ಳಬಹುದು, ಏಕೆಂದರೆ BPF ನ ಸೃಷ್ಟಿಕರ್ತರು ಮತ್ತು ನಿರ್ವಾಹಕರಲ್ಲಿ ಒಬ್ಬರಾದ ಡೇನಿಯಲ್ ಬೋರ್ಕ್‌ಮನ್ ಸ್ವಿಸ್ ಆಗಿದ್ದಾರೆ):

$ 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

ಏನೋ ಹೊಸತು! ಪ್ರೋಗ್ರಾಂ ನಮ್ಮ ಸಿ ಮೂಲ ಫೈಲ್‌ನ ತುಣುಕುಗಳನ್ನು ಮುದ್ರಿಸಿದೆ. ಇದನ್ನು ಲೈಬ್ರರಿ ಮಾಡಿದೆ 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 ಸಹಾಯಕ ಕಾರ್ಯದ ವ್ಯಾಖ್ಯಾನಗಳು Linux ಸಿಸ್ಟಮ್ ಕರೆ ವ್ಯಾಖ್ಯಾನಗಳಿಗೆ ಹೋಲುತ್ತವೆ. ಇಲ್ಲಿ, ಉದಾಹರಣೆಗೆ, ಯಾವುದೇ ವಾದಗಳಿಲ್ಲದ ಕಾರ್ಯವನ್ನು ವ್ಯಾಖ್ಯಾನಿಸಲಾಗಿದೆ. (ಮೂರು ಆರ್ಗ್ಯುಮೆಂಟ್‌ಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳುವ ಕಾರ್ಯವನ್ನು ಮ್ಯಾಕ್ರೋ ಬಳಸಿ ವ್ಯಾಖ್ಯಾನಿಸಲಾಗಿದೆ 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 ಪ್ರೋಗ್ರಾಂಗಳು ಈ ಕಾರ್ಯವನ್ನು ಬಳಸಲು, ಅವರು ಅದನ್ನು ನೋಂದಾಯಿಸಿಕೊಳ್ಳಬೇಕು, ಉದಾಹರಣೆಗೆ ಪ್ರಕಾರಕ್ಕಾಗಿ 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;
    ...
    }
}

ಹೊಸ BPF ಪ್ರೋಗ್ರಾಂ ಪ್ರಕಾರಗಳನ್ನು ಫೈಲ್‌ನಲ್ಲಿ "ವ್ಯಾಖ್ಯಾನಿಸಲಾಗಿದೆ" include/linux/bpf_types.h ಮ್ಯಾಕ್ರೋ ಬಳಸಿ BPF_PROG_TYPE. ಉಲ್ಲೇಖಗಳಲ್ಲಿ ವ್ಯಾಖ್ಯಾನಿಸಲಾಗಿದೆ ಏಕೆಂದರೆ ಇದು ತಾರ್ಕಿಕ ವ್ಯಾಖ್ಯಾನವಾಗಿದೆ, ಮತ್ತು ಸಿ ಭಾಷೆಯಲ್ಲಿ ಸಂಪೂರ್ಣ ಕಾಂಕ್ರೀಟ್ ರಚನೆಗಳ ವ್ಯಾಖ್ಯಾನವು ಇತರ ಸ್ಥಳಗಳಲ್ಲಿ ಕಂಡುಬರುತ್ತದೆ. ನಿರ್ದಿಷ್ಟವಾಗಿ, ಫೈಲ್ನಲ್ಲಿ 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
};

ಅಂದರೆ, ಪ್ರತಿಯೊಂದು ಪ್ರಕಾರದ BPF ಪ್ರೋಗ್ರಾಂಗೆ, ಪ್ರಕಾರದ ಡೇಟಾ ರಚನೆಗೆ ಪಾಯಿಂಟರ್ ಅನ್ನು ವ್ಯಾಖ್ಯಾನಿಸಲಾಗಿದೆ 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, ಇದು ಪ್ರತಿ ಬಾರಿ ಸವಾಲನ್ನು ಎದುರಿಸಿದಾಗ ಪರಿಶೀಲಕವನ್ನು ರನ್ ಮಾಡುತ್ತದೆ ಕೆಲವು ರೀತಿಯ BPF ಪ್ರೋಗ್ರಾಂ ಒಳಗೆ ಕಾರ್ಯಗಳು, ನೋಡಿ verifier.c.

ಕಾಲ್ಪನಿಕ BPF ಪ್ರೋಗ್ರಾಂ ಕಾರ್ಯವನ್ನು ಹೇಗೆ ಬಳಸುತ್ತದೆ ಎಂಬುದನ್ನು ನೋಡೋಣ 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_id. 9-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 ಅಥವಾ ಅನಂತ ಲೂಪ್: ನಮ್ಮ ಲೋಡರ್ ಪ್ರೋಗ್ರಾಂ ನಿರ್ಗಮಿಸುತ್ತದೆ, ಆದರೆ ಈವೆಂಟ್ ಮೂಲಕ್ಕೆ ಸಂಪರ್ಕಗೊಂಡಿರುವುದರಿಂದ BPF ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಕೊಲ್ಲಲಾಗುವುದಿಲ್ಲ. ಯಶಸ್ವಿ ಡೌನ್‌ಲೋಡ್ ಮತ್ತು ಸಂಪರ್ಕದ ನಂತರ, ಆಗಮಿಸುವ ಪ್ರತಿ ನೆಟ್‌ವರ್ಕ್ ಪ್ಯಾಕೆಟ್‌ಗೆ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತದೆ 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 ಇದು ಡೀಬಗ್ ಫೈಲ್‌ಗೆ ಬರೆಯುವುದು ಯಾವುದಕ್ಕೂ ಅಲ್ಲ: ಇದು ಉತ್ಪಾದನೆಯಲ್ಲಿ ಬಳಸಲು ಅತ್ಯಂತ ಯಶಸ್ವಿ ಸಹಾಯಕ ಅಲ್ಲ, ಆದರೆ ನಮ್ಮ ಗುರಿ ಸರಳವಾದದ್ದನ್ನು ತೋರಿಸುವುದು.

BPF ಕಾರ್ಯಕ್ರಮಗಳಿಂದ ನಕ್ಷೆಗಳನ್ನು ಪ್ರವೇಶಿಸಲಾಗುತ್ತಿದೆ

ಉದಾಹರಣೆ: BPF ಪ್ರೋಗ್ರಾಂನಿಂದ ನಕ್ಷೆಯನ್ನು ಬಳಸುವುದು

ಹಿಂದಿನ ವಿಭಾಗಗಳಲ್ಲಿ ನಾವು ಬಳಕೆದಾರರ ಸ್ಥಳದಿಂದ ನಕ್ಷೆಗಳನ್ನು ಹೇಗೆ ರಚಿಸುವುದು ಮತ್ತು ಬಳಸುವುದು ಎಂಬುದನ್ನು ಕಲಿತಿದ್ದೇವೆ ಮತ್ತು ಈಗ ನಾವು ಕರ್ನಲ್ ಭಾಗವನ್ನು ನೋಡೋಣ. ಎಂದಿನಂತೆ, ಒಂದು ಉದಾಹರಣೆಯೊಂದಿಗೆ ಪ್ರಾರಂಭಿಸೋಣ. ನಮ್ಮ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಪುನಃ ಬರೆಯೋಣ 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, ಇದು ಹಾರಾಡುತ್ತ BPF ಬೈನರಿ ಕೋಡ್ ಅನ್ನು ಉತ್ಪಾದಿಸುತ್ತದೆ.

ತರ್ಕವನ್ನು ಅನುಸರಿಸಲು ಸುಲಭವಾಗುವಂತೆ, ಈ ಉದ್ದೇಶಗಳಿಗಾಗಿ ನಾವು ನಮ್ಮ ಉದಾಹರಣೆಯನ್ನು ಪುನಃ ಬರೆಯುತ್ತೇವೆ 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 ನಮ್ಮ BPF ಪ್ರೋಗ್ರಾಂನ ರಚನೆಗಳ ಒಂದು ಶ್ರೇಣಿಯ ವ್ಯಾಖ್ಯಾನವಾಗಿದೆ 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 ಡೆವಲಪರ್ ಟೂಲ್ಕಿಟ್ ಅನ್ನು ನೋಡುತ್ತೇವೆ.

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

  • llvm/clang
  • pahole
  • ಅದರ ತಿರುಳು
  • bpftool

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

llvm/clang

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

$ 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 ನನ್ನಿಂದ ತೆಗೆದುಕೊಳ್ಳಲಾಗಿದೆ bpf_devel_QA.)

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

ಫೈಲ್‌ನಲ್ಲಿ BPF ಆಯ್ಕೆಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ .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

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

ಈ ಬರಹದ ಸಮಯದಲ್ಲಿ bpftool RHEL, Fedora ಮತ್ತು Ubuntu ಗೆ ಮಾತ್ರ ಸಿದ್ಧವಾಗಿದೆ (ನೋಡಿ, ಉದಾಹರಣೆಗೆ, ಈ ಥ್ರೆಡ್, ಇದು ಪ್ಯಾಕೇಜಿಂಗ್‌ನ ಅಪೂರ್ಣ ಕಥೆಯನ್ನು ಹೇಳುತ್ತದೆ 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 ಪರಿಣಾಮಕಾರಿಯಾಗಿ ಅಳೆಯಲು ಮತ್ತು ಕೋರ್ನ ಕಾರ್ಯವನ್ನು ಬದಲಾಯಿಸಲು ಚಿಗಟವನ್ನು ಶೂ ಮಾಡಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಯುನಿಕ್ಸ್‌ನ ಅತ್ಯುತ್ತಮ ಸಂಪ್ರದಾಯಗಳಲ್ಲಿ ಈ ವ್ಯವಸ್ಥೆಯು ಅತ್ಯಂತ ಯಶಸ್ವಿಯಾಗಿದೆ: ಕರ್ನಲ್ ಅನ್ನು (ಮರು) ಪ್ರೋಗ್ರಾಂ ಮಾಡಲು ನಿಮಗೆ ಅನುಮತಿಸುವ ಸರಳ ಕಾರ್ಯವಿಧಾನವು ಹೆಚ್ಚಿನ ಸಂಖ್ಯೆಯ ಜನರು ಮತ್ತು ಸಂಸ್ಥೆಗಳಿಗೆ ಪ್ರಯೋಗ ಮಾಡಲು ಅವಕಾಶ ಮಾಡಿಕೊಟ್ಟಿತು. ಮತ್ತು, ಪ್ರಯೋಗಗಳು, ಹಾಗೆಯೇ ಬಿಪಿಎಫ್ ಮೂಲಸೌಕರ್ಯದ ಅಭಿವೃದ್ಧಿಯು ಪೂರ್ಣಗೊಂಡಿಲ್ಲವಾದರೂ, ಸಿಸ್ಟಮ್ ಈಗಾಗಲೇ ಸ್ಥಿರವಾದ ಎಬಿಐ ಅನ್ನು ಹೊಂದಿದ್ದು ಅದು ನಿಮಗೆ ವಿಶ್ವಾಸಾರ್ಹ ಮತ್ತು ಮುಖ್ಯವಾಗಿ ಪರಿಣಾಮಕಾರಿ ವ್ಯವಹಾರ ತರ್ಕವನ್ನು ನಿರ್ಮಿಸಲು ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ.

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

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

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

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

ಲಿಂಕ್‌ಗಳು

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

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

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

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

  5. BPF ಗೆ ಧುಮುಕುವುದು: ಓದುವ ವಸ್ತುಗಳ ಪಟ್ಟಿ. ಕ್ವೆಂಟಿನ್ ಮೊನೆಟ್‌ನಿಂದ BPF ದಾಖಲಾತಿಗೆ ದೈತ್ಯ (ಮತ್ತು ಇನ್ನೂ ನಿರ್ವಹಿಸುತ್ತಿರುವ) ಲಿಂಕ್‌ಗಳ ಪಟ್ಟಿ.

ಮೂಲ: www.habr.com

ಕಾಮೆಂಟ್ ಅನ್ನು ಸೇರಿಸಿ