Thursday, 5 October 2017

Linux Information

1. Linux Auto Login

Sometimes we may want to skip the linux login prompt, for example, a remotely-powered linux server in the basement of a building. Here is how we do it on RedHat linux 7.3.

  • modify /etc/inittab , comment out:
    1:2345:respawn:/sbin/mingetty tty1
    Add:
    1:2345:/usr/bin/openvt -f -c 1 -w -- /bin/login -f root
    -c 1 for console 1
    -- for separator

  • modify ~/.bash_profile, add:
    startx
    to start the X windows
  • modify ~/.xinitrc, add:
    gnome-session
    or
    startkde

2. Linux Programming

Linux Command Line Tools
Some useful tools are listed here.
% nm <object-files>
list symbols from object files

% readelf -d <elf-files>
display dynamic info about ELF files

% ldd <executable-files>
list shared library dependencies

% ar crv <lib-files> <object-files>
create static library

***GNU Make***
Make commands is used extensively in Linux software development.

A) Make command line syntax:
The `-s' or `--silent' flag prevents all echoing, as if all commands are started with `@'.
eg. make -s zImage
The `-f' flag specifies a name of the makefile.
eg. make -f myMakefile
We can run make -n <target>, to tell make to print out what it should have done, without actually doing it.

B) The general Makefile syntax is:
target : dependency
 rules (or commands)
The command lines start with a tab character. Blank lines and lines of just comments may appear among the command lines; they are ignored. (But beware, an apparently "blank" line that begins with a tab is not blank! It is an empty command.)
To make a particular target, we need to pass its name to make as a parameter. Without a parameter, make will try to make the first target listed in the makefile.
Make invokes a shell for executing rules, and uses a new shell for each rule. To let all script commands appear on one logical line, we must add backslash at the end of each line. The @sign will tell make not to print out each command on standard output.

C) Makefile contents:
Makefiles contain five kinds of things: explicit rules, implicit rules, variable definitions, directives, and comments.

directive:
     include filenames...

When make processes an include directive, it suspends reading of the containing makefile and reads from each listed file in turn. When that is finished, make resumes reading the makefile in which the directive appears.
If you want make to simply ignore a makefile which does not exist and cannot be remade, with no error message, use the -include directive instead of include, like this:
     -include filenames...

D) Other Makefile stuff:
Targets that do not refer to files but are just actions are called phony targets.
objects = main.o kbd.o command.o display.o \
               insert.o search.o files.o utils.o

.PHONY : clean
     clean :
             -rm $(objects)

clean:
 -rm -f *.o
The rules of making the target "clean" comes without dependency. This mean that the target is always considered out of date, and its rule is always executed. Also notice that the command starts with "-". This tells make to ignore the result of the command, and so make clean will always succeed. The `-' is discarded before the command is passed to the shell for execution.
For example,

     clean:
             -rm -f *.o
A few special macros are defined in make. Here is the summary.
foo.o : foo.c
 gcc -c $(CFLAGS) %^ -o $@

$^ is the dependency (foo.c)
$@ is the target (foo.o)

foo.o : foo.c defs.h hack.h
 gcc -c $(CFLAGS) $< -o $@

$< is the first dependency (foo.c)

.c.a:
 gcc -c $<
 ar rv $@ $*.o

$* is the name of the target without the suffix
Make has a special syntax for dealing with library files. The syntax is lib(file.o). It means that the object file file.o is stored in the library file lib.a.
$(LIBRARY): $(LIBRARY)(db_api.o)
db_api.o: db_api.c db_api.h
For a project with multiple subdirectories, a main makefile in the main directory will invoke the sub-makefiles. The syntax is like this:
(cd subdirectory;$(MAKE))
or, equivalently, this:
$(MAKE) -C subdir
The brackets ensure that it is all processed by a single shell. Since a new shell is invoked for this, the program running the make doesn't execute the cd command. Only the shell invoked to carry out the rule is in a different directory.

