All notes
Posix

POSIX programming

Mutex, Semaphore, SpinLock

51cto. StackOverflow: when should one use a spinlock instead of mutex. MakeLinux: spinlock. JustSoftwareSolutions: locks, mutexes, semaphore.

首先spinlock是只有在内核态才有的,当然你也可以在用户态自己实现,但是如果想要调用spinlock_t类型,那只有内核态才有。但是semaphore是内核态和用户态都有的,mutex是一种特殊的semaphore。

spinlock是一种忙等待,也就是说,进程是不会睡眠的,只是一直在那里死循环。而mutex是睡等,也就是说,如果拿不到临界资源,那它会选择进程睡眠。那什么时候用spinlock,什么时候用mutex呢?首先,如果是在不允许睡眠的情况下,只能只用spinlock,比如中断的时候(interrupt handlers)。然后如果临界区中执行代码的时间小于进程上下文切换的时间,那应该使用spinlock。反之应该使用mutex, esp. when running with a single-core CPU。

那mutex和semaphore有什么区别呢?mutex是用作互斥的,而semaphore是用作同步的。也就是说,mutex的初始化一定是为1,而semaphore可以是任意的数,所以如果使用mutex,那第一个进入临界区的进程一定可以执行,而其他的进程必须等待。而semaphore则不一定,如果一开始初始化为0,则所有进程都必须等待。同时mutex和semaphore还有一个区别是,获得mutex的进程必须亲自释放它,而semaphore则可以一个进程获得,另一个进程释放。

On a multi-core/multi-CPU systems, with plenty of locks that are held for a very short amount of time only, the time wasted for constantly putting threads to sleep and waking them up again might decrease runtime performance noticeably.

A hybrid mutex behaves like a spinlock at first on a multi-core system. If a thread cannot lock the mutex, it won't be put to sleep immediately, since the mutex might get unlocked pretty soon, so instead the mutex will first behave exactly like a spinlock.

A hybrid spinlock behaves like a normal spinlock at first, but to avoid wasting too much CPU time, it may have a back-off strategy. It will usually not put the thread to sleep (since you don't want that to happen when using a spinlock), but it may decide to stop the thread (either immediately or after a certain amount of time).

If in doubt, use mutexes, they are usually the better choice and most modern systems will allow them to spinlock for a very short amount of time, if this seems beneficial.

Semaphore

CSDN. 信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:
P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.
The single-letter operation names are from Dijkstra's original paper on semaphores.

Read/Write Locks

C++ examples

std::mutex and std::timed_mutex are just plain single-owner mutexes.
std::recursive_mutex and std::recursive_timed_mutex are recursive mutexes, so multiple locks may be held by a single thread.
std::shared_timed_mutex is a read/write mutex.

//
// Lock_guard.
//

std::mutex m;
void f(){
    std::lock_guard<std::mutex> guard(m);
    // do stuff
}

//
// Unique_lock can be returned from a function without releasing the lock, and can have the lock released before the destructor:
//

std::mutex m;
std::unique_lock<std::mutex> f(){
    std::unique_lock<std::mutex> guard(m);
    // do stuff
    return std::move(guard);
}

void g(){
    std::unique_lock<std::mutex> guard(f());
    // do more stuff
    guard.unlock();
}

//
// Shared_lock and shared_timed_mutex.
//

std::shared_timed_mutex m;
void reader(){
    std::shared_lock<std::shared_timed_mutex> guard(m);
    // do read-only stuff
}
void writer(){
    std::lock_guard<std::shared_timed_mutex> guard(m);
    // update shared data
}

fork

#include <unistd.h>
pid_t fork(void);

Fork() causes creation of a new process. The new process (child process) is an exact copy of the calling process (parent process) except for the following:

vfork

The vfork() function is the same as fork() except that it does not make a copy of the address space. The memory is shared reducing the overhead of spawning a new process with a unique copy of all the memory.

#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>   // Declaration for exit()

using namespace std;

int globalVariable = 2;

mainFork()
{
	string sIdentifier;
	int    iStackVariable = 20;

	id_t pID = fork();
	if (pID == 0) {                // child
	  // Code only executed by child process
	  sIdentifier = "Child Process: ";
	  globalVariable++;
	  iStackVariable++;
	}
	else if (pID < 0) {           // failed to fork
	    cerr << "Failed to fork" << endl;
	    exit(1);
	}
	else {                                   // parent
	  // Code only executed by parent process
	  sIdentifier = "Parent Process:";
	}
	
	// Code executed by both parent and child.
	cout << sIdentifier;
	cout << " Global variable: " << globalVariable;
	cout << " Stack variable: "  << iStackVariable << endl;
}

// Result for fork():
//+ Parent Process: Global variable: 2 Stack variable: 20
//+	Child Process:  Global variable: 3 Stack variable: 21

int globalVariable = 2;
mainVfork()
{
	string sIdentifier;
	int    iStackVariable = 20;
	
	pid_t pID = vfork();
	if (pID == 0) {				   // child
	  // Code only executed by child process
	  sIdentifier = "Child Process: ";
	  globalVariable++;
	  iStackVariable++;
	  cout << sIdentifier;
	  cout << " Global variable: " << globalVariable;
	  cout << " Stack variable: "  << iStackVariable << endl;
	  _exit(0);
	}
	else if (pID < 0) {			   // failed to fork
		cerr << "Failed to fork" << endl;
		exit(1);
	}
	else {									// parent
	  // Code only executed by parent process
	  sIdentifier = "Parent Process:";
	}
	
	// executed only by parent
	cout << sIdentifier;
	cout << " Global variable: " << globalVariable;
	cout << " Stack variable: "	 << iStackVariable << endl;
	exit(0);
}

// Result for vfork():
//+ Child Process:  Global variable: 3 Stack variable: 21
//+ Parent Process: Global variable: 3 Stack variable: 21

exec

execl, execlp, execle, execv, execvp, execvpe - execute a file. Ref.

#include <unistd.h>

extern char **environ;

int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);

