在阅读操作系统导论一书,书中第二章有个例子如下:
common.h

#ifndef __common_h__
#define __common_h__

#include <sys/time.h>
#include <sys/stat.h>
#include <assert.h>

double GetTime() {
    struct timeval t;
    int rc = gettimeofday(&t, NULL);
    assert(rc == 0);
    return (double) t.tv_sec + (double) t.tv_usec/1e6;
}

void Spin(int howlong) {
    double t = GetTime();
    while ((GetTime() - t) < (double) howlong)
    ; // do nothing in loop
}

#endif // __common_h__

mem.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "common.h"

int main(int argc, char *argv[]) {
    if (argc != 2) { 
    fprintf(stderr, "usage: mem <value>\n"); 
    exit(1); 
    } 
    int *p; 
    p = malloc(sizeof(int));
    assert(p != NULL);
    printf("(%d) addr pointed to by p: %p\n", (int) getpid(), p);
    *p = atoi(argv[1]); // assign value to addr stored in p
    while (1) {
    Spin(1);
    *p = *p + 1;
    printf("(%d) value of p: %d\n", getpid(), *p);
    }
    return 0;
}

书中的例子是以./mem & ; ./mem & 演示同一行运行多个linux命令,我这边却报错:

-bash: syntax error near unexpected token `;'

linux系统中同一行运行多个命令的方法,归纳起来有如下三种,可以组合使用:
每个命令之间用;隔开
说明:各命令的执行给果,不会影响其它命令的执行。换句话说,各个命令都会执行,但不保证每个命令都执行成功。
这种方式我试验了一些基本命令是ok的,但涉及到需要后台运行的会报错。

[root intro]#ls;cd /root
common.h          cpu    get_sp    io.c      mem    parallel.sh  threads.c
common_threads.h  cpu.c  get_sp.c  Makefile  mem.c  README.md
[root ~]#

每个命令之间用&&隔开
说明:若前面的命令执行成功,才会去执行后面的命令。这样可以保证所有的命令执行完毕后,执行过程都是成功的。

每个命令之间用||隔开
说明:||是或的意思,只有前面的命令执行失败后才去执行下一条命令,直到执行成功一条命令为止。

如果要以后台方式运行可以使用./mem 10 & ./mem 20 &
但这种方式有个弊端,需要手动kill掉任务,否则一直运行,如果有内容输出到终端的话,用户体验就有点差了。
how to run two or more terminal commands at once in linux
想起了3 4年前在stackoverflow copy的一个大佬的脚本,非常之有用

#!/bin/bash

for cmd in "$@"; do {
  echo "Process \"$cmd\" started";
  $cmd & pid=$!
  PID_LIST+=" $pid";
} done

trap "kill $PID_LIST" SIGINT

echo "Parallel processes have started";

wait $PID_LIST

echo
echo "All processes have completed";

上面的脚本是在后台运行任务,然后记录下进程号,捕获SIGINT (ctrl + c),然后kill掉进程。这个脚本中有个疑惑的地方是脚本中

$cmd & pid=$!

这里的写法居然和我司某大佬写法一致,就不能写成两行吗?这么写除了让人看不懂还有啥好处吗?
脚本相关参考文章:
Bash shell内置wait命令简介

[root intro]#./parallel.sh "./mem 10" "./mem 100"
Process "./mem 10" started
Process "./mem 100" started
Parallel processes have started
(6615) addr pointed to by p: 0x55d3d2265260
(6616) addr pointed to by p: 0x55d068656260
(6615) value of p: 11
(6616) value of p: 101
(6615) value of p: 12
(6616) value of p: 102
(6615) value of p: 13
(6616) value of p: 103
(6615) value of p: 14
(6616) value of p: 104
^C
All processes have completed

可是我同时运行两个实例,p的地址不相同,与书上说的不同。这里需要注意的是,书中的结果是在禁用地址空间随机化之后的运行结果。

[root intro]#
[root intro]#
[root intro]#echo 0 > /proc/sys/kernel/randomize_va_space
[root intro]#
[root intro]#./parallel.sh "./mem 10" "./mem 100"
Process "./mem 10" started
Process "./mem 100" started
(6660) addr pointed to by p: 0x555555756260
Parallel processes have started
(6661) addr pointed to by p: 0x555555756260
(6660) value of p: 11
(6661) value of p: 101
(6660) value of p: 12
(6661) value of p: 102
(6660) value of p: 13
(6661) value of p: 103
(6660) value of p: 14
(6661) value of p: 104
^C
All processes have completed

上面可以看到我们禁用地址空间随机化后,运行结果与书中结果一致,运行同一程序的多个实例,在相同地址申请了内存,但似乎相同的内存地址处有不同的值。原因究竟为何?
下面几篇文章介绍了这一特性(由浅入深)
ASLR 是如何保护 Linux 系统免受缓冲区溢出攻击的
address space layout randomization
ALSR

Linux ASLR can be configured through setting a value in /proc/sys/kernel/randomize_va_space.
Three types of flags are available

0 – No randomization. Everything is static.
1 – Conservative randomization. Shared libraries, stack, mmap(), VDSO and heap are randomized.
2 – Full randomization. In addition to elements listed in the previous point, memory managed through brk() is also randomized.

我们可以使用如下程序验证,当设置为2时验证栈指针ESP一直在变化:

#include<stdio.h>

unsigned long sp(void){
    #if defined(__i386__)
        asm("mov %esp, %eax");
    #elif defined(__x86_64__)
        asm("mov %rsp, %rax");
    #endif
}

int main(int argc, const char *argv[])
{
    unsigned long esp = sp();
    printf("Stack pointer (ESP : 0x%lx)\n",esp);
    return 0;
}

运行结果:

[root intro]#sysctl -w kernel.randomize_va_space=2
kernel.randomize_va_space = 2
[root intro]#./get_sp
Stack pointer (ESP : 0x7ffe2c975840)
[root intro]#./get_sp
Stack pointer (ESP : 0x7fff4d0c4100)
[root intro]#./get_sp
Stack pointer (ESP : 0x7ffd3d30b3e0)
[root intro]#./get_sp
Stack pointer (ESP : 0x7ffc50a7ff00)
[root intro]#./get_sp
Stack pointer (ESP : 0x7ffebd14b9c0)

关闭地址空间随机化后 我们可以看到:运行一个程序的多个实例,每个正在运行的程序都在相同的地址处分配了内存,但每个实例似乎都独立更新了值。实际上这是由于操作系统虚拟化内存这一特性。每个进程访问自己的私有虚拟地址空间(virtual address space),操作系统以某种方式映射到机器的物理内存上。因此关闭地址空间随机化后,相同程序运行的实例打印出的虚拟地址相同,但实际映射到的物理内存不同,因此能够正确更新各自映射的物理内存内容。