E) Variable substitution
To substitute a variable's value, write a dollar sign followed by the name of the variable in parentheses or braces: either `$(foo)' or `${foo}' is a valid reference to the variable foo. This special significance of `$' is why you must write `$$' to have the effect of a single dollar sign in a file name or command.
The first flavor of variable is a Recursively expanded variable. Variables of this sort are defined by lines using `=' or by the define directive.
foo = $(bar)
bar = $(ugh)
ugh = Huh?

all:@echo $(foo)
It will echo `Huh?'. `$(foo)' expands to `$(bar)' which expands to `$(ugh)' which finally expands to `Huh?'.
To avoid all the problems and inconveniences of recursively expanded variables, there is another flavor: Simply expanded variables. Simply expanded variables are defined by lines using `:='
foo := $(bar)
bar := $(ugh)
ugh := Huh?

all:@echo $(foo)
This will echo nothing, as $(foo) is undefined here.
There is another way, called a Conditional variable assignment operator, because it only has an effect if the variable is not yet defined. This statement:
  FOO ?= bar
is exactly equivalent to this:
ifeq ($(origin FOO), undefined)
  FOO = bar
endif

F) String Substitution (in Makefile):
$(patsubst pattern,replacement,text)
Finds whitespace-separated words in text that match pattern and replaces them with replacement. Here pattern may contain a `%' which acts as a wildcard, matching any number of any characters within a word. If replacement also contains a `%', the `%' is replaced by the text that matched the `%' in pattern. `%' characters in patsubst function invocations can be quoted with preceding backslashes (`\'). Whitespace between words is folded into single space characters; leading and trailing whitespace is discarded.
For example,
$(patsubst %.c,%.o,x.c.c bar.c)
produces the value `x.c.o bar.o'.
Substitution references are a simpler way to get the effect of the patsubst function:
$(var:pattern=replacement)
is equivalent to
$(patsubst pattern,replacement,$(var))
The second shorthand simplifies one of the most common uses of patsubst: replacing the suffix at the end of file names.
$(var:suffix=replacement)
is equivalent to
$(patsubst %suffix,%replacement,$(var))
For example, you might have a list of object files:
objects = foo.o bar.o baz.o
To get the list of corresponding source files, you could simply write:
$(objects:.o=.c)
instead of using the general form:
$(patsubst %.o,%.c,$(objects))
Another example in real Makefile:
SUB = server client

%.build:
 (cd $(patsubst %.build, %, $@) && $(MAKE))

%.clean:
 (cd $(patsubst %.clean, %, $@) && $(MAKE) clean)

all:  $(patsubst %, %.build, $(SUB))
clean:  $(patsubst %, %.clean, $(SUB))

G) Conditional statement (in Makefile):
Three types: if-else-fi, ifeq-else-endif, ifdef-else-endif.
@if [ -z $(DESTDIR) ]; then \
    /sbin/depmod -ae ; \
elif [ -f $(SYSTEMMAP) ]; then \
    /sbin/depmod -ae -b $(DESTDIR) -F $(SYSTEMMAP) ; \
else \
    echo "Don't forget the target system."; \
fi
ifeq ($(strip $(foo)),)
  text-if-empty
else
  text-if-not-empty
endif
Don't specify $(foo) for variable referencing if used in ifdef statement
foo = $(bar)
ifdef foo
  frobozz = yes
else
  frobozz = no
endif


Using GNU GDB and gdbserver
GDB can be used to debug programs written in C and C++. GDB distribution contains gdbserver in one of its subdirectory. We run gdbserver on target platform in order to save space, and use GDB on host to connect to the gdbserver for debugging. For more information, click here.

Compiling and generating library images
The sequences of generating a *.a (static library)
a) gcc -c xxx.c
b) ar crv libxxx.a xxx.o

The sequences of generating a *.so (dynamic library)

gcc -c -fPIC xxx.c
gcc -shared -o libxxx.so xxx.o