#include <unistd.h>

execl("/bin/ls", "/bin/ls", "-r", "-t", "-l", (char *) 0);

char *args[] = {"/bin/ls", "-r", "-t", "-l", (char *) 0 };
execv("/bin/ls", args);

char *env[] = { "USER=user1", "PATH=/usr/bin:/bin:/opt/bin", (char *) 0 };
char *Env_argv[] = { (char *)"/bin/ls", (char *)"-l", (char *)"-a", (char *) 0 };
execve (Env_argv[0], Env_argv, env);

wait, waitpid

Wait for a child process to stop or terminate. Ref.

#include <sys/wait.h>
pid_t wait(int *stat_loc);
pid_t waitpid(pid_t pid, int *stat_loc, int options);

getopt

getopt has three ways to deal with options that follow non-options argv elements:

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int
main (int argc, char **argv)
{
  int aflag = 0;
  int bflag = 0;
  char *cvalue = NULL;
  int index;
  int c;

  opterr = 0;
  while ((c = getopt (argc, argv, "abc:")) != -1)
    switch (c)
      {
      case 'a':
        aflag = 1;
        break;
      case 'b':
        bflag = 1;
        break;
      case 'c':
        cvalue = optarg;
        break;
      case '?':
        if (optopt == 'c')
          fprintf (stderr, "Option -%c requires an argument.
", optopt);
        else if (isprint (optopt))
          fprintf (stderr, "Unknown option `-%c'.
", optopt);
        else
          fprintf (stderr,
                   "Unknown option character `\x%x'.
",
                   optopt);
        return 1;
      default:
        abort ();
      }
  printf ("aflag = %d, bflag = %d, cvalue = %s
",
          aflag, bflag, cvalue);

  for (index = optind; index < argc; index++)
    printf ("Non-option argument %s
", argv[index]);
  return 0;
}

// % testopt
// aflag = 0, bflag = 0, cvalue = (null)
// 
// % testopt -a -b
// aflag = 1, bflag = 1, cvalue = (null)
// 
// % testopt -ab
// aflag = 1, bflag = 1, cvalue = (null)
// 
// % testopt -c foo
// aflag = 0, bflag = 0, cvalue = foo
// 
// % testopt -cfoo
// aflag = 0, bflag = 0, cvalue = foo
// 
// % testopt arg1
// aflag = 0, bflag = 0, cvalue = (null)
// Non-option argument arg1
// 
// % testopt -a arg1
// aflag = 1, bflag = 0, cvalue = (null)
// Non-option argument arg1
// 
// % testopt -c foo arg1
// aflag = 0, bflag = 0, cvalue = foo
// Non-option argument arg1
// 
// % testopt -a -- -b
// aflag = 1, bflag = 0, cvalue = (null)
// Non-option argument -b
// 
// % testopt -a -
// aflag = 1, bflag = 0, cvalue = (null)
// Non-option argument -

An option character in this string can be followed by a colon (':') to indicate that it takes a required argument.
If an option character is followed by two colons ('::'), its argument is optional; this is a GNU extension.

mkdir

#include <sys/stat.h>

int mkdir(const char *path, mode_t mode);

int status;
// Create the dir with permission 775.
status = mkdir("/home/cnd/mod1", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
On success, it returns 0. http://pubs.opengroup.org/onlinepubs/009695399/functions/mkdir.html.

pipe

参数filedes返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输入。

#include <unistd.h>

// Un-named pipe.
int pipe(int filedes[2]);

// Another method: named pipe:
mkfifo("myfifo","rw");

popen

Ref.

#include <stdio.h>

FILE * popen(const char *command, const char *mode);
int pclose(FILE *stream);

Shared memory

cnblogs.

共享内存是运行在同一台机器上的进程间通信最快的方式。通常由一个进程创建一块共享内存区,其余进程对这块内存区进行读写。

得到共享内存有两种方式:映射/dev/mem设备和内存映像文件。前一种方式不给系统带来额外的开销,但在现实中并不常用,因为它控制存取的将是实际的物理内存,在Linux系统下,这只有通过限制Linux系统存取的内存才可以做到,这当然不太实际。

使用共享存储来实现进程间通信的注意点是对数据存取的同步,必须确保当一个进程去读取数据时,它所想要的数据已经写好了。通常,信号量被要来实现对共享存储数据存取的同步,另外,可以通过使用shmctl函数设置共享存储内存的某些标志位如SHM_LOCK、SHM_UNLOCK等来实现。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

// One process create a shared memory.
// Return value is the shmid.
int shmget(key_t key, int size, int flag);

// Other proceses use this shm.
// INPUT shmid is the output of shmget().
// Return value is the actual memory addr to be used.
void *shmat(int shmid, void *addr, int flag);

semaphore

#include <stdio.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
void main() {
  key_t unique_key; /* 定义一个IPC关键字*/
  int id;
  struct sembuf lock_it;
  union semun options;
  int i;
  
  unique_key = ftok(".", 'a'); /* 生成关键字,字符'a'是一个随机种子*/
  /* 创建一个新的信号量集合*/
  id = semget(unique_key, 1, IPC_CREAT | IPC_EXCL | 0666);
  printf("semaphore id=%d
", id);
  options.val = 1; /*设置变量值*/
  semctl(id, 0, SETVAL, options); /*设置索引0的信号量*/
  
  /*打印出信号量的值*/
  i = semctl(id, 0, GETVAL, 0);
  printf("value of semaphore at index 0 is %d
", i);
  
  /*下面重新设置信号量*/
  lock_it.sem_num = 0; /*设置哪个信号量*/
  lock_it.sem_op = -1; /*定义操作*/
  lock_it.sem_flg = IPC_NOWAIT; /*操作方式*/
  if (semop(id, &lock_it, 1) == -1) {
      printf("can not lock semaphore.
");
      exit(1);
  }
  
  i = semctl(id, 0, GETVAL, 0);
  printf("value of semaphore at index 0 is %d
", i);
  
  /*清除信号量*/
  semctl(id, 0, IPC_RMID, 0);
}

dirname, basename

#include <libgen.h>

char *dirname(char *path);
char *basename(char *path);
Points to note:

path         dirname     basename
"/usr/lib"      "/usr"      "lib"
"/usr/"         "/"         "usr"
"usr"           "."         "usr"
"/"             "/"         "/"
""              "."         "."
"."             "."         "."
".."            "."         ".."

http://linux.die.net/man/3/dirname.

File status

http://stackoverflow.com/questions/12774207/fastest-way-to-check-if-a-file-exist-using-standard-c-c11-c gives a comparison on what is the fastest way to determine whether a file exists.

#include <unistd.h>
int access(const char *path, int amode);

if( access( fname, F_OK ) != -1 ) {
    // file exists
} else {
    // file doesn't exist
}

#include <sys/stat.h>

inline bool exists_test2 (const std::string& name) {
	return ( access( name.c_str(), F_OK ) != -1 );
}

inline bool exists_test3 (const std::string& name) {
	struct stat buffer;	
	return (stat (name.c_str(), &buffer) == 0); 
}

You can also use R_OK, W_OK, and X_OK in place of F_OK (file existence) to check for read permission, write permission, and execute permission (respectively) rather than existence, and you can OR any of them together (i.e. check for both read and write permission using R_OK|W_OK). http://stackoverflow.com/questions/230062/whats-the-best-way-to-check-if-a-file-exists-in-c-cross-platform.

ioctl

Ref.

ioctl(keyFd, FIONREAD, &b)
得到缓冲区里有多少字节要被读取,然后将字节数放入b里面。
Ref: err = ioctl(pipedesc, FIONREAD, &bytesAvailable);, Unless this returns the error code for "invalid argument" (or any other error) bytesAvailable contains the amount of data available for unblocking read operations at that time.