<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>LD — Little Step</title>
    <link>https://rex.writeas.com/tag:LD</link>
    <description></description>
    <pubDate>Sat, 14 Nov 2026 00:36:58 +0000</pubDate>
    <item>
      <title>Buffer mode change</title>
      <link>https://rex.writeas.com/buffer-mode-change?pk_campaign=rss-feed</link>
      <description>I am using ppscheck to monitor with PPS status recently, and found a problem for the tool. The task is quite simple. It checks the PPS st...</description>
      <content:encoded><![CDATA[<p>I am using <code>ppscheck</code> to monitor with PPS status recently, and found a problem for the tool.</p>

<p>The task is quite simple. It checks the PPS status, with <code>ppscheck</code>, queries the output periodically, and it is done with Python subprocess.</p>

<p>Sample code is like this:</p>

<pre><code class="language-python">cmd = &#34;sudo ppscheck /dev/ttyS0&#34;
args = shlex.split(cmd)
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
fd = proc.stdout.fileno()

poller = select.epoll()
poller.register(fd, select.EPOLLIN)
</code></pre>

<p>I create a process and register the stdout fd to epoll, but after the running the fd is not readable.</p>

<p>I try to execute the command by shell, the output is normal. And then I redirect all the outputs to one file, but find the file is empty!</p>

<pre><code class="language-shell"># running OK
$ sudo ppscheck /dev/ttyS0

# redirect all output to file, file is empty
$ sudo ppscheck /dev/ttyS0 &gt; pps.log 2&gt;&amp;1
$ cat pps.log
$
</code></pre>

<p>The first thing came to me is that the <code>ppscheck</code> tool buffers the output, and furthermore the mode is not line buffered.</p>

<p>And after some search I find <code>stdbuf</code> can be use to change the buffer mode of a program.</p>

<p>After add “stdbuf -oL” to the command, and the problem is solved.</p>

<pre><code class="language-python">cmd = &#34;sudo stdbuf -oL ppscheck /dev/ttyS0&#34;
</code></pre>

<p>And now I am wondering how <code>stdbuf</code> is implemented to achieve this goal.</p>

<p>It may read the output of the program, and when receive newline and then make a flush() call? Then I realize it can&#39;t be done in this way, since the original program is buffered, you will wait until the buffer is full and produce the outputs.</p>

<p>So I check the source code of <a href="https://github.com/coreutils/coreutils/blob/00ea4bacf6063ccc125209d5186f8f2382c6f0d4/src/stdbuf.c" rel="nofollow ugc">stdbuf</a>, the main code is as following:</p>

<pre><code class="language-c">/* main function */
if (! set_libstdbuf_options ())
  {
    error (0, 0, _(&#34;you must specify a buffering mode option&#34;));
    usage (EXIT_CANCELED);
  }

/* Try to preload libstdbuf first from the same path as
   stdbuf is running from.  */
set_program_path (program_name);
if (!program_path)
  program_path = xstrdup (PKGLIBDIR);  /* Need to init to non-NULL.  */
set_LD_PRELOAD ();
free (program_path);

execvp (*argv, argv);

int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;
error (0, errno, _(&#34;failed to run command %s&#34;), quote (argv[0]));
return exit_status;

/* set_libstdbuf_options */
if (*stdbuf[i].optarg == &#39;L&#39;)
  ret = asprintf (&amp;var, &#34;%s%c=L&#34;, &#34;_STDBUF_&#34;,
                  toupper (stdbuf[i].optc));
else
  ret = asprintf (&amp;var, &#34;%s%c=%&#34; PRIuMAX, &#34;_STDBUF_&#34;,
                  toupper (stdbuf[i].optc),
                  (uintmax_t) stdbuf[i].size);
if (ret &lt; 0)
  xalloc_die ();

if (putenv (var) != 0)
{
  die (EXIT_CANCELED, errno,
       _(&#34;failed to update the environment with %s&#34;),
       quote (var));
}
</code></pre>

<p>The above logic is very simple, basically it only set buffer options, and then executed the program with <code>execvp</code>.</p>

<p>The key part is how the buffer mode is set, the program use a nice way to archive this, via <a href="https://www.baeldung.com/linux/ld_preload-trick-what-is" rel="nofollow ugc">LD_PRELOAD</a> trick.</p>

<p>The stdbuf has a nother part called libstd.</p>

<p>Stdbuf set two kinds of environment variables before run the program. The first one is the buffer mode, variables are <code>_STDBUF_I</code>, <code>_STDBUF_O</code> and <code>_STDBUF_E</code> for stdin, stdout and stderr. The second one is the <code>LD_PRELOAD</code> environment variable, adding <code>libstdbuf</code>. And after <code>libstdbuf</code> is loaded, the buffer mode is set, the code is as following:</p>

<pre><code class="language-c">/* Use __attribute to avoid elision of __attribute__ on SUNPRO_C etc.  */
static void __attribute ((constructor))
stdbuf (void)
{
  char *e_mode = getenv (&#34;_STDBUF_E&#34;);
  char *i_mode = getenv (&#34;_STDBUF_I&#34;);
  char *o_mode = getenv (&#34;_STDBUF_O&#34;);
  if (e_mode) /* Do first so can write errors to stderr  */
    apply_mode (stderr, e_mode);
  if (i_mode)
    apply_mode (stdin, i_mode);
  if (o_mode)
    apply_mode (stdout, o_mode);
}

/* core part of apply_mode */
if (setvbuf (stream, buf, setvbuf_mode, size) != 0)
  {
    fprintf (stderr, _(&#34;could not set buffering of %s to mode %s\n&#34;),
             fileno_to_name (fileno (stream)), mode);
    free (buf);
  }
</code></pre>

<p>The <code>__attribute ((constructor))</code> is the entry point of shared library in GCC.</p>

<p>So when <code>libstdbuf.so</code> is loaded, <code>stdbuf</code> function is called and buffer mode is set.</p>

<p>And after that, the original program is executed normally.</p>

<p>#stdbuf <a href="https://rex.writeas.com/tag:libstdbuf" class="hashtag" rel="nofollow ugc"><span>#</span><span class="p-category">libstdbuf</span></a> <a href="https://rex.writeas.com/tag:ld" class="hashtag" rel="nofollow ugc"><span>#</span><span class="p-category">ld</span></a> <a href="https://rex.writeas.com/tag:LD" class="hashtag" rel="nofollow ugc"><span>#</span><span class="p-category">LD</span></a>\_PRELOAD <a href="https://rex.writeas.com/tag:ppscheck" class="hashtag" rel="nofollow ugc"><span>#</span><span class="p-category">ppscheck</span></a> <a href="https://rex.writeas.com/tag:coreutil" class="hashtag" rel="nofollow ugc"><span>#</span><span class="p-category">coreutil</span></a></p>
]]></content:encoded>
      <guid>https://rex.writeas.com/buffer-mode-change</guid>
      <pubDate>Sun, 17 Jan 2021 07:12:47 +0000</pubDate>
    </item>
  </channel>
</rss>