Posted by Bo Yang at 2015-08-31 with tags Notes , Unix/Linux
Since CentOS 7(or RedHat 7) is quite different from CentOS 6.x, most notes online for installing TFTP server in CentOS are obsolete already. This post not only summarizes the procedure of installing & configuring TFTP server, but also introduces a general strategy of configuring network services in CentOS 7.
tftp-server
TFTP server can be installed using following command, where
xinetd
is necessary.
yum install tftp tftp-server* xinetd*
Then edit
/etc/xinetd.d/tftp
- set
disable
to
no
and add
-c
option into
server_args
if you need to upload files to TFTP server from client.
service tftp
{
socket_type = dgram
protocol = udp
wait = yes
user = root
server = /usr/sbin/in.tftpd
server_args = -c -s /tftpboot
disable = no
per_source = 11
cps = 100 2
flags = IPv4
}
The CentOS 7 services(
systemd
) can be configured from files under
/usr/lib/systemd/system/
. Go to this dir, and edit
tftp.service
as follows:
[root@localhost system]# cat tftp.service
[Unit]
Description=Tftp Server
[Service]
ExecStart=/usr/sbin/in.tftpd -c -s /tftpboot
StandardInput=socket
[Install]
WantedBy=multi-user.target
The default
tftp.service
doesn’t have the
[Install]
unit, but it’s required by
systemd
. Besides, the tftpd options also need to be changed in the
ExecStart
entry.
Although
service
commands are deprecated in CentOS 7, they are still available but simply redirected to
systemctl
. So you still can use
service xinetd start
and
service tftp start
to start
xinetd
and TFTP.
However, to make them automatically start after boot, following commands are needed:
[root@localhost system]# systemctl enable xinetd
[root@localhost system]# systemctl enable tftp
After these two commands, permanent links will be made for
xinetd
and TFTP services.
In CentOS 7, the SELinux is not supposed to be disabled(the system will abort booting if you disable SELinux). So the TFTP read and write must be allowed in SELinux. By default, the SELinux uses
enforcing
policy, which does not accept any change. To make any change to SELinux, first modify
/etc/selinux/config
and change the policy to
permissive
:
[bo@ucs-c200 notes]$ cat /etc/selinux/config
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
# enforcing - SELinux security policy is enforced.
# permissive - SELinux prints warnings instead of enforcing.
# disabled - No SELinux policy is loaded.
SELINUX=permissive
# SELINUXTYPE= can take one of three two values:
# targeted - Targeted processes are protected,
# minimum - Modification of targeted policy. Only selected processes are protected.
# mls - Multi Level Security protection.
SELINUXTYPE=targeted
Then reboot the system. After system boot up, check SELinux status:
[root@localhost system]# sestatus
SELinux status: enabled
SELinuxfs mount: /sys/fs/selinux
SELinux root directory: /etc/selinux
Loaded policy name: targeted
Current mode: permissive
Mode from config file: permissive
Policy MLS status: enabled
Policy deny_unknown status: allowed
Max kernel policy version: 28
Then check the tftp permissions in SELinux:
[root@localhost bobyan]# getsebool -a | grep tftp
tftp_anon_write --> off
tftp_home_dir --> off
If the TFTP write is off as shown above, enable it with
setsebool
command:
[root@localhost bobyan]# setsebool -P tftp_anon_write 1
[root@localhost bobyan]# setsebool -P tftp_home_dir 1
Above changes to SELinux are permanent, so no need to change any SELinux config files any more.
firewalld
Unlike CentOS 6.x, the
firewalld
is used to replace
iptables
as default firewall in CentOS 7. Fortunately,
iptable
config file
/etc/sysconfig/iptables
is also used by
firewalld
. So to allow TFTP services, following line should be added to
/etc/sysconfig/iptables
-A INPUT -m state --state NEW -m udp -p udp -m udp --dport 69 -j ACCEPT
Then restart
firewalld
using command
firewall-cmd --reload
.
A more standard way to allow TFTP is to use
firewall-cmd
command:
firewall-cmd --zone=public --add-service=tftp --permanent
Where the
--permanent
option is used to permanently enable the TFTP port. Command
firewall-cmd --reload
is needed every time changing the firewall config.
To check the status or enable
firewalld
, following commands can be used:
systemctl status firewalld
systemctl enable firewalld
systemctl start firewalld
Posted by Bo Yang at 2015-08-20 with tags Notes , Unix/Linux
This post introduces how to configure and use syslogd-compatible syslog tools. These tips should be supported by rsyslog, but rsyslog-specific commands are not covered.
As documented in the man page, the Linux system log is configured in file
/etc/syslog.conf
by default.You can specify other config file with
-f
option for syslogd. The format of syslog config file is
<facility>.<priority> [logfile]
The supported facilities and priorites are defined in
syslog.h
:
#define LOG_EMERG 0 /* system is unusable */
#define LOG_ALERT 1 /* action must be taken immediately */
#define LOG_CRIT 2 /* critical conditions */
#define LOG_ERR 3 /* error conditions */
#define LOG_WARNING 4 /* warning conditions */
#define LOG_NOTICE 5 /* normal but significant condition */
#define LOG_INFO 6 /* informational */
#define LOG_DEBUG 7 /* debug-level messages */
/* facility codes */
#define LOG_KERN (0<<3) /* kernel messages */
#define LOG_USER (1<<3) /* random user-level messages */
#define LOG_MAIL (2<<3) /* mail system */
#define LOG_DAEMON (3<<3) /* system daemons */
#define LOG_AUTH (4<<3) /* security/authorization messages */
#define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */
#define LOG_LPR (6<<3) /* line printer subsystem */
#define LOG_NEWS (7<<3) /* network news subsystem */
#define LOG_UUCP (8<<3) /* UUCP subsystem */
#define LOG_CRON (9<<3) /* clock daemon */
#define LOG_AUTHPRIV (10<<3) /* security/authorization messages (private) */
#define LOG_FTP (11<<3) /* ftp daemon */
/* other codes through 15 reserved for system use */
#define LOG_LOCAL0 (16<<3) /* reserved for local use */
#define LOG_LOCAL1 (17<<3) /* reserved for local use */
#define LOG_LOCAL2 (18<<3) /* reserved for local use */
#define LOG_LOCAL3 (19<<3) /* reserved for local use */
#define LOG_LOCAL4 (20<<3) /* reserved for local use */
#define LOG_LOCAL5 (21<<3) /* reserved for local use */
#define LOG_LOCAL6 (22<<3) /* reserved for local use */
#define LOG_LOCAL7 (23<<3) /* reserved for local use */
The local facilities can be used for redirecting/filtering the log of your own programs. For example, given a program
foo
, if you want to log all the non-critical messages in
/var/log/foo.log
, and make the critical logs go to system log file
/var/log/messages
, you can use the following config file
# use facility local1 for foo logs
local1.debug;local1.info;local1.notice;local1.warn -/var/log/foo.log
local1.panic;local1.alert;local1.crit;local1.err -/var/log/messages
*.*;local1.none /var/log/messages
The special priority
none
prevents those messages from being logged even though they would have been included in the
.
. In the above config, all facilities except for
local1
will be logged to
/var/log/messages
.
However, the dash(-) in front of the log filename is not documented in the man page, but it turns out to mean “Don’t sync after every write to the file”. Except that rsyslogd won’t sync anyway, unless you add a special directive in the Global Directives section. Note that you might lose information if the system crashes right behind a write attempt. Nevertheless this might give you back some performance, especially if you run programs that use logging in a very verbose manner. So for most people, a dash makes no difference one way or the other – it will be ignored.
And in program
foo
, what you need to do is open log file by specifing
LOG_LOCAL1
facility. The use of
openlog()
is mandatory here. Otherwise, it will automatically be called by syslog(), in which case facility will default to
LOG_USER
.
#include <syslog.h>
int main(int, char**)
{
openlog("foo", 0, LOG_LOCAL1);
syslog(LOG_INFO, "test info log"); /* go to /var/log/foo.log */
syslog(LOG_ERR, "test error log"); /* go to /var/log/messages */
closelog();
}
Posted by Bo Yang at 2015-05-27 with tags Notes , Unix/Linux
In Linux, there are two kinds of crashes - kernel panic/oom and user space core dump. For kernel panic, the standard config is rebooting the system. Unfortunately, the panic log can only be printed to console and will disappear after reboot if there is no additional device to record the console log - most kernel panic/oom logs won’t be written to system log, and even they do, there is no way to sync them to disk storage during panic.
As for user-sapce core dump, core files will be generated to
/tmp/pid.core
(core pattern can be changed in
/proc/sys/kernel/core_pattern
) by default. It is up to the admin to decide if the system or process needs reboot after core dump. Although a script can be used to record more logs for the coredump, sometimes it’s still useful to retain some info in the persistent memory, like the backtrace of the coredumped process.
phram
is a Memory Technology Device(MTD) driver, which supports accessing non-system memory, i.e. from the PCI address space. This module can act as a special memory file to store important data during reboots.
ramoops
is an kernel oops/panic logger that writes its logs to predefined memory area before the system crashes. It works by logging oopses and panics in a circular buffer. As long as the device has power supply during reboot, the content stored in ramoops won’t go away.
Both
phram
and
ramoops
can be compiled to
.ko
shared library, which can be configured using Linux menuconfig.
phram
can be dynamically loaded using following command:
insmod /lib/modules/phram.ko phram=<name>,<addr>,<len>
where
<name>
is the device(i.e. file) name under
/dev/mtdchar/
,
<addr>
is the reserved starting memory address, and
<len>
is the predefined length of the memory area.
To load
ramoops
insmod /lib/modules/ramoops.ko mem_address=<addr> mem_size=<len> [record_size=<chunks>]
where
<addr>
and
<len>
have the same meaning as above, and
record_size
is the chunks of reserved memory area.
phram
Memory
For kernel panic, the ramoops will automatically copy the panic log into the reserved memory. The panic log begins with leading “====” followed by a timestamp and a new line.
If you need to store other info,
dd
command can be used to copy file to the memory, e.g.
dd if=/var/log/messages bs=1 count=65536 skip=<fsize - len> of=/dev/mtdchar/phram-oops
According to
dd
manual,
if
is the the input file stream,
of
is the output file,
bs
specifies the bytes to be read and write at a time, and
skip
configs the input blocks to be skipped from the input file. Here the size of
block
is defined by
bs
. In the above example, the block size is set to 1 byte, and only the last
len
bytes will be copied to
phram
memory.
Unfortunately, reading & writing byte-by-byte usaully is very slow. A faster way is setting
bs
to a larger number, like 128, 512, 1024, etc. In this case, the
skip
and
count
need to be calculated correspondingly: say the size of reserved memory is
len
, input file size is
fsize
,
bs
is set to
bsize
, then
// len: memory size
// fsize: input file size
bs = bsize
if (fsize <= bsize)
skip = 0;
else
skip = (fsize - len)/bsize + 1;
count = len/bsize + 1;
dd
command can also be used for dumping data from reserved phram memory area, e.g.
dd if=/dev/mtdchar/phram-oops bs=<len> count=1
Other file operation commands/APIs can also be used for
phram
memory device.
phram
Memory
phram
memory can be cleared by file operation coomands/APIS, e.g.
dd if=/dev/zero bs=<len> count=1 of=/dev/mtdchar/phram-oops
Posted by Bo Yang at 2015-04-02 with tags Notes , Unix/Linux
As explained in previous post Linux System Log , threre is no way to directly change the default timestamps printed on Linux console. However, it is still possible to get human readable timestamp without using fancy syslog tools like syslog-ng .
A bash script of customizing Linux dmesg timestamp is also given in my post post Linux System Log . Unfortunately, that script won’t work if bash is not available in your Linux system. For example, the default shell in OpenWRT is Busybox ash, which lacks many powerful features of bash. The most significant shortcoming for ash is the lame arithmetic operations. In addition, many useful options for standard commands are not supported in Busybox ash. Fortunately, we have sed and awk installed in OpenWRT. So the arithmetic operations can be done by awk.
The basic idea of customizing console syslog timestamp is periodically calling
dmesg -c
, which clears system circular buffer after dumping the system log. In order to also store the log in syslog file(like /var/log/messages), the dumped message also needs to be redirected(or appending) to the syslog file. The timestamps of
dmesg
can be replaced with a human readable one. And the required addition/subtraction is implemented by awk in a very special way.
Although
tail -f /var/log/messages
would automatically stop after some time, the
-F
option works for ash tail command, i.e.
tail -F /var/log/messages
.
Following is the source code in
ash
:
#!/bin/ash
#
# This script is used to tail the latest syslogs to stdout,
# syslog file and specified log file.
#
SYSLOG=/var/log/messages
custom_log=
[ ! -z "$1" ] && custom_log=$1
base=$(cut -d" " -f1 /proc/uptime);
ut=$(date +%s);
# FIXME: Arithmetic operations are not fully supported in ash, use awk instead.
base=`date | awk "{now=$ut - $base; printf \"%d\", now}"`
dmesg -c >> $SYSLOG # clear circular syslog buffer
while true
do
dmesg -c | sed "s/^\[[ ]*\?\([0-9.]*\)\] \(.*\)/\\1 \\2/" |
while read ts msg; do
now=`date | awk "{now=$base + $ts; printf \"%d\", now}"`
newts=`date +"%m/%d/%Y %H:%M:%S" --date "@$now"` # human readable timestamp
printf "[%s] %s\n" "$newts" "$msg";
done | sed "s/$/$(printf '\r')/" | tee -a $SYSLOG $custom_log
sleep 1
done
Posted by Bo Yang at 2015-03-30 with tags Notes , Unix/Linux
Memory leak can be detected by monitoring the free memory periodically. Command
free
can be used to show rough memory usage. A more detailed way to analyze memroy is
cat /proc/meminfo
and
cat /proc/slabinfo
.
/proc/meminfo
contains info about total memory, free memory, total highmem, free highmem, total lowmem, free lowmem and etc. Usually highmem is user-space memory, while lowmem is kernel-space memory. If free highmem(HighFree) or free lowmem(LowFree) is continuously decreasing, in most cases it means user space or kernel space memory leaking.
Once detected memory leaking, we need to determine which slab(s) is(are) leaking. This can be done by monitoring
/proc/slabinfo
. If the number of a slab’s active objects(column 2) or total objects(column 3) keep increasing, then this slab is very likely leaking memory.
Following script can be used to monitor both meminfo and slabinfo:
#!/bin/sh
MAX_SIZE=20000000
MON_FILE=/path/to/monitor_output_$(uname -n)
while true
do
date >> $MON_FILE
cat /proc/meminfo >> $MON_FILE
echo "----------------------" >> $MON_FILE
cat /proc/slabinfo >> $MON_FILE
echo "----------------------" >> $MON_FILE
ps -o pid,comm,stat,time,rss,vsz >> $MON_FILE
echo "++++++++++++++++++++++" >> $MON_FILE
fsize=`ls -l $MON_FILE | awk '{print $5}'`
if [ $fsize -gt $MAX_SIZE ]; then
# upload file to TFTP server
suffix=`cat /proc/uptime | cut -d" " -f1`
mv $MON_FILE $MON_FILE.$suffix
# upload to cloud
fi
sleep 300
done
After continuously monitoring for hours, simply grep some keywords(e.g. HighFree, LowFree, kmalloc-192, etc.) could find the trend of memory usage. Following data is extracted from a real memory leak monitoring log:
LowFree: 599392 kB
LowFree: 571072 kB
LowFree: 544484 kB
LowFree: 516832 kB
LowFree: 489232 kB
LowFree: 462280 kB
LowFree: 433680 kB
LowFree: 405244 kB
LowFree: 378572 kB
LowFree: 350136 kB
LowFree: 322648 kB
LowFree: 295824 kB
LowFree: 267532 kB
LowFree: 238272 kB
LowFree: 210856 kB
LowFree: 181148 kB
LowFree: 153652 kB
LowFree: 123148 kB
LowFree: 599392 kB
LowFree: 571072 kB
LowFree: 544484 kB
LowFree: 181148 kB
LowFree: 153652 kB
LowFree: 123148 kB
LowFree: 94548 kB
LowFree: 94548 kB
After finding out the leaking slab, more info could be detected by tracing that slab. If the kernel was built with option
CONFIG_SLUB_DEBUG
, the simplest way is to issue command
echo 1 > /sys/kernel/slab/<leaking_slab>/trace
. Then the memory allocation trace for this slab will be printed to the console:
[ 375.201468] TRACE kmalloc-4096 alloc 0xe6be6000 inuse=8 fp=0x (null)
[ 375.207872] Backtrace:
[ 375.210309] [<c0012378>] (dump_backtrace+0x0/0x114) from [<c03a0a5c>] (dump_stack+0x18/0x1c)
[ 375.218712] r6:ef300480 r5:e6be6000 r4:c0d40c00 r3:c07004c4
[ 375.224367] [<c03a0a44>] (dump_stack+0x0/0x1c) from [<c03a2738>] (alloc_debug_processing+0xc8/0x164)
[ 375.233489] [<c03a2670>] (alloc_debug_processing+0x0/0x164) from [<c03a2d0c>] (__slab_alloc.isra.50.constprop.56+0x538/0x5dc)
[ 375.244767] r7:80080008 r6:00080007 r5:e6be6000 r4:c0d40c00
[ 375.250421] [<c03a27d4>] (__slab_alloc.isra.50.constprop.56+0x0/0x5dc) from [<c00e872c>] (__kmalloc_track_caller+0xbc/0x190)
[ 375.261605] [<c00e8670>] (__kmalloc_track_caller+0x0/0x190) from [<c031f96c>] (__alloc_skb+0x58/0xf4)
[ 375.270790] [<c031f914>] (__alloc_skb+0x0/0xf4) from [<c03200d4>] (dev_alloc_skb+0x40/0x64)
[ 375.279131] [<c0320094>] (dev_alloc_skb+0x0/0x64) from [<bf6bb504>] (__adf_nbuf_alloc+0x24/0xa4 [adf])
[ 375.288409] r4:ea5c8600 r3:00000004
[ 375.292158] [<bf6bb4e0>] (__adf_nbuf_alloc+0x0/0xa4 [adf]) from [<bf8d5f40>] (htt_rx_ring_fill_n+0x34/0x108 [umac])
[ 375.302405] r7:00000000 r6:000005b1 r5:ea5c8720 r4:ea5c8600
[ 375.308372] [<bf8d5f0c>] (htt_rx_ring_fill_n+0x0/0x108 [umac]) from [<bf8d6888>] (htt_rx_msdu_buff_replenish+0x54/0x6c [umac])
[ 375.319400] r8:bf927c04 r7:eaaee9c0 r6:eb5b3c00 r5:ea5c8720 r4:ea5c8600
[ 375.326429] [<bf8d6834>] (htt_rx_msdu_buff_replenish+0x0/0x6c [umac]) from [<bf8c6b24>] (ol_rx_indication_handler+0x7bc/0x8cc [umac])
[ 375.338081] r5:ea5c8600 r4:00000000
[ 375.341955] [<bf8c6368>] (ol_rx_indication_handler+0x0/0x8cc [umac]) from [<bf8d770c>] (htt_t2h_msg_handler_fast+0xac/0x280 [umac])
[ 375.353764] [<bf8d7660>] (htt_t2h_msg_handler_fast+0x0/0x280 [umac]) from [<bf8c02dc>] (CE_per_engine_service_each+0x178/0x4b4 [umac])
[ 375.365823] [<bf8c0164>] (CE_per_engine_service_each+0x0/0x4b4 [umac]) from [<bf8c3634>] (ath_tasklet+0x68/0x128 [umac])
[ 375.376507] [<bf8c35cc>] (ath_tasklet+0x0/0x128 [umac]) from [<c0064478>] (tasklet_action+0xa0/0x11c)
[ 375.385567] r6:e8b88000 r5:c435ef44 r4:c435ef40
[ 375.390159] [<c00643d8>] (tasklet_action+0x0/0x11c) from [<c006495c>] (__do_softirq+0x140/0x34c)
[ 375.398937] [<c006481c>] (__do_softirq+0x0/0x34c) from [<c0064d38>] (do_softirq+0x4c/0x58)
[ 375.407185] [<c0064cec>] (do_softirq+0x0/0x58) from [<c0064dd0>] (local_bh_enable_ip+0x8c/0xcc)
[ 375.415870] r4:e8b88000 r3:0000004a
[ 375.419431] [<c0064d44>] (local_bh_enable_ip+0x0/0xcc) from [<c03aa68c>] (_raw_spin_unlock_bh+0x54/0x58)
[ 375.428865] r5:00000304 r4:e9662c00
[ 375.432427] [<c03aa638>] (_raw_spin_unlock_bh+0x0/0x58) from [<c038a47c>] (packet_poll+0xa4/0xe4)
[ 375.441299] [<c038a3d8>] (packet_poll+0x0/0xe4) from [<c0316e34>] (sock_poll+0x24/0x28)
[ 375.449265] r7:ea9ffe40 r6:00000000 r5:e8b89c4c r4:e8b89c04
[ 375.454920] [<c0316e10>] (sock_poll+0x0/0x28) from [<c00fc914>] (do_sys_poll+0x20c/0x3e8)
[ 375.463074] [<c00fc708>] (do_sys_poll+0x0/0x3e8) from [<c00fcbb0>] (sys_poll+0x64/0xd0)
[ 375.471071] [<c00fcb4c>] (sys_poll+0x0/0xd0) from [<c000e7c0>] (ret_fast_syscall+0x0/0x30)
[ 375.479318] r6:0007a120 r5:00000000 r4:6b3f60a0
The first line could only be “TRACE kmalloc-4096 alloc” or “free”, which logs the entry address of this slab. So if the memory leak is very fast, it is possible to monitor all of the alloc/free slabs before the system out of memory. Then find out addresses that never freed, analyze the call traces, and hopefull we could detect the problematic module or functions.
Posted by Bo Yang at 2015-01-12 with tags Notes , Unix/Linux
Linux adopts a ring buffer in kernel with a size of
__LOG_BUF_LEN
bytes to store system logs, where
__LOG_BUF_LEN
equals (
1 << CONFIG_LOG_BUF_SHIFT
) (see
kernel/printk.c
for details). Using a ring buffer implies that older messages get overwritten once the buffer fills up, but this is only a minor drawback compared to the robustness of this solution (i.e. minimum memory footprint, callable from every context, not many resources wasted if nobody reads the buffer, no filling up of disk space/ram when some kernel process goes wild and spams the buffer, …). Using a reasonably large buffer size should give you enough time to read your important messages before they are overwritten.
The kernel log buffer is accessible for reading from userspace by
/proc/kmsg
.
/proc/kmsg
behaves more or less like a FIFO and blocks until new messages appear. Please note, reading from
/proc/kmsg
consumes the messages in the ring buffer so they may not be available for other programs. It is usually a good idea to let
klogd
or
syslog
do this job and read the content of the buffer via dmesg.
printk
printk
is the kernel function to classify messages according to their severity by loglevels and write them to the circular system message buffer. The function then wakes any process that is waiting for messages, that is, any process that is sleeping in the
syslog
system call or that is reading
/proc/kmsg
.
printk
can be invoked from anywhere, even from an interrupt handler, with no limit on how much data can be printed.
printk( KERN_CRIT "Error code %08x.\n", val );
There are eight possible loglevel strings, defined in the header <linux/kernel.h>; we list them in order of decreasing severity:
| Name | String | Meaning | alias function |
|---|---|---|---|
| KERN_EMERG | “0” | Emergency messages, system is about to crash or is unstable | pr_emerg |
| KERN_ALERT | “1” | Something bad happened and action must be taken immediately | pr_alert |
| KERN_CRIT | “2” | A critical condition occurred like a serious hardware/software failure | pr_crit |
| KERN_ERR | “3” | An error condition, often used by drivers to indicate difficulties with the hardware | pr_err |
| KERN_WARNING | “4” | A warning, meaning nothing serious by itself but might indicate problems | pr_warning |
| KERN_NOTICE | “5” | Nothing serious, but notably nevertheless. Often used to report security events. | pr_notice |
| KERN_INFO | “6” | Informational message e.g. startup information at driver initialization | pr_info |
| KERN_DEBUG | “7” | Debug messages | pr_debug, pr_devel if DEBUG is defined |
| KERN_DEFAULT | “d” | The default kernel loglevel | |
| KERN_CONT | ”” |
“continued” line of log printout (only done after a line that had no enclosing
\n
)
|
pr_cont |
Each string (in the macro expansion) represents an integer in angle brackets. Integers range from 0 to 7, with smaller values representing higher priorities.
A
printk
statement with no specified priority defaults to
DEFAULT_MESSAGE_LOGLEVEL
, specified in
kernel/printk.c
as an integer. For this the kernel compares the log level of the message to the
console_loglevel
(a kernel variable) and if the priority is higher (i.e. a lower value) than the console_loglevel the message will be printed to the current console. The
console_loglevel
can be checked by
# cat /proc/sys/kernel/printk
7 4 1 7
The first integer shows you your current
console_loglevel
; the second is the
DEFAULT_MESSAGE_LOGLEVEL
.
Kernel log timestamp is added by
vprintk()
, in
kernel/printk.c
:
#if defined(CONFIG_PRINTK_TIME)
static bool printk_time = 1;
#else
static bool printk_time = 0;
#endif
if (printk_time) {
/* Add the current time stamp */
char tbuf[50], *tp;
unsigned tlen;
unsigned long long t;
unsigned long nanosec_rem;
t = cpu_clock(printk_cpu);
nanosec_rem = do_div(t, 1000000000);
tlen = sprintf(tbuf, "[%5lu.%06lu] ",
(unsigned long) t,
nanosec_rem / 1000);
for (tp = tbuf; tp < tbuf + tlen; tp++)
emit_log_char(*tp);
printed_len += tlen;
}
klogd
If the
klogd
process is running, it retrieves kernel messages and dispatches them to
syslogd
, which in turn checks
/etc/syslog.conf
to find out how to deal with them.
syslogd
differentiates between messages according to a facility and a priority; allowable values for both the facility and the priority are defined in
<sys/syslog.h>
. Kernel messages are logged by the
LOG_KERN
facility at a priority corresponding to the one used in
printk
(for example,
LOG_ERR
is used for
KERN_ERR
messages).
If
klogd
isn’t running, data remains in the circular buffer until someone reads it or the buffer overflows.
If you want to avoid clobbering your system log with the monitoring messages from your driver, you can either specify the (file) option to
klogd
to instruct it to save messages to a specific file, or customize
/etc/syslog.conf
to suit your needs. Yet another possibility is to take the brute-force approach: kill
klogd
and verbosely print messages on an unused virtual terminal, or issue the command
cat /proc/kmsg
from an unused xterm.
syslog
Accessing to the log buffer is provided at the core through the multi-purpose
syslog
system call. The prototype for the
syslog system
call is defined in
./linux/include/linux/syslog.h
; its implementation is in
./linux/kernel/printk.c
.
The
syslog
call serves as the input/output (I/O) and control interface to the kernel’s log message ring buffer. From the syslog call, an application can read log messages (partial, in their entirety, or only new messages) as well as control the behavior of the ring buffer (clear contents, set the level of messages to be logged, enable or disable console, and so on).
Although reading from
/proc/kmsg
consumes the data from the log buffer, the
syslog
system call can optionally return log data while leaving it for other processes as well.
Kernel space syslog API:
#include <syslog.h>
void openlog(const char *ident, int option, int facility);
void syslog(int priority, const char *format, ...);
void closelog(void);
#include <stdarg.h>
void vsyslog(int priority, const char *format, va_list ap);
closelog()
closes the descriptor being used to write to the system logger.
openlog()
opens a connection to the system logger for a program.
syslog()
generates a log message, which will be distributed by
syslogd
. It does this by writing to the Unix domain socket
/dev/log
.
vsyslog()
is functionally identical to
syslog()
, with the BSD style variable length argument.
User space syslog API(glibc wrapper):
int syslog( int type, char *bufp, int len );
int klogctl( int type, char *bufp, int len );
klogctl()
is the glibc wrapper to control the kernel
printk()
buffer.
dmesg
The
dmesg
command is used to print and control the kernel ring buffer. This command uses the
klogctl
system call to read the kernel ring buffer and emit it to standard output (stdout). The command can also be used to clear the kernel ring buffer (using the
-c
option), set the level for logging to the console (the
-n
option), and define the size of the buffer used to read the kernel log messages (the
-s
option).
dmesg
reads by default a buffer of max 16392 bytes, so if you use a larger log buffer you have to invoke
dmesg
with the
-s
parameter e.g.:
### CONFIG_LOG_BUF_SHIFT 17 = 128k
$ dmesg -s 128000
syslog-ng
The
syslog-ng
application is a flexible and highly scalable system logging application that is ideal for creating centralized and trusted logging solutions. It extends the original syslogd model with content-based filtering, rich filtering capabilities, flexible configuration options and adds important features to syslog.
syslog-ng
also supports ISO/RFC timestamp for system logs. For more info about this powerful log system, please refer to
the manual
.
By default the time stamps are printed in “seconds since boot” (this is the way the kernel is programmed to print the time stamps in
vprintk()
, and it can not be changed to print the time stamps in a human readable format). The system uptime can be helpful to calculate an absolute time stamp if needed (run the
uptime
command).
Given timestamp:
[196149.728085] hello world
The algorithm below converts the printed time stamps to a human readable format:
1. Take the log's time stamp in seconds:
196149.728085 seconds (round the number down/up if needed)
2. Divide the time stamp in seconds by 60 to get the total amount of minutes:
196150 : 60 = 3269.1667 minutes (round the number down/up if needed)
3. Divide the time stamp in minutes by 60 to get the total amount of hours:
3269.1667 : 60 = 54.486111666666666666666666666667 hours
4. Break the decimal number into 2 parts:
54.486111666666666666666666666667 hours = (54 hours) + (0.486111666666666666666666666667 decimal hours)
5. Use the time conversion charts below to convert decimal hours to minutes:
0.486111666666666666666666666667 decimal hours ~ 0.48 decimal hours ~ 29 minutes
6. Note: For more precise conversion (down to seconds), you can use various time converters available on the Internet. Just search for 'convert decimal time' in any search engine.
7. Hence, we get that the log was created this amount of time since boot:
196149.728085 seconds ~ 54 hours 29 minutes
8. Check the current system's uptime:
[Expert@HostName]# uptime
9. To get the log's real time stamp, subtract the log's time stamp in dmesg kernel ring buffer from the current system's uptime.
Following is a Shell script to transform uptime timestamp to human-readable timestamp:
#!/bin/bash
# Translate dmesg timestamps to human readable format
# desired date format
date_format="%a %b %d %T %Y"
# uptime in seconds
uptime=$(cut -d " " -f 1 /proc/uptime)
# run only if timestamps are enabled
if [ "Y" = "$(cat /sys/module/printk/parameters/time)" ]; then
dmesg | sed "s/^\[[ ]*\?\([0-9.]*\)\] \(.*\)/\\1 \\2/" | while read timestamp message; do
#date +"%s" -d "1970-01-01 00:00:00"
#awk '{printf("%d:%02d:%02d\n", ($1/3600), ($1%3600/60),($1%60))}' /proc/uptime
printf "[%s] %s\n" "$(date --date "now - $uptime seconds + $timestamp seconds" +"${date_format}")" "$message"
done
else
echo "Timestamps are disabled (/sys/module/printk/parameters/time)"
fi
Posted by Bo Yang at 2015-01-07 with tags Notes , Click , Network
The Click programming language was developed to configure Click routers, but nowadays you also can use it to write test cases for Click elements.
The Click Script Language defines a configuration graph, which consists of connected elements. Each element has an element class specified by class name. Elements are connected through their input and output ports. Input and output ports are distinguished by number, while elements are distinguished by name.
Click configuration strings are comma-separated lists of arguments delimited by parentheses. The fundamental syntax of Click Script Language is:
name :: class(config-string); // declare element object
name1, name2, ..., nameN :: class(config); // declaration shorhand
name1[port1] -> [port2]name2; // connect two elements
name1[port1] -> [port2a]name2[port2b] -> [port3]name3; // piggyback connections
name1 -> name2 :: class(config-string) -> name3; // declaring elements inside connections is allowed
name1 -> class(config-string) -> name3; // anonymous element
require(requirement[, requirement …]); // list config requirements
n1, n2 :: class -> n3; // many-to-one connections
// many-to-many connections:
// A many-to-many connection matches output ports to input ports.
// There must be as many ports on the left as on the right.
// '=>' is the many-to-many connector.
c[0], c[1], c[2] => Paint(0), Paint(1), Paint(2) -> next;
c => Paint(0), Paint(1), Paint(2) -> next;
An element group is one or more Click statements enclosed in parentheses. Within the parentheses, the special pseudoelements
input
and
output
refer to connections from outside the group. Click expands the group at parse time, so connections through
input
and
output
have no run-time overhead. The following five lines are equivalent:
x -> y;
x -> ( input -> output ) -> y;
x -> ( [0] -> [0] ) -> y;
x -> (->) -> y;
x -> ( [0]->[0]; [1]->[1] ) => ( [0]->[0]; [1]->[1] ) -> y;
Line five uses the fact that connections may be repeated without error (the line expands to
x -> y; x -> y
), where the explicit semicolons are used to avoid ambiguity.
Element groups have implicit, overridable port specifications that list all their ports in sequential order. For example, these three lines are equivalent:
x => ( [0]->[0]; [1]->[1] ) -> y;
x => [0,1] ( [0]->[0]; [1]->[1] ) [0,1] -> y;
x -> y; x [1] -> y;
An element group does not define a new scope. Its contents may refer to elements declared outside of the group, and declarations inside the group are visible after the group closes. This differs from compound elements, described next, which have a related syntax but additionally introduce a new scope.
A
compound element
is a scoped collection of elements that behaves like a single element. A compound element can be used anywhere an element class is expected (that is, in a declaration or connection). Syntactically, a compound element is a set of Click statements enclosed in braces
{ }
. Inside the braces, the special names
input
and
output
represent connections from or to the outside.
Compound element classes are router configuration fragments consisted by Click elements and statements that are treated like element classes. For compound elements, only components remain in the final configuration graph, and all compound element structure is compiled away. This process is called
flattening
, during which compound element components are given names that reflect their origin. For example, a component named
e
of a compound element
compound
is named
compound/e
in the flattened configuration.
elementclass Name { // compound elements
... Click Statements … // defined by click statements but not C++ class
}
e :: { // anonymous compound element class
... Click Statements …
}
elementclass MyQueue Queue; // define an alias for an existing element class
Like any element, compound elements may have input and output ports. Each connection to or from a compound element port is transformed by flattening into a connection to or from one of its components’ ports. Inside a compound element class, the special pseudoelements
input
and
output
specify how this transformation proceeds. Given a compound element e. If
e/input
connects to component
e/c
through port
i
, then every connection to
e
’s input port
i
will flatten into a connection to its component
e/c
. For example,
elementclass Example {
s1 :: InfiniteSource; s2 :: RatedSource;
s1 -> [0]output; s2 -> [0]output;
}
e :: Example -> d :: Discard;
above code will be flattened to
e/s1 :: InfiniteSource; e/s2 :: RatedSource; d :: Discard;
e/s1 -> d; e/s2 -> d;
Compound element classes also can take varying number configuration arguments, input ports, and output ports.
Formal parameters
define the arguments that a compound element class should take. A
formal parameter
is a sequence of alphanumeric characters preceded by a dollar sign, such as
$var
. A compound element class may begin with a list of parameters.
While formal parameters let a compound element class support a fixed number of positional arguments,
overloading
lets several compound element definitions with different numbers of arguments or ports share a single name. As a side-effect, overloading also support different behaviors in a compound element based on numbers of input/output ports. Every compound element class can correspond to a number of definitions, which are textually separated by
||
, which are distinguished by the numbers of formal parameters, input ports or output ports. Given a declaration of compound element, the interpreter checks the number of arguments and search for a matching definition. If found one, then it is expanded; otherwise, an error will occur.
// Example 1: argument overloading
elementclass ShapedQueue {
input -> Queue -> Shaper(10000) -> output;
||
$cap | input -> Queue($cap) -> Shaper(10000) -> output;
}
q1 :: ShapedQueue; // OK; uses first definition
q2 :: ShapedQueue(1024); // OK; uses second definition
q3 :: ShapedQueue(1024, 10000); // error—no matching definition
// Example 1: port overloading - additional output port
elementclass VerboseCheckIPHeader {
input -> c :: CheckIPHeader -> output; c[1] -> Print(CheckIPHeader) -> Discard;
||
input -> c :: CheckIPHeader -> output; c[1] -> Print(CheckIPHeader) -> [1]output;
}
Overloading also allows user to add new definitions to an existing element class. For example, following definition will add a two-argument version of
Queue
:
elementclass Queue {
... || // 'dot dot dot' is part of the syntax
$capacity, $rate | input -> Queue($capacity)
-> Shaper($rate) -> output;
}
q1 :: Queue; // built-in Queue
q2 :: Queue(1024); // built-in Queue
q3 :: Queue(1024, 10000); // overloaded Queue definition above
When choosing the definition that corresponds to a given compound element declaration, Click only considers the definitions that were lexically visible at the point of declaration.
Compound element configuration arguments are only meaningful inside the configuration strings of components. So that you cannot change the element class of a given component, or cause components to be added to or subtracted from the compound. However, it is possible to build a compound element that sends packets through different sets of components based on the value of its configuration string. Following example uses StaticSwitch to implement a selective checksum check.(The
CheckIPHeader
element checks an IP header’s length and checksum for sanity;
CheckIPHeader2
does the work of
CheckIPHeader
except for the checksum check.)
elementclass MaybeChecksum { $checksum_p |
input -> sw :: StaticSwitch($checksum_p);
sw[0] -> CheckIPHeader2 -> output;
sw[1] -> CheckIPHeader -> output;
};
c1 :: MaybeChecksum(0); // uses CheckIPHeader2, skips checksum
c2 :: MaybeChecksum(1); // uses CheckIPHeader, checks checksum
The
Script element
implements a simple scripting language interpreter useful for controlling Click configurations. Scripts can set variables, call handlers, wait for prodding from other elements, and stop the router. Script element is defined in
click/elements/standard/Script.hh
. For details of instructions and handlers, please refer to
http://www.read.cs.ucla.edu/click/elements/script
.
In the Script element, each keyword is handled by a corresponding handler:
void
Script::add_handlers()
{
set_handler("step", Handler::OP_WRITE | Handler::h_nonconst, step_handler, 0, ST_STEP);
set_handler("goto", Handler::OP_WRITE | Handler::h_nonconst, step_handler, 0, ST_GOTO);
set_handler("run", Handler::OP_READ | Handler::READ_PARAM | Handler::OP_WRITE | Handler::h_nonconst, step_handler, 0, ST_RUN);
set_handler("add", Handler::OP_READ | Handler::READ_PARAM, arithmetic_handler, ar_add, 0);
set_handler("sub", Handler::OP_READ | Handler::READ_PARAM, arithmetic_handler, ar_sub, 0);
set_handler("min", Handler::OP_READ | Handler::READ_PARAM, arithmetic_handler, ar_min, 0);
set_handler("max", Handler::OP_READ | Handler::READ_PARAM, arithmetic_handler, ar_max, 0);
set_handler("length", Handler::OP_READ | Handler::READ_PARAM, basic_handler, ar_length, 0);
set_handler("unquote", Handler::OP_READ | Handler::READ_PARAM, basic_handler, ar_unquote, 0);
…….
}
| All of the handlers defined in the Script element need to parse the script language, which is implemented in click/lib/confparse.[hh | cc]. In this way, the Script element could serve as an interpreter to the script instructions and handlers. |
Testie
is a simple test tool for Click elements, which enables the Test-Driven-Development of Click. Each testie file is written mainly in Click script language and incorporates a shell script to be run. Tool
click/test/testie
runs the Click script, and checks the expected error or output. A testie file may have following layout:
%info
// a short description of the test.
%require [-q]
// prerequisites that must be satisfied before the test can run.
%include FILENAME
// interpolate the contents of another testie file.
%script
// Shell scripts that controls the test. Testie will run each command in sequence.
// Command “click” need to be specified to interpret Click scripts.
%file [-d] [+LENGTH] FILENAME...
// Create an input file for the script. FILENAME can be `stdin`, which sets the script's standard input.
%expectv [-ad] [+LENGTH] FILENAME...
%expect [-adiw] [+LENGTH] FILENAME…
%expectx [-adiw] [+LENGTH] FILENAME...
// An expected output file for the script. FILENAME can be 'stdout' or 'stderr'.
// Testie will run the script, then compare the file generated by script with the provided data. The files are compared line-by-line.
// The -a flag marks this expected output as an alternate. Testie will compare the script's output file with each provided alternate; the test succeeds if any of the alternates match.
// The -d flag behaves as in %file.
// The -i flag makes any regular expressions case-insensitive (text outside of regular expressions must match case), and the -w flag ignores any differences in amount of whitespace within a line.
%ignorex [-di] [+LENGTH] [FILENAME]
%ignore, %ignorev
// lines to be ignored.
In addition to above sections, you also can define one or multiple Script elements in the testie file, which group a set of Click Script instructions.
click/test/testie
is the Perl tool to run testie files. Given a testie file, the testie interpreter will first read and parse each section defined in the testie file:
%require
files will be expanded, files defined in section
%file
will be created in a temporary directory, commands listed under
%script
will be recorded in a hash, and so on. After preprocessing, the commands will be executed by shell or interpreted by click.
click/test/testie
is a very good template of simple interpreter.
Systemd unit files give Linux administrators a quiet superpower: the ability to run arbitrary commands before a service starts and after it stops. The directives ExecStartPre and ExecStopPost sit just outside the main process lifecycle, which makes them ideal for preparatory work and cleanup work that should never be confused with the running service itself.
In Australian enterprise and research environments, where CentOS Stream and Rocky Linux still underpin plenty of state government workloads, university clusters, and the kinds of internal platforms you find at AARNet-connected institutions, the habit of stuffing everything into one startup script is alive and well. Moving that scaffolding into properly typed unit directives tends to make failover across data centres in Sydney, Melbourne, and Brisbane a lot more predictable.
The pair is small, but it covers a surprisingly broad set of operational needs: preflight checks, lockfile cleanup, temp directory teardown, post-run log archiving, and notifications to monitoring stacks. Once you treat them as legitimate lifecycle hooks rather than incidental escape hatches, the unit file becomes the source of truth for the service's full contract with the operating system.
ExecStartPre runs one or more commands before the main ExecStart command fires. Each entry is a separate process line in the unit file, executed in the order it appears, and the service will refuse to start if any of them exits non-zero. That strict gating is the entire point: it lets a unit declare its own dependencies without shelling out to a wrapper script.
ExecStopPost runs after the service has been asked to stop and after the optional ExecStop command has done its work. Unlike ExecStartPre, failure here does not block the unit from being considered stopped; the process is already gone. This makes ExecStopPost the natural home for post-mortem tasks like flushing logs, releasing semaphores, or copying a crash dump somewhere safe.
Both directives support the usual systemd specifiers such as %i (instance name), %H (hostname), and %t (runtime directory), and they can be prefixed with - (dash) to mark a command as best-effort, in which case its exit status is logged but ignored. That single character changes the semantics from a hard guardrail to a soft cleanup.
Preflight validation is the classic use case. Before bringing up a custom Java service or a Python FastAPI app, a unit can curl a configuration endpoint, confirm a license server is reachable, or assert that a required mount point is present. Doing this in ExecStartPre lets systemd record the failure with systemctl status and keeps the wrapper script short.
Lockfiles and stale state are another good fit. Network file shares between Melbourne and Adelaide sites sometimes leave behind .pid files after unclean shutdowns. A one-liner that removes a stale lock and verifies that no competing process is listening on the required socket can save a night-shift operator a phone call at three in the morning, AEST.
Environment preparation also belongs here. Generating a machine-specific config from a Jinja-style template, sourcing a vault-decrypted secret into an EnvironmentFile-compatible cache, or warming up a local DNS cache all sit comfortably in a [Service] block without any wrapper.
Cleanup is the obvious one. Temp directory contents, shared memory segments, leftover iptables rules, and ephemeral SSH tunnels should all be torn down once the unit stops. Placing these in ExecStopPost means they fire whether the service was asked to stop gracefully or killed by an OOM event.
Artefact preservation is underrated. A pre-flight web crawler that gathers pages nightly might leave an incomplete output directory behind if killed mid-run. An ExecStopPost command that tars the partial output into /var/log/scrape-staging/ with a UTC timestamp preserves evidence for the Australian Cyber Security Centre's Essential Eight reporting process when an incident is later reviewed.
Notification of external systems is another. Posting a small JSON payload to a Slack webhook in Canberra, or to a Mongoose-based status board, can be wired into ExecStopPost so the monitoring team gets a heads-up the moment something exits unexpectedly, instead of waiting for their next scrape interval.
Ordering matters more than people expect. ExecStartPre commands run strictly in declaration order, and so do ExecStopPost commands. If one pre-start command depends on a directory created by the previous one, declare the dependency explicitly rather than assuming serial execution will paper over a race.
The - prefix deserves a thought before each line. A failing ping test on an optional external service should not stop the unit; a failing signature verification absolutely should. Treat every ExecStartPre line as either mandatory or best-effort, and put a - on the best-effort ones so systemd ignores their exit codes.
For short-lived units of Type=oneshot, ExecStartPre and ExecStopPost behave the same way as they do for Type=simple, but RemainAfterExit=y becomes more relevant. A configuration audit unit that runs hourly and reports back fits this pattern: pre-start sanity checks, the audit itself, then post-stop archival.
A minimal prep-and-serve unit could look like the following. It checks that the configured root directory exists and that a backing NFS mount from the Adelaide office is reachable before booting the daemon, and it tars up yesterday's served files on shutdown:
[Unit]
Description=Internal file serve
After=network-online.target
[Service]
Type=simple
ExecStartPre=/usr/bin/test -d /srv/share
ExecStartPre=-/usr/bin/timeout 5 ls /mnt/nfs-adl
ExecStart=/usr/bin/python3 -m http.server 8080 --directory /srv/share
ExecStopPost=/bin/tar -czf /var/log/share-%d.tar.gz /srv/share/archive
Restart=on-failure
A second example, a periodic ingest job run from systemd timers, uses the directives to validate input files, record a heartbeat into /var/lib/heartbeat/done, and emit a final status line into the journal after the unit stops.
Output from ExecStartPre and ExecStopPost ends up in the journal, which means journalctl -u myservice.service covers everything from preflight to cleanup. Adding StandardOutput=journal and tagging commands with SyslogIdentifier= makes it easy to grep a unit's full lifecycle out of a busy host, especially on a shared cluster where dozens of units log to the same daemon.
Common pitfalls include using interactive commands that need a controlling terminal, depending on network services without expressing that with After= directives, and forgetting that ExecStartPre runs as root by default. If the service drops privileges later with User= and Group=, the pre-start commands still run with elevated rights, which can leave files owned by root in places the service later cannot read.
It is also worth checking that environment variables set in Environment= or EnvironmentFile= are visible to the hook scripts. They are, by default, but a wrapper that execs into a shell with a stripped environment will lose them and produce failures that look unrelated to the actual cause.
| Directive | When it runs | Failure stops unit? | Best for |
|---|---|---|---|
| ExecStartPre | Before ExecStart | Yes (unless prefixed with -) |
Validation, prerequisites, environment prep |
| ExecStart | Main process | N/A (this is the main process) | The actual service |
| ExecStop | Asked to stop, before kill | No | Graceful shutdown |
| ExecStopPost | After service is stopped | No | Cleanup, archival, notifications |
| ExecReload | On systemctl reload |
No | Configuration reload |
| ExecReloadPost | After reload signal handled | No | Post-reload housekeeping |
Recommendations for teams shipping systemd-managed services from Australian data centres:
- so a flaky optional check cannot take the whole service down./usr/libexec/ and call it from the unit.SyslogIdentifier= so preflight failures show up cleanly in journalctl output rather than getting buried in a long script's stdout.systemctl start and a deliberate failure early in the day, not at midnight when something in the Sydney office has already gone sideways.If you are running CentOS Stream, Rocky, or another RHEL-derivative across multiple sites in Australia and you have a unit file worth sharing, drop the cleaned-up version into a gist and link it in the comments below. Bo Yang's Blog readers are always keen on a copy-pasteable example that has survived a real production outage.
Posted by Bo Yang at 2014-12-19 with tags Unix/Linux , Notes
This is the second article(collection) on how to build a *nix development environment by integrating remote servers and local Linux/Mac clients. For the previous article on this topic, please refer to Building Remote+Local *nix Develop Environment .
Add following line into your
$HOME/.vimrc
autocmd CursorMoved * exe printf('match IncSearch /\V\<%s\>/', escape(expand('<cword>'), '/\'))
Or use a more complicated one in the
.vimrc
:
" Highlight all instances of word under cursor, when idle.
" Useful when studying strange source code.
" Type z/ to toggle highlighting on/off.
nnoremap z/ :if AutoHighlightToggle()<Bar>set hls<Bar>endif<CR>
function! AutoHighlightToggle()
let @/ = ''
if exists('#auto_highlight')
au! auto_highlight
augroup! auto_highlight
setl updatetime=4000
echo 'Highlight current word: off'
return 0
else
augroup auto_highlight
au!
au CursorHold * let @/ = '\V\<'.escape(expand('<cword>'), '\').'\>'
augroup end
setl updatetime=500
echo 'Highlight current word: ON'
return 1
endif
endfunction
If you have generated ctags file, then you can automatically load it by:
export
CTAGS_TAG
in
$HOME/.bashrc
by
export CTAGS_TAG=/path/to/your/tags
.
add following lines into your
$HOME/.vimrc
```Shell
if filereadable($CTAGS_TAG)
set tags=$CTAGS_TAG
endif
```
If you want to access the most recently used files in Vim, you need plugin
MRU
. The
:MRU
command will show you all the recently used files, and you can choose a file and press
<Enter>
to open it in current window. In addition
t
key.
<Enter>
or
v
or
o
or
t
.
q
key or the
<Esc>
key or using one of the Vim window commands.
:MRU
command, such as
:MRU <pattern>
.
Vim
runtimepath
manager, widely used by many plugins. Adding
call pathogen#infect()
to your
.vimrc
, then any plugins you wish to install can be extracted to a subdirectory under
~/.vim/bundle
. And they will be added to the
runtimepath
.
The NERD tree allows you to explore your filesystem and to open files and directories, and NERDTree Tabs can make NERDTree available for all Vim tabs(sometimes it is useful). After installing the these plugins, you can add the following lines to
.vimrc
.
" Nerd Tree
" let g:NERDTreeDirArrows=0 " Do not use new arrows for directories
map <C-n> :NERDTreeToggle<CR>
let g:nerdtree_tabs_open_on_gui_startup=0 " no nerdtree_tabs by default
For some Linux distributions, the NERDTree could not show arrows for directories, then you need to uncomment the line
let g:NERDTreeDirArrows=0
in your
.vimrc
.
Supertab is a vim plugin which allows you to use
<Tab>
for all your insert completion needs (:help ins-completion).
These two plugins are used for searching/opening files(even not in ctags) in Vim. CtrlP is written in pure Vimscript, so it is very slow. Although Command-T is faster, it relies on Ruby, which makes it difficult to install. Actually, I rarely use them in daily work.
My
vimrc
can be found at
https://github.com/bo-yang/misc/blob/master/vimrc
.
Cscope is a tool for browsing source code. You can either run cscope standalone or use it with Vim . No matter in which way, you need to generate cscope database first. And the cscope DB depends on the source files you specified. General steps of using cscope are:
find /my/project/dir -name '*.c' -o -name '*.h' > /foo/cscope.files
cd /foo
cscope -b
CSCOPE_DB=/foo/cscope.out; export CSCOPE_DB
In Vim, you can load cscope DB by command
:cs add <path_to_cscope_db>
. For more cscope operations in Vim, please run command
:cs help
. To automatically load cscope into Vim, you can export
CSCOPE_DB
in
$HOME/.bashrc
, such as
export CSCOPE_DB=/path/to/cscope.out
Then the
CSCOPE_DB
will be automatically loaded every time you run Vim.
To save the effort of building cscope DB, I wrote a cross-platform(Linux & Mac OS X) wrapper script, which can be found in my GitHub channel .
Since
sshfs
command requires too much parameters, and things will be worse when the network is not stable. Following script will ease your pain.
#!/bin/sh
USER=<your_name>
SERVER=<your_server>
remote_dir=/nobackup/$USER
local_dir=$HOME/Documents/VMs
if [ ! -d ${local_dir} -o ! -s ${local_dir} ]
then
sudo umount -f $local_dir
fi
cd ${local_dir}
sshfs $USER@${SERVER}:${remote_dir} ${local_dir}
Posted by Bo Yang at 2014-11-20 with tags Unix/Linux , Multithreading , Notes
When using
pthread
for multithreading, most threads call
pthread_exit()
implicitly on return from the thread start routine. Besides,
pthread_exit()
also can be used to terminate the initial process thread in
main()
, leaving other threads to continue operation. The process will go away automatically when the last thread terminates. If you don’t care about the process exit status, or if is difficult to know the created thread IDs(e.g. created by third party APIs), you can call
pthread_detach(pthread_self());
pthread_exit(NULL);
at the end of the
main()
function.
However, you must be carefull when using
pthread_exit()
in the main thread. Because after calling
pthread_exit()
and before the process really terminate, the process becomes “zombie” - it still exists even though it is “dead”, just like a Unix/Linux process that’s terminated but hasn’t yet been “reaped” by a
wait
operation. The zombie process may retain most or all of the system resources that it used when running, so it is not a good idea to leave threads in this state for longer than necessary. And obviously, zombie process cannot save you resources! So also don’t try
pthread_exit()
for saving CPU and memory.
I also noticed a undocumented problem caused by
pthread_exit()
- it may lead to failure of open procfs(
/proc/
) files! If one of your threads would open
/proc/mounts
(
currently I only find this file will go wrong, and other procfs files like
/proc/cpuinfo
or
/proc/uptime
can be successfully opened
) during its life, and
pthread_exit()
is called after creating these threads in the main thread, you will meet the “Invalid argument” error because of functions like
open("/proc/mounts",'r')
.
Following program demonstrates this problem:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS 5
void *PrintHello(void *threadid)
{
long tid;
tid = (long)threadid;
printf("Hello World! It's me, thread #%ld!\n", tid);
sleep(10); // sleep sometime
/* Read file */
FILE* fp=fopen("/proc/mounts","r"); // would fail
//FILE* fp=fopen("/proc/cpuinfo","r"); // would succeed
if(fp==NULL) {
fprintf(stderr,"Failed to open file!\n");
} else {
char line[80];
if(fgets(line,80,fp)==NULL)
fprintf(stderr,"Failed to read file!\n");
else
fprintf(stdout,"%s\n",line);
fclose(fp);
}
sleep(1200);
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
pthread_t threads[NUM_THREADS];
int rc;
long t;
for(t=0;t<NUM_THREADS;t++){
printf("In main: creating thread %ld\n", t);
rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
if (rc){
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
/* Last thing that main() should do */
pthread_detach(pthread_self());
pthread_exit(NULL);
}
After building above code, say
pthread_exit_test
, run this program and find the PID of it. Then
cat /proc/<PID>/status
, you will find the process status like
Name: pthread_exit_te
State: Z (zombie)
Tgid: 3091
Pid: 3091
PPid: 2849
TracerPid: 0
Uid: 370845 370845 370845 370845
Gid: 25 25 25 25
Utrace: 0
FDSize: 0
Groups: 25 1000000312
Threads: 6
SigQ: 0/78966
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000004
SigCgt: 0000000180000000
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: ffffffffffffffff
Cpus_allowed: 3f
Cpus_allowed_list: 0-5
Mems_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
Mems_allowed_list: 0
voluntary_ctxt_switches: 14
nonvoluntary_ctxt_switches: 1
For the above program, if replace
pthread_exit()
with
pthread_join()
or
while(1) {sleep(120); }
, it would work well. The
while
loop is especially usefull when you don’t know the thread id to be joined.
Until now I am still don’t know why openning
/proc/mounts
would fail and why openning
/proc/cpuinfo
could succeed in above code. I also tried other system file or link, and found all of them could be successively readed.