Files, Pipes, Sockets
Everything is represented as a file under Linux. Even hardware devices are represented by files in Linux. The low-level system calls can be used to access the files, and similarly the hardware devices, pipes, sockets.
int open(const char *path, int flags);
- open system call returns a new file deescccriptor
size_t read(int fd, void * buf, size_t nbytes);
- read system call reads up to nbytes frrommm file associated with the file descriptor fd to buf
size_t write(int fd, const void * buf, size_t nbytes);
- write system call writes up to nbytes tooo the file referenced by the file descriptor fd from the buf
int close(int fd);
- close system call closes a file descriiptttor, so that it may be reused
int ioctl(int fd, int cmd, ...);
- ioctl system call provides an interfacce for controlling the behavior of devices
off_t lseek(int fd, long int offset, int whence);
- lseek system call sets the read/write pooointer of a file descriptor
The standard library and its header file stdio.h provide an interface to low-level system calls. The library provides functions to take care of the buffering of the devices. In standard library, the equivalent to a file descriptor is called a stream, and is implemented as FILE * .
FILE * fopen(const char *filename, const char modes);
- fopen library function returns a FILE * pointer
size_t fread(void * ptr, size_t size, size_t nitems, FILE * stream);
- fread library function reads to ptr frrommm the stream, for a record of size and a count of nitems
size_t fwrite(const void * ptr, size_t size, size_t nitems, FILE * stream);
- fwrite library function writes from thhe ptr to the stream, for a record of size and a count of nitems
int fclose(FILE * stream);
- fclose library function closes the speeciiified stream
int fseek(FILE * stream, long int offset, int whence);
- fseek library function sets the positiionnn in the stream for the next read or write on that stream.
Pipe is a primitive form of inter process communications. It allows the data flow from one process to go to another process. For shell commands, it is entered as :
cmd1 | cmd2
For high-level pipe function, they will operate on file stream.
FILE * popen(const char * command, const char * open_mode);
It allows a program to invoke another program as a new process, and either pass data or receive data from it.
int pclose(FILE * stream);
It closes the file stream associated with the pipe.
At the same time, a low-lovel pipe function provides a way of passing data between two programs, without the overhead of invoking a shell to interpret the requested command.
int pipe(int fd[2]);
It is passed an array of two integer file descriptors. Any data written to fd[1] can be read back from fd[0]. Low-level system calls, read and write, are used to access the data.
A named pipe exists in the file system as a special type of file, but behaves like the unnamed pipes we discussed earlier. It is called FIFO too.
int mkfifo(const char * filename, mode_t mode);
It creates a named pipe, using absolute pathname.
We can remove the FIFO by using the rm command, or from within a program by using the unlink function.
A FIFO exists as a named file, not as an open file descriptor. It must be opened before it can be read from or written to. The open and close system calls can be used for the purpose. The read and write system calls can be used to access the FIFO after it is opened.

Using GCC __func__ macro
GCC provides three magic variables which hold the name of the current function, as a string. The first of these is __func__, which is part of the C99 standard:
The identifier __func__ is implicitly declared by the translator as if, immediately following the opening brace of each function definition, the declaration
           static const char __func__[] = "function-name";
appeared, where function-name is the name of the lexically-enclosing function. This name is the unadorned name of the function.
__FUNCTION__ is another name for __func__. Older versions of GCC recognize only this name. However, it is not standardized. For maximum portability, we recommend you use __func__, but provide a fallback definition with the preprocessor:
     #if __STDC_VERSION__ < 199901L
     # if __GNUC__ >= 2
     #  define __func__ __FUNCTION__
     # else
     #  define __func__ ""
     # endif
     #endif
In C, __PRETTY_FUNCTION__ is yet another name for __func__. However, in C++, __PRETTY_FUNCTION__ contains the type signature of the function as well as its bare name, such as "void a::sub(int)".
Click here to see the examples: exam.c and exam.h file.

Debugging
There are 8 severity levels in linux kernel, defined in <linux/kernel.h>. DEFAULT_MESSAGE_LOGLEVEL is specified in kernel/printk.c, and applied to printk with no specified priority. If priority is less than console_loglevel, the message is displayed. If both klogd and syslogd are running on the system, kernel message are appended to /var/log/messages, independent of console_loglevel.
In <linux/kernel.h>, #define console_loglevel as DEFAULT_CONSOLE_LOGLEVEL. As printk writes message to a circular buffer, it then wakes up any process that is waiting for the message. If klogd is running, it retrieves kernel message and dispatch them to syslogd, which in turn check the settings in /etc/syslog.conf.

