{"id":301,"date":"2014-04-10T11:40:47","date_gmt":"2014-04-10T11:40:47","guid":{"rendered":"http:\/\/queen.run.montefiore.ulg.ac.be\/~barbette\/?p=301"},"modified":"2014-04-10T11:41:35","modified_gmt":"2014-04-10T11:41:35","slug":"understanding-sleeping-mechanism-in-linux-kernel","status":"publish","type":"post","link":"https:\/\/perso.uclouvain.be\/tom.barbette\/understanding-sleeping-mechanism-in-linux-kernel\/","title":{"rendered":"Understanding sleeping mechanism in Linux Kernel"},"content":{"rendered":"<p>The students of INFO-0940 were asked to remove a task from it&#8217;s current scheduler class when calling a new syscall and put it back when we call again that syscall. The question is : what to do if the task is currently sleeping? Cause the timer will expire at one moment and maybe put back the state to TASK_RUNNING and run the task which should haven&#8217;t been able to run again. So either it expires after the processus is back in its previous scheduler, and that&#8217;s okay as it will reset it in a runnable state. Or it expires while it&#8217;s still removed, and that&#8217;s a problem as it will put it back in its previous state (or not, depending on how the syscall is implemented).<br \/>\nWarning : this post is just the result of a quick look. More to give you some ideas and basis for understanding the sleeping mechanism.<\/p>\n<p>&nbsp;<\/p>\n<p>So we should try to understand how sleep works.<\/p>\n<p>&nbsp;<\/p>\n<p>Let&#8217;s look at the nanosleep syscall (which is called when you user sleep() or usleep() functions in C).<\/p>\n<pre class=\"prettyprint linenums\">SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp,\r\n\t\tstruct timespec __user *, rmtp)\r\n{\r\n\tstruct timespec tu;\r\n\r\n\tif (copy_from_user(&amp;tu, rqtp, sizeof(tu)))\r\n\t\treturn -EFAULT;\r\n\r\n\tif (!timespec_valid(&amp;tu))\r\n\t\treturn -EINVAL;\r\n\r\n\treturn hrtimer_nanosleep(&amp;tu, rmtp, HRTIMER_MODE_REL, CLOCK_MONOTONIC);\r\n}<\/pre>\n<p>It copies the userspace data, check that the time is valid and call hrtimer_nanosleep.<\/p>\n<pre class=\"prettyprint linenums\">long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp,\r\n\t\t       const enum hrtimer_mode mode, const clockid_t clockid)\r\n{\r\n\tstruct restart_block *restart;\r\n\tstruct hrtimer_sleeper t;\r\n\tint ret = 0;\r\n\tunsigned long slack;\r\n\r\n\tslack = current-&gt;timer_slack_ns;\r\n\tif (rt_task(current))\r\n\t\tslack = 0;\r\n\r\n\thrtimer_init_on_stack(&amp;t.timer, clockid, mode);\r\n\thrtimer_set_expires_range_ns(&amp;t.timer, timespec_to_ktime(*rqtp), slack);\r\n\tif (do_nanosleep(&amp;t, mode))\r\n\t\tgoto out;\r\n\r\n\t\/* Absolute timers do not update the rmtp value and restart: *\/\r\n\tif (mode == HRTIMER_MODE_ABS) {\r\n\t\tret = -ERESTARTNOHAND;\r\n\t\tgoto out;\r\n\t}\r\n\r\n\tif (rmtp) {\r\n\t\tret = update_rmtp(&amp;t.timer, rmtp);\r\n\t\tif (ret &lt;= 0)\r\n\t\t\tgoto out;\r\n\t}\r\n\r\n\trestart = &amp;current_thread_info()-&gt;restart_block;\r\n\trestart-&gt;fn = hrtimer_nanosleep_restart;\r\n\trestart-&gt;nanosleep.clockid = t.timer.base-&gt;clockid;\r\n\trestart-&gt;nanosleep.rmtp = rmtp;\r\n\trestart-&gt;nanosleep.expires = hrtimer_get_expires_tv64(&amp;t.timer);\r\n\r\n\tret = -ERESTART_RESTARTBLOCK;\r\nout:\r\n\tdestroy_hrtimer_on_stack(&amp;t.timer);\r\n\treturn ret;\r\n}<\/pre>\n<p>It initialize a timer and call do_nanosleep giving it the timer. After do_nanosleep it will destroy\/free the timer structure.<\/p>\n<pre class=\"prettyprint linenums\">static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mode)\r\n{\r\n\thrtimer_init_sleeper(t, current);\r\n\r\n\tdo {\r\n\t\tset_current_state(TASK_INTERRUPTIBLE);\r\n\t\thrtimer_start_expires(&amp;t-&gt;timer, mode);\r\n\t\tif (!hrtimer_active(&amp;t-&gt;timer))\r\n\t\t\tt-&gt;task = NULL;\r\n\r\n\t\tif (likely(t-&gt;task))\r\n\t\t\tschedule();\r\n\r\n\t\thrtimer_cancel(&amp;t-&gt;timer);\r\n\t\tmode = HRTIMER_MODE_ABS;\r\n\r\n\t} while (t-&gt;task &amp;&amp; !signal_pending(current));\r\n\r\n\t__set_current_state(TASK_RUNNING);\r\n\r\n\treturn t-&gt;task == NULL;\r\n}<\/pre>\n<p>This is the interesting part.<\/p>\n<p>&nbsp;<\/p>\n<p>The first thing done is calling hrtimer_init_sleeper(); which will set parameters of the timer to call the function hrtimer_wakeup when the timer expires, and specify the task to wakeup at that time. hrtimer_wakeup will simply set the task to NULL and call wake_up_process() on that task. We&#8217;ll come back to wake_up_process(task) later.<\/p>\n<p>&nbsp;<\/p>\n<p>We see that after that the state is changed to TASK_INTERRUPTIBLE. Then it starts the timer strictly speaking, and go in schedule().<\/p>\n<p>&nbsp;<\/p>\n<p>Remember that schedule(), before scheduling a new task, will test the state of the current (which is now &#8220;previous&#8221;) task, and if the state is not TASK_RUNNING, it will remove that task from its runqueue (and that is the case as it is in the TASK_INTERRUPTIBLE state).<\/p>\n<p>&nbsp;<\/p>\n<p>Schedule() goes on and start scheduling another task, if there is not, it will run the &#8220;idle&#8221; task.<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"line-height: 1.5;\">At some point, the timer will expire. The timer rely on an hardware timer which will cause an interrupt, leaving any current task to process the interrupt handler (this is not the schedule() handler and so on !). The interrupt handler for the hardware timer is \u00a0hrtimer_interrupt() which will run\u00a0<\/span><span style=\"line-height: 1.5;\">hrtimer_wakeup() for all expired timer. As said before, outr timer for the sleeping mechanism will set the timer task as NULL, and call wake_up_process(). But that functions just put back the process in the runqueue and set its state to TASK_RUNNING, not actually scheduling it. The interrupt handler will finish and the CPU will go back to it&#8217;s currently running process (maybe the idle process, running the cpu_idle() function).\u00a0<\/span><\/p>\n<p>&nbsp;<\/p>\n<p>That currently running process will eventually finish its processing or be preempted (the normal scheduling mechanism), and the process which was sleeping will re-run again when it will be picked again by pick_next_task().<\/p>\n<p>&nbsp;<\/p>\n<p>But where will this process restart? After the sleep() or usleep() call? No ! Where it was&#8230; in the nanosleep syscall. What does the syscall do after its call to schedule()? Put the state back to TASK_RUNNING and effectively running. That seems obvious as we said before that the syscall is taking care of destroying the timer structure, so it should not restart after the syscall.<\/p>\n<p>&nbsp;<\/p>\n<p>Note that I skipped the part where do_nanosleep() is looping and so on&#8230; I tried usleep(1) and sleep(1), and the loop never happend. I suspect it&#8217;s for very long timers, the kernel would&#8217;nt launch a timer of 1 hour long&#8230; But it&#8217;s just a guess.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The students of INFO-0940 were asked to remove a task from it&#8217;s current scheduler class when calling a new syscall and put it back when we call again that syscall. The question is : what to do if the task is currently sleeping? Cause the timer will expire at one moment and maybe put back &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/perso.uclouvain.be\/tom.barbette\/understanding-sleeping-mechanism-in-linux-kernel\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Understanding sleeping mechanism in Linux Kernel&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[18,16],"tags":[],"class_list":["post-301","post","type-post","status-publish","format-standard","hentry","category-os","category-unix"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Understanding sleeping mechanism in Linux Kernel - Tom Barbette<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/perso.uclouvain.be\/tom.barbette\/understanding-sleeping-mechanism-in-linux-kernel\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Understanding sleeping mechanism in Linux Kernel - Tom Barbette\" \/>\n<meta property=\"og:description\" content=\"The students of INFO-0940 were asked to remove a task from it&#8217;s current scheduler class when calling a new syscall and put it back when we call again that syscall. The question is : what to do if the task is currently sleeping? Cause the timer will expire at one moment and maybe put back &hellip; Continue reading &quot;Understanding sleeping mechanism in Linux Kernel&quot;\" \/>\n<meta property=\"og:url\" content=\"https:\/\/perso.uclouvain.be\/tom.barbette\/understanding-sleeping-mechanism-in-linux-kernel\/\" \/>\n<meta property=\"og:site_name\" content=\"Tom Barbette\" \/>\n<meta property=\"article:published_time\" content=\"2014-04-10T11:40:47+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2014-04-10T11:41:35+00:00\" \/>\n<meta name=\"author\" content=\"Tom Barbette\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@TomBarbette\" \/>\n<meta name=\"twitter:site\" content=\"@TomBarbette\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Tom Barbette\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/understanding-sleeping-mechanism-in-linux-kernel\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/understanding-sleeping-mechanism-in-linux-kernel\\\/\"},\"author\":{\"name\":\"Tom Barbette\",\"@id\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/#\\\/schema\\\/person\\\/7f791766092e1207bc7a94a65e74f4fd\"},\"headline\":\"Understanding sleeping mechanism in Linux Kernel\",\"datePublished\":\"2014-04-10T11:40:47+00:00\",\"dateModified\":\"2014-04-10T11:41:35+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/understanding-sleeping-mechanism-in-linux-kernel\\\/\"},\"wordCount\":668,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/#organization\"},\"articleSection\":[\"INFO-0940 Operating Systems\",\"Unix\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/understanding-sleeping-mechanism-in-linux-kernel\\\/\",\"url\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/understanding-sleeping-mechanism-in-linux-kernel\\\/\",\"name\":\"Understanding sleeping mechanism in Linux Kernel - Tom Barbette\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/#website\"},\"datePublished\":\"2014-04-10T11:40:47+00:00\",\"dateModified\":\"2014-04-10T11:41:35+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/understanding-sleeping-mechanism-in-linux-kernel\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/understanding-sleeping-mechanism-in-linux-kernel\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/understanding-sleeping-mechanism-in-linux-kernel\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Understanding sleeping mechanism in Linux Kernel\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/#website\",\"url\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/\",\"name\":\"Tom Barbette\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/#organization\",\"name\":\"Efficiency of Networked Systems Group\",\"url\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/wp-content\\\/uploads\\\/2022\\\/04\\\/cropped-logo-uclouvain-2021-barbette.png\",\"contentUrl\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/wp-content\\\/uploads\\\/2022\\\/04\\\/cropped-logo-uclouvain-2021-barbette.png\",\"width\":512,\"height\":512,\"caption\":\"Efficiency of Networked Systems Group\"},\"image\":{\"@id\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/x.com\\\/TomBarbette\",\"https:\\\/\\\/www.linkedin.com\\\/in\\\/tom-barbette\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/#\\\/schema\\\/person\\\/7f791766092e1207bc7a94a65e74f4fd\",\"name\":\"Tom Barbette\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/aecd99cba3b4d91d3775fde3219bfa362cd99acfbe95848ec303b9685f366fcd?s=96&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/aecd99cba3b4d91d3775fde3219bfa362cd99acfbe95848ec303b9685f366fcd?s=96&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/aecd99cba3b4d91d3775fde3219bfa362cd99acfbe95848ec303b9685f366fcd?s=96&r=g\",\"caption\":\"Tom Barbette\"},\"sameAs\":[\"https:\\\/\\\/www.tombarbette.be\"],\"url\":\"https:\\\/\\\/perso.uclouvain.be\\\/tom.barbette\\\/author\\\/tom\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Understanding sleeping mechanism in Linux Kernel - Tom Barbette","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/perso.uclouvain.be\/tom.barbette\/understanding-sleeping-mechanism-in-linux-kernel\/","og_locale":"en_US","og_type":"article","og_title":"Understanding sleeping mechanism in Linux Kernel - Tom Barbette","og_description":"The students of INFO-0940 were asked to remove a task from it&#8217;s current scheduler class when calling a new syscall and put it back when we call again that syscall. The question is : what to do if the task is currently sleeping? Cause the timer will expire at one moment and maybe put back &hellip; Continue reading \"Understanding sleeping mechanism in Linux Kernel\"","og_url":"https:\/\/perso.uclouvain.be\/tom.barbette\/understanding-sleeping-mechanism-in-linux-kernel\/","og_site_name":"Tom Barbette","article_published_time":"2014-04-10T11:40:47+00:00","article_modified_time":"2014-04-10T11:41:35+00:00","author":"Tom Barbette","twitter_card":"summary_large_image","twitter_creator":"@TomBarbette","twitter_site":"@TomBarbette","twitter_misc":{"Written by":"Tom Barbette","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/perso.uclouvain.be\/tom.barbette\/understanding-sleeping-mechanism-in-linux-kernel\/#article","isPartOf":{"@id":"https:\/\/perso.uclouvain.be\/tom.barbette\/understanding-sleeping-mechanism-in-linux-kernel\/"},"author":{"name":"Tom Barbette","@id":"https:\/\/perso.uclouvain.be\/tom.barbette\/#\/schema\/person\/7f791766092e1207bc7a94a65e74f4fd"},"headline":"Understanding sleeping mechanism in Linux Kernel","datePublished":"2014-04-10T11:40:47+00:00","dateModified":"2014-04-10T11:41:35+00:00","mainEntityOfPage":{"@id":"https:\/\/perso.uclouvain.be\/tom.barbette\/understanding-sleeping-mechanism-in-linux-kernel\/"},"wordCount":668,"commentCount":0,"publisher":{"@id":"https:\/\/perso.uclouvain.be\/tom.barbette\/#organization"},"articleSection":["INFO-0940 Operating Systems","Unix"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/perso.uclouvain.be\/tom.barbette\/understanding-sleeping-mechanism-in-linux-kernel\/","url":"https:\/\/perso.uclouvain.be\/tom.barbette\/understanding-sleeping-mechanism-in-linux-kernel\/","name":"Understanding sleeping mechanism in Linux Kernel - Tom Barbette","isPartOf":{"@id":"https:\/\/perso.uclouvain.be\/tom.barbette\/#website"},"datePublished":"2014-04-10T11:40:47+00:00","dateModified":"2014-04-10T11:41:35+00:00","breadcrumb":{"@id":"https:\/\/perso.uclouvain.be\/tom.barbette\/understanding-sleeping-mechanism-in-linux-kernel\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/perso.uclouvain.be\/tom.barbette\/understanding-sleeping-mechanism-in-linux-kernel\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/perso.uclouvain.be\/tom.barbette\/understanding-sleeping-mechanism-in-linux-kernel\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/perso.uclouvain.be\/tom.barbette\/"},{"@type":"ListItem","position":2,"name":"Understanding sleeping mechanism in Linux Kernel"}]},{"@type":"WebSite","@id":"https:\/\/perso.uclouvain.be\/tom.barbette\/#website","url":"https:\/\/perso.uclouvain.be\/tom.barbette\/","name":"Tom Barbette","description":"","publisher":{"@id":"https:\/\/perso.uclouvain.be\/tom.barbette\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/perso.uclouvain.be\/tom.barbette\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/perso.uclouvain.be\/tom.barbette\/#organization","name":"Efficiency of Networked Systems Group","url":"https:\/\/perso.uclouvain.be\/tom.barbette\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/perso.uclouvain.be\/tom.barbette\/#\/schema\/logo\/image\/","url":"https:\/\/perso.uclouvain.be\/tom.barbette\/wp-content\/uploads\/2022\/04\/cropped-logo-uclouvain-2021-barbette.png","contentUrl":"https:\/\/perso.uclouvain.be\/tom.barbette\/wp-content\/uploads\/2022\/04\/cropped-logo-uclouvain-2021-barbette.png","width":512,"height":512,"caption":"Efficiency of Networked Systems Group"},"image":{"@id":"https:\/\/perso.uclouvain.be\/tom.barbette\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/TomBarbette","https:\/\/www.linkedin.com\/in\/tom-barbette"]},{"@type":"Person","@id":"https:\/\/perso.uclouvain.be\/tom.barbette\/#\/schema\/person\/7f791766092e1207bc7a94a65e74f4fd","name":"Tom Barbette","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/aecd99cba3b4d91d3775fde3219bfa362cd99acfbe95848ec303b9685f366fcd?s=96&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/aecd99cba3b4d91d3775fde3219bfa362cd99acfbe95848ec303b9685f366fcd?s=96&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/aecd99cba3b4d91d3775fde3219bfa362cd99acfbe95848ec303b9685f366fcd?s=96&r=g","caption":"Tom Barbette"},"sameAs":["https:\/\/www.tombarbette.be"],"url":"https:\/\/perso.uclouvain.be\/tom.barbette\/author\/tom\/"}]}},"_links":{"self":[{"href":"https:\/\/perso.uclouvain.be\/tom.barbette\/wp-json\/wp\/v2\/posts\/301","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/perso.uclouvain.be\/tom.barbette\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/perso.uclouvain.be\/tom.barbette\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/perso.uclouvain.be\/tom.barbette\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/perso.uclouvain.be\/tom.barbette\/wp-json\/wp\/v2\/comments?post=301"}],"version-history":[{"count":5,"href":"https:\/\/perso.uclouvain.be\/tom.barbette\/wp-json\/wp\/v2\/posts\/301\/revisions"}],"predecessor-version":[{"id":319,"href":"https:\/\/perso.uclouvain.be\/tom.barbette\/wp-json\/wp\/v2\/posts\/301\/revisions\/319"}],"wp:attachment":[{"href":"https:\/\/perso.uclouvain.be\/tom.barbette\/wp-json\/wp\/v2\/media?parent=301"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/perso.uclouvain.be\/tom.barbette\/wp-json\/wp\/v2\/categories?post=301"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/perso.uclouvain.be\/tom.barbette\/wp-json\/wp\/v2\/tags?post=301"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}