# ANSY SUBJECTS 2025 ## Useful links slides: https://zarak.fr/resources/ANSY.pdf Linux kernel mailing lists: https://lkml.org/ Linux sources: https://elixir.bootlin.com/linux/v6.17/source ## Information about all submissions The submissions will be done by email at ansy@zarak.fr, tag `[ANSY]`. You can submit each part individually. There is no restriction on the language used or the submission architecture, but please make it clear, and readable. The easier it is for me to correct you, the more likely I will be to fully appreaciate your work. Recommended languages however includes: - python - bash - c - golang I will also accept "normal" languages for the kind of tasks I'm asking you to do, but I reserve myself the right of refusing a submission if it's an obvious troll like brainfuck or ook!. Please send files like: ``` TP<number>-<login>.zip └── TP<number>-<login>/ ├── README └── ... ``` I'd need everybody to follow these simple instructions to avoid making me loose time reshaping your submitted files, thanks ! If you have any question, or if you're stuck, please reach out to me ! ### Note Please track the time you're spending on each part and provide the information in the submission's README. This will allow me to better estimate the workload of those subjets and possibly adapt it for next year # ANSY 1 ## Rendu La date max de rendu est fixée au début du cours n°3, le jeudi 16/10/25 à 9h ## Exercices 1. Indiquer dans le README une philosophie de base du développement de Linux, notammement mis en lumière dans l'échange houleux et très discutable sur la forme entre Linus Torvalds et Mauro Chehab (https://lkml.org/lkml/2012/12/23/75) 2. Avec strace, trouver le syscall qui est executé plus de 10k fois par le binaire fourni, et me rapporter dans le README combien de fois il est appelé 3. écrire un programme qui permet d'executer le binaire fourni et faire en sorte qu'il fonctionne, c'est à dire qu'il retourne 0 en exit code et qu'il affiche la phrase de fin ("gg!"). Pour ça, il va falloir utiliser strace pour comprendre son fonctionnement, et faire en sorte qu'il puisse s'executer comme il devrait. Il y a 2-3 "pièges", en tout cas du bruit, pour essayer de se mettre dans une condition plus "réelle" 4. BONUS OPTIONNEL: Atteindre la seconde fin du programme (bonus, plus difficile) Le binaire à analyser est disponible sur https://zarak.fr/resources/straceme Je vous demande de n'utiliser que strace pour l'analyse et pour le comprendre. ### Tips and tricks Vous pouvez modifier l'environment d'execution du binaire, ou bien modifier le comportement des syscalls avec `strace -e`. Prenez le temps de regarder la man page, de lire un peu les exemples et de voir les possibilités. # ANSY 2 ## Let's observe the CPU/Scheduler in action. Write a program than can creates situations to prove how the niceness of 2 different processes interferes within each other. The idea is to be able to provide an answer for such problem: I have process A with a niceness of X. I have process B with a niceness of Y. Both processes are started at the same time, and require the same amount of CPU in total. But they're fightining each other for said CPU. How much more real time A (or B) will need compared to the amount of time it needed if it were to run alone ? Answer can be summed up in an array like: Niceness of A (X) | Niceness of B (Y) | Extra time needed for A | Extra time needed for B | --------------|---------------|-------------------------|------------------------------| 0|10|0% (as fast as if it was alone) | 100% (needed twice the usual amount of time) 0|0|50% |50% Of course those values are example. Create a program that takes as an input X and Y (argv), runs two processes in parallel with given niceness, measure how much time it needed to execute the two and output the % of extra time needed in comparison of a single execution. ### Tips - You'll need to run a CPU-bound program for this test to make sense - You'll need to run in a single core those two processes to see how the scheduler will make decisions. You'll have to force those 2 processes on the same one core - Execution time will vary from a laptop to another, don't hardcode the referential execution time ## Extra challenge This challenge is a bonus, it's quite difficult and may take quite some time to achieve. Don't feel forced to do it if you don't have the time. Create a program that can create a very high cpu load -- let's say twice the amount of cores -- **without** using the CPU (CPU usage % shall remain below ~10%). ## Submission The deadline is set to the 27th of october, 13h00. As usual, the submission will be done by email, with a .zip described above. I'm expecting a README with the amount of time spent, an array providing answer for (X=0,Y=0),(X=0,Y=10),(X=0,Y=15), and ofc the program(s) # ANSY 3 This subject will be centered around the `madvise(2)` syscall and understanding some elements of Linux' memory management. We'll write a benchmark program that will work on big area of allocated memory, file-backed, and measure the time it takes to read or write on this memory area. The program will also take some optionnal `madvise(2)` argument to apply to the whole area, to see its effect on the performances. I suggest this TP to be written in a low-level language. If may include a high-level wrapper for the init phase if you wish. ## Init phase Prepare the benchmark conditions: allocate a big file on the disk with random data taken from `/dev/urandom`. The bigger the better: more space will mean results a tad more precise. If the file already exists, skip its creation. You'll also need to reset the caches: `echo 3 | sudo tee /proc/sys/vm/drop_caches` ## Run phase The idea will be to `mmap(2)` the whole file, and to run a read or a write benchmark (CLI chosen) after applying some CLI provided `madvise(2)` argument, to see its effect on the benchmark. Read and write benchmarks must either be sequential (default) or random (CLI indicated). `madvise(2)` flags we're interested in testing are the following: - MADV_NORMAL (default) - MADV_RANDOM - MADV_SEQUENTIAL - MADV_WILLNEED - MADV_DONTNEED - MADV_HUGEPAGE - MADV_NOHUGEPAGE - MADV_COLLAPSE - MADV_POPULATE_READ (ignore its potential error if any) The specs are not much more precise than that. You have the freedom to write the program in a way that you think makes the most sense for this task. ## Submission The deadline is set to the 14th of November, 10h00. As usual, the submission will be done by email, with a .zip described above. You will provide the program(s) and also a README containing a complete benchmark results: | Mode (r/w) | Order (**s**equential/**r**andom) | madvise | time taken| |------------|-----------------------------------|--------------|-----------| | r | sequential | MADV_NORMAL | x ms | | ... | | | | Also write a very small conclusion on what you observed. What do you think about the flags (NO)HUGEPAGE in this context ? Don't hesitate to have a look in the usage of the MADV_* flags in the Linux code to help with your intuition/explanation. **PLEASE** provide the time it took you to realize this subject in its entirety, it'll help me and the next promotion ! ## Tips As long as you understand the code, you can explain it in its entirety and you could rewrite it yourself if asked, I'm perfectly ok for you to use LLMs to write the bench script if it helps you going faster. # ANSY BONUS subject This subject is a bonus, either to help you boost your grade if you believe you did not perform well on other subjects, or to help compensate a missing submission. **The deadline for this exercise will be the 11th of January 23:59.** ## Manipulating block devices The point of this exercise will be to write a small program/script to manipulate block devices, kernel modules related to block devices, filesystems and famous userland tools. The suggested language is bash. You will have to create a few scenarios and run benchmark to test them out. The benchmark will be run with `fio`: `fio --name=random-write --rw=randwrite --bs=4k --numjobs=1 --size=1g --iodepth=256 --runtime=60 --time_based --end_fsync=1` As you can read, you'll need a GiB of free space to run this test. It is therefore recommended to create each situation with 5 GiB to begin with, to be sure to have enough room in the end. ## Core The core of your program will: 1. Create a file that will be used as a block device. - *It's recommended to use a file rather than a disk partition as it's simpler for theses tests. But keep in mind that once this file has been setup to behave as a block device, things will be the same as if it's a "real" block device (a real hard drive, USB key, ...)* 1. Expose this file as a block device via a loop device. Check **losetup** - *pro-tip: use `-f --show`* 3. You now have a block device than can be used. Depending on the arguments of your script, you will apply or not some transformation to it. Refer to the next section to know what transformation are expected here 4. After the optional transformation, you end up with a block device. This block device to be used needs a filesystem. Create an XFS filesystem on the block device 5. The filesystem to be accessed needs to be mounted. Mount it to the path specified by `--path` (defaults to `/mnt/final` if not specified) 6. If the user asked for it via `--bench`, run a benchmark on the final filesystem ## Transformations Here are a list of flags your program can take and that will apply transformation to the block device at the step 3: - `--loop` will take a number X as argument (default: 0) and will repeat the core of the program X times, meaning repeating steps 1 to 4 included, and step 5 with a path of `/mnt/loop-$i`. - *If another transformation is asked, this transformation must be done at each loop iteration.* - *The step 1 must be performed on the filesystem created at the previous iteration of course* - For the last iteration of the loop, the name to be used on step 5 is `/mnt/final` - With `--loop 0` (the default), one has one `/mnt/final` - With `--loop 1`, one has `/mnt/loop-1` for the first iteration of the core loop, and `/mnt/final` for the last iteration - `--basic-lvm` will create a LVM PV, VG and single LV taking all the space possible of the block device. - *The names are up to you, but `--loop` can be used, near that in mind. Refer to [archlinux LVM doc for help](https://wiki.archlinux.org/title/LVM)* - `--crypt` will create a LUKS2 encrypted block device with cryptsetup. The password will be very secure of course, as it's going to be `password` - *you will have to create the encrypted block device, but also open it to use it !* - *The name of the devices is up to you. [archlinux doc](https://wiki.archlinux.org/title/dm-crypt/Device_encryption)* - `--dm-linear` will create a block device from your whole block device in a linear way - *[doc](https://docs.kernel.org/admin-guide/device-mapper/linear.html)* - `--dm-delay` will perform as `--dm-linear`, but will add a delay of 1000 ms - *[doc](https://docs.kernel.org/admin-guide/device-mapper/delay.html)* - `--md` will create 2 block devices from your block device (split it in half with `dmsetup`'s linear option), and then a RAID 1 disk from the two block devices - *[doc](https://wiki.archlinux.org/title/RAID)* Each transformation shall take as an input 1 block device, and provide as an output 1 block device. They can be chained this way. ## Pro-tips It is recommended to start simple. Create the `core` part of the program, and test it well. Add a `--cleanup` flag to not do anything but remove what your program has created, this will help you a lot Gradually add the transformations support. They may seems complex, but each one taken individually is actually fairly straight forward with the appropriate documentation Reach me for help if things are unclear, or not working as expected. You can use argbash.dev to simplify development if you choose bash