To use syslogd:





  • /etc/init.d/syslog -- script file
  • /etc/syslog.conf -- config file
  • create /var/log directory.

    An example syslog.conf config file:
    # cat syslog.conf
    *.*                      /var/log/messages
    

    AWK 
    It is an interpreted programming language for performing complex text processing tasks. It is also a simple text processing utility. It stands for the names of its authors: Aho, Weinberger & Kernighan.
    simple syntax:
    awk <search pattern> {<program actions>} <data filename>
    eg. awk '/gold/ {print $5,$6,$7,$8}' coins.txt
    full syntax:
    awk 'BEGIN   {<initialization>}
     <search pattern 1> {<program actions>}
     <search pattern 2> {<program actions>}
     ...
     END   {<final actions>}'
    
    eg. awk 'END {print NR, "conis"}' coins.txt
    
    NR - Awk's pre-defined varaibles, stands for Number of Records
    Awk regards each line of input data as composed of multiple fields, which are essentially words separated by blank spaces. A blank space is the default "field separator". To tell Awk to use another field separator, use -F.
    eg. awk -F\"  -- use " as the separator
    
    eg. awk -F\" '/REL/ {print $$2}' include/linux/version.h
    

    SED
    Stream editor
    sed -e s/<reg expr>/<replace text>/{flags}
    
    eg. sed -e '1,2 s/line/LINE/' test.txt
    eg. sed -e 's/cat/dog/g' test.txt
    
    regular expr
    ^ - match the beginning of the line
    $ - match the end of the line
    . - match any single character
    * - match arbitrary many occurrences of charater
    ? - match 0 or 1 instance of character
    

    Spinlock and Semaphore
  • Spinlocks are very small and fast, and can be used anywhere. If your task can't get the spinlock, your task keeps trying (spinning) until your task can.
  • Semaphore can have more than one holder at any time (the number decided at initialization time), although it is most commonly used as a single-holder lock (known as mutex). If your task can't get a semaphore, your task will put itself on the queue, and be woken up when the semaphore is released. This means the CPU will do something else while your task is waiting.
  • Semaphore is used for synchronization between user contexts. User Context means the kernel is executing on behalf of a particular process (ie. a system call or trap) or kernel thread. This is not to be confused with userspace. It can be interrupted by software or hardware interrupts.
  • Spinlock is used for synchronization between user context and interrupt context. spin_lock_bh() disables softirqs on that CPU, then grabs the lock. spin_lock_irq() is defined to disable interrupts on that cpu, then grab the lock.

    Exporting Symbols
  • In source file:
     #define EXPORT_SYMTAB
    
    and then after you write your function:
     EXPORT_SYMBOL(your_function_name);
    
  • In Makefile:

  •  export-objs := filename.o
    

    Networking commands
    Commands for device statistics:
    cat /proc/net/dev
      show device statistics
    cat /proc/net/snmp
      show snmp statistics
    
    
    Commands for routing table:
    route add -net 192.168.2.0 netmask 255.255.255.0 gw 192.168.1.1
      add gateway 192.168.1.1 to routing table for network 192.168.2.0
    route del -net 192.168.2.0/24
      delete routing table entry
    route -n
      show routing table status
    

    Commands for ethernet:
    ifconfig eth0 txqueuelen 10000
      increase the transmit queue of the network interface
    echo 1 > /proc/sys/net/ipv4/ip_forward
      enable ip forwarding
    /sbin/ethtool -K eth0 tso off
      disable TCP segment offloading
    /bin/uname -r
      show kernel version
    
    netstat -p --tcp
      show network connection for tcp protocol

    Reset procedures for MIPS and ARM




  • MIPS IDT438
    sys_reboot (kernel/sys.c)
    --> machine_restart (arch/mips/kernel/reeseet.c)
        --> _machine_restart (assigned in arch/mips/rc32438/79EB438/setup.c)
           --> idt_reset (arch/mips/rc32438/79EB438/reset.c)
    
  • ARM IXP425

  • sys_reboot (kernel/sys.c)
    --> machine_restart (arch/arm/kernel/prooceess.c)
        --> arch_reset (include/asm-arm/arch-ixp425/system.h)
    

    3. Shell Script Programming

    A. Passing parameters to shell script
    As a simple example, i have a shell script which requires to take in one parameter. I name it "cp_script".
    #!/bin/sh
    echo "Parameter " $1
    
    Then i call the shell script on the command line and pass it a parameter:
    $./cp_script madwifi
    Parameter madwifi
    
    That's it, using $1 in shell script to take in the command line parameter.

    B. If-else-fi in shell script:
    [ expr ] is used to see if an expression is true.
    if [ -f $dir/$file ] || [ -f $dir/$newfile ]; then
        echo "Either this filename [$file] exists"
        echo "Or this filename [$newfile] exists"
    elif [ -d $dir ]; then
        echo "This dirname [$dir] exists"
    else
        echo "Neither [$dir] or [$file or $newfile] exist"
    fi
    

    C. Variable declaration:
    We use something as:
    #!/bin/sh
    
    TC="/sbin/tc"
    
    $TC qdisc ls
    $TC qdisc del dev eth0 root
    $TC qdisc add dev eth0 root pfifo_fast
    
    
    
    
    D. The getopt command
    The getopt command is used to parse the command line parameters. It is made up of two parts: options and non-option parameters.
    -o specifies the short option, -n is the name of the program, -- is the start of non-option parameters
    #! /bin/bash
    
    echo "param" $@
    TEMP=`getopt -o nr -n oudanhodou -- "$@"`
    echo "temp" $TEMP
    unset TEMP
    
    
    Execution:
    $ ./oudanhodou -r
    param -r
    temp -r --
    
    $ ./oudanhodou -r fresh
    param -r fresh
    temp -r -- 'fresh'
    

    4. Linux Setup

    A. Linux network setup on Redhat 9
    /etc/hosts
    127.0.0.1  localhost
    
    /etc/host.conf
    order host,bind
    
    /etc/resolv.conf
    nameserver  192.168.4.254
    
    It is the IP of DNS server.
    /etc/services
    
    It contains port allocation information.

    B. Change ip address
    /etc/sysconfig/network-scripts/ifcfg-eth0
    IPADDR=192.168.4.239
    DHCP_HOSTNAME=192.168.4.251
    
    /sbin/ifup eth0
    
    C. Use dhcp client
    execute redhat-config-network
    
    then execute /sbin/dhclient
    
    settings in /etc/dhclient-eth0.conf
    
    For Dhcp server and client settings on embedded system, click on DHCP info.

    D. Update linux kernel
    1. create floppy boot disk
    ls /lib/modules
     2.4.2-2
    
    mkbootdisk --device /dev/fd0 2.4.2-2
    
    2. clean old kernel config
    make mrproper
    
    3. change whatever settings you want
    make menuconfig
    
    4. update the change
    make dep
    
    5. make it
    make bzImage
    
    6. make the modules
    make modules
    
    7. install the newly made modules
    make modules_install
    
    8. update the boot loader config file with new kernel information
    /etc/lilo.conf
    
    9. read from new boot loader config file and store into boot sector
    /sbin/lilo -v
    
    
    E. lilo information
    "/sbin/lilo -v" read the /etc/lilo.conf file, to determine what to write to MBR.
    lilo.conf example
    boot=/dev/hda #install lilo in the first harddisk
    map=/boot/map
    install=/boot/boot.b #specify new boot sector file
    prompt
    timeout=50 #wait for 5 sec
    lba32    #describe harddisk geometry
    default=linux
    image=/boot/vmlinus-2.4.20-8 #specify linux kernel
     label=linux
     root=/dev/hda1  #specify root partition
    image=/boot/vmlinus
     label=failsafe
     root=/dev/hda1
     initrd=/boot/initrd.img
    other=/dev/hda2
     label=windows
     table=/dev/hda
    
    
    F. Make ramdisk
    mkdir ramdisk
    cd ramdisk
    dd if=/dev/zero of=initrd bs=1k count=8192
     bs is the block size
    /sbin/mke2fs -vFm0 initrd 8192
    mkdir mnt
    sudo mount -o loop initrd mnt
    cd mnt
    cp -a .....
    cd ..
    sudo umount initrd
    
    To do "mount -o loop" on target platform:
    1. kernel got to have loopback device support enabled
    2. On file system, create /dev/loop file
    3. kernel got to enable ext2 support
    
    target bootloader configured with:
    bootparm1=/root=/dev/ram init=/linuxrc rw
    
    
    G. Starting X windows
    startx > log 2>&1
    gcc -Wl,-v 2>&1 | grep "GNU ld"
    

    H. Sudo List
    In /etc/sudoers file, we can add in a list of users to specify their permissions.
    User_Alias   SW_STAFF = julian, hunk, yeosv
    # User privilege specification
    root   ALL=(ALL) ALL
    SW_STAFF  ALL=NOPASSWD: ALL
    newcomer ALL=(ALL) NOPASSWD: ALL
    
    
    I. Kernel Booting Sequences
    This is the kernel booting sequences for ARM architecture processor. This information may apply to other architecture with minor modification.
    (arch/arm/boot/compressed/head.S)
     -> setup the stack
     -> call decompressed_kernel (arch/arm/boot/compressed/misc.c)
     -> jump to decompressed code
    
    (arch/arm/kernel/head.S)
    _stext   -> start_kernel (init/main.c)     [ task 0, idle task ]
       -> setup_arch (arch/arm/kernel/setup.c)
       -> trap_init
       -> init_IRQ
       -> sched_init
       -> softirq_init
       -> time_init
       -> console_init
       -> init_modules
       -> kmem_cache_init
       -> calibrate_delay
       -> mem_init
       -> kmem_cache_sizes_init
       -> fork_init
       -> proc_caches_init
       -> vfs_caches_init
       -> ...
       -> rest_init             [ launch init kernel thread ]
        -> init (init/main.c)     [ the "init" kernel thread ]
         -> do_basic_setup
         -> prepare_namespace
         -> launch /sbin/init
    
    include/asm-arm/arch-ixp425/memory.h
     contains PAGE_OFFSET and PHYS_OFFSET
    
    arch/arm/boot/Makefile
     contains ZTEXTADDR and ZRELADDR