文章目录 隐藏目录

1、环境变量的操作

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

// 声明环境变量
extern char** environ;
int main()
{
    // 输出全部环境变量
    for (int i = 0; environ[i]; ++i) {
        printf("%s\n",environ[i]);
    }
    // getenv - get an environment variable
    // setenv - change or add an environment variable
    printf("%d\n", setenv("ABB","QQQ",true));
    printf("%s\n", getenv("ABB"));

    return 0;
}

2、创建进程

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>

void process() 
{
    // 进程创建完成后所有资源子进程都拷贝一份,与父进程独立
    int top = 0; 
    // fork - create a child process
    // getpid - get process identification
    // sleep - sleep for a specified number of seconds
    pid_t pid = fork();
    if (pid == -1) printf("create a child process failed!\n");
    else if (pid == 0) {
        printf("child process id:%d\n",getpid());
        while (true) {
            sleep(1);
            printf("%d\n", top++);
        }
    }else {
        printf("process id:%d\n", getpid());
        while (true) {
            sleep(1);
            printf("%d\n", top);
        }
    }
}
void more_process() {
    int i;
    // 创建多个线程
    for (i = 0; i < 10; ++i) {
        pid_t cpid = fork();
        if (cpid == -1);// 创建错误
        else if (cpid == 0) break;
    }
    if(i == 10) printf(" process id:%d\n", getpid());
    else printf("child process id:%d\n", getpid());
}
int main() 
{
    more_process();
    return 0;
}

进程创建完成后所有资源子进程都拷贝一份,与父进程独立(读时共享,写时复制),只有少部分数据不独立(文件标识符、mmap建立映射区)。

3、获取用户ID和组ID操作

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

int main() 
{
    // 获取用户ID,有效用户ID
    printf("%ld\n",getuid());
    printf("%ld\n", geteuid());
    printf("%ld\n", getgid());
    printf("%ld\n", getegid());
    return 0;
}

4、exec函数

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

int main() 
{
    // execl, execlp, execle, execv, execvp, execvpe - execute a file
    pid_t pid = fork();
    if (pid == -1);
    else if (pid == 0) {
        // execlp("ls","ls","-l", nullptr);
        execl("/bin/ls", "ls", "-l", nullptr);
    }
    else printf("main process...\n");
    return 0;
}

5、文件描述符复制(dup)

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
    // execl, execlp, execle, execv, execvp, execvpe - execute a file
    int fd = open("/home/pouee/ps.out",O_WRONLY|O_CREAT|O_TRUNC,0644);
    dup2(fd, 1);
    pid_t pid = fork();
    if (pid == -1);
    else if (pid == 0) {
        // execlp("ls","ls","-l", nullptr);
        execl("/bin/ls", "ls", "-l", nullptr);
    }
    else printf("main process...\n");
    return 0;
}

6、孤儿进程

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>

int main()
{
    pid_t pid = fork();
    if (pid == -1);
    else if (pid == 0) {
        sleep(2);
        // 父进程先结束后,子进程将会变成孤儿进程
        printf("child process id: %d, parent id: %d\n", getpid(), getppid());
    }
    else {
        printf("process id:%d\n", getpid());
    }
    return 0;
}

7、僵尸进程

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>

int main()
{
    pid_t pid = fork();
    if (pid == -1);
    else if (pid == 0) {
        printf("Finished! \n");
    }
    else {
        while (true);
        // 父进程若不处理子进程,子进程结束时将变成僵尸进程,PCB驻留内存
        // pouee     11336  0.0  0.0      0     0 ? Z    21 : 25   0 : 00[KernelProgrammi] <defunct>
        printf("process id:%d\n", getpid());
    }
    return 0;
}

8、用wait函数回收子进程

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>

// 回收子进程
// pid_t wait(int *wstatus);
// pid_t waitpid(pid_t pid, int* wstatus, int options);

int main()
{
    // The wait() system call suspends execution of the calling
    // process until one of  its  children terminates.  
    pid_t pid = fork();
    if (pid == -1);
    else if (pid == 0) {
        sleep(18);
        printf("Finished! \n");
        return 10;
    }
    else {
        int status;
        //pid_t rp = wait(nullptr);
        /*
            The  waitpid()  system  call  suspends  execution of the calling process until a child specified by pid
            argument has changed state.  By default, waitpid() waits only for terminated children, but this  behav‐
            ior is modifiable via the options argument, as described below.
            The value of pid can be:
            <-1    meaning wait for any child process whose process group ID is equal to the absolute value of pid.
            -1     meaning wait for any child process.
            0      meaning  wait  for  any  child  process  whose  process group ID is equal to that of the calling
                   process.
            >0     meaning wait for the child whose process ID is equal to the value of pid.
        */
        pid_t rp = 0;
        // rp = waitpid(pid, &status, 0);

        /*
            The value of options is an OR of zero or more of the following constants:
            WNOHANG     return immediately if no child has exited.
            WUNTRACED   also return if a child has stopped (but not traced via ptrace(2)).  Status for traced chil‐
                        dren which have stopped is provided even if this option is not specified.
            WCONTINUED  (since Linux 2.6.10)
                        also return if a stopped child has been resumed by delivery of SIGCONT.
        */
        while (!rp)rp = waitpid(pid, &status, WNOHANG);
        if (rp == -1) { printf("failed!\n"); }
        else {
            // 正常退出
            if (WIFEXITED(status)) {
                printf("return value: %d\n", WEXITSTATUS(status));
            }
            else if (WIFSIGNALED(status)) {  // 接受到信号
             // 执行 kill -9 14419 可以得到9号信号
                printf("signal value: %d\n", WTERMSIG(status));
            }
        }
    }
    return 0;
}

9、使用管道进行IPC

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>

int main()
{
    int fd[2],buf;
    int ret = pipe(fd);
    if (ret == -1);//error
    pid_t pid = fork();
    if (pid == -1);
    else if (pid != 0) {
        close(fd[1]);
        while (read(fd[0], &buf, sizeof(int))) {
            printf("read : %d\n",buf);
        }
    }else {
        close(fd[0]);
        for (int i = 0; i < 10; ++i) {
            buf = i;
            write(fd[1], &buf, sizeof(int));
            sleep(1);
        }
    }
    return 0;
}

10、使用mmap函数操作文件

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#define MSIZE 1024 * 1024
int main()
{
    int fd = open("/home/pouee/mytestcc", O_CREAT | O_RDWR, 0644);
    int len = ftruncate(fd, MSIZE);// 必须有大小
    // MSIZE不能为零
    // PROT_READ:   映射区可读
    // PROT_WRITE:  映射区可写
    // MAP_SHARED:  与文件同步
    // 最后一个参数必须是操作系统页的整数倍,代表便宜
    char *p = (char *)mmap(nullptr, 
        MSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (p == MAP_FAILED); // error
    strcpy(p,"Hello World!"); // 复制数据到内存
    munmap(p, MSIZE);           // 关闭映射
    close(fd);                  // 关闭文件
    return 0;
}

11、使用共享内存(mmap函数)父子进程间通信

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#define MSIZE 1024 * 1024
int main()
{
    //int fd = open("/home/pouee/mytestcc", O_CREAT | O_RDWR, 0644);
    //int len = ftruncate(fd, MSIZE);
    char *p = (char *)mmap(nullptr, 
        // 匿名映射,只能在Linux中使用,不能在Unix类似等系统上使用
        // 可以使用字符设备文件 /dev/zero                   
        MSIZE, PROT_READ | PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, /*0*/-1, 0);
    if (p == MAP_FAILED); // error
    pid_t pid = fork();
    if (pid == -1)printf("ERROR\n");
    else if (pid == 0) { 
        *p = 'a'; // 子进程传递值给父进程
    } else {
        sleep(1);
        printf("RET:%c\n", *p);
        munmap(p, MSIZE);
    }
    munmap(p, MSIZE);
    //close(fd);
    return 0;
}

12、使用共享内存(mmap函数)一般进程间通信

进程一

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#define MSIZE 1024 * 1024
int main()
{
    int fd = open("/home/pouee/mytest.cc", O_CREAT | O_RDWR, 0644);
    int len = ftruncate(fd, MSIZE);
    char *p = (char *)mmap(nullptr, 
        MSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (p == MAP_FAILED); // error

    sleep(10);
    // 接收一个字符数据
    printf("RET:%c\n", *p);
    munmap(p, MSIZE);
    sleep(10);
    close(fd);
    return 0;
}

进程二

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#define MSIZE 1024 * 1024
int ma1in()
{
    int fd = open("/home/pouee/mytest.cc", O_CREAT | O_RDWR, 0644);
    int len = ftruncate(fd, MSIZE);
    char* p = (char*)mmap(nullptr,
        MSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (p == MAP_FAILED); // error

    *p = 'z';
    munmap(p, MSIZE);
    sleep(10);
    close(fd);
    return 0;
}

13、Linux常规信号一览表

1) SIGHUP: 当用户退出shell时,由该shell启动的所有进程将收到这个信号,默认动作为终止进程

2) SIGINT:当用户按下了<Ctrl+C>组合键时,用户终端向正在运行中的由该终端启动的程序发出此信号。默认动

作为终止进程。

3) SIGQUIT:当用户按下<ctrl+>组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出些信

号。默认动作为终止进程。

4) SIGILL:CPU检测到某进程执行了非法指令。默认动作为终止进程并产生core文件

5) SIGTRAP:该信号由断点指令或其他 trap指令产生。默认动作为终止里程 并产生core文件。

6) SIGABRT: 调用abort函数时产生该信号。默认动作为终止进程并产生core文件。

7) SIGBUS:非法访问内存地址,包括内存对齐出错,默认动作为终止进程并产生core文件。

8) SIGFPE:在发生致命的运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为0等所有的算法错误。默认动作为终止进程并产生core文件。

9) SIGKILL:无条件终止进程。本信号不能被忽略,处理和阻塞。默认动作为终止进程。它向系统管理员提供了可以杀死任何进程的方法。

10) SIGUSE1:用户定义 的信号。即程序员可以在程序中定义并使用该信号。默认动作为终止进程。

11) SIGSEGV:指示进程进行了无效内存访问。默认动作为终止进程并产生core文件。

12) SIGUSR2:另外一个用户自定义信号,程序员可以在程序中定义并使用该信号。默认动作为终止进程。

13) SIGPIPE:Broken pipe向一个没有读端的管道写数据。默认动作为终止进程。

14) SIGALRM: 定时器超时,超时的时间 由系统调用alarm设置。默认动作为终止进程。

15) SIGTERM:程序结束信号,与SIGKILL不同的是,该信号可以被阻塞和终止。通常用来要示程序正常退出。执行shell命令Kill时,缺省产生这个信号。默认动作为终止进程。

16) SIGSTKFLT:Linux早期版本出现的信号,现仍保留向后兼容。默认动作为终止进程。

17) SIGCHLD:子进程结束时,父进程会收到这个信号。默认动作为忽略这个信号。

18) SIGCONT:如果进程已停止,则使其继续运行。默认动作为继续/忽略。

19) SIGSTOP:停止进程的执行。信号不能被忽略,处理和阻塞。默认动作为暂停进程。

20) SIGTSTP:停止终端交互进程的运行。按下<ctrl+z>组合键时发出这个信号。默认动作为暂停进程。

21) SIGTTIN:后台进程读终端控制台。默认动作为暂停进程。

22) SIGTTOU: 该信号类似于SIGTTIN,在后台进程要向终端输出数据时发生。默认动作为暂停进程。

23) SIGURG:套接字上有紧急数据时,向当前正在运行的进程发出些信号,报告有紧急数据到达。如网络带外数据到达,默认动作为忽略该信号。

24) SIGXCPU:进程执行时间超过了分配给该进程的CPU时间 ,系统产生该信号并发送给该进程。默认动作为终止进程。

25) SIGXFSZ:超过文件的最大长度设置。默认动作为终止进程。

26) SIGVTALRM:虚拟时钟超时时产生该信号。类似于SIGALRM,但是该信号只计算该进程占用CPU的使用时间。默认动作为终止进程。

27) SGIPROF:类似于SIGVTALRM,它不公包括该进程占用CPU时间还包括执行系统调用时间。默认动作为终止进程。

28) SIGWINCH:窗口变化大小时发出。默认动作为忽略该信号。

29) SIGIO:此信号向进程指示发出了一个异步IO事件。默认动作为忽略。

30) SIGPWR:关机。默认动作为终止进程。

31) SIGSYS:无效的系统调用。默认动作为终止进程并产生core文件。

34) SIGRTMIN ~ (64) SIGRTMAX:LINUX的实时信号,它们没有固定的含义(可以由用户自定义)。所有的实时信号的默认动作都为终止进程。

这里特别强调了9) SIGKILL 和19) SIGSTOP信号,不允许忽略和捕捉,只能执行默认动作。甚至不能将其设置为阻塞。

14、向其他进程发送信号

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>

int main()
{
    /*
        The  kill()  system  call can be used to send any signal to any process
        group or process.
        If pid is positive, then signal sig is sent to the process with the  ID
        specified by pid.
        If pid equals 0, then sig is sent to every process in the process group
        of the calling process.
        If pid equals -1, then sig is sent to every process for which the call‐
        ing  process  has  permission  to  send  signals,  except for process 1
        (init), but see below.
        If pid is less than -1, then sig  is  sent  to  every  process  in  the
        process group whose ID is -pid.
    */
    kill(1, SIGKILL);
    if (-1 == kill(1, SIGKILL)) perror("kill failed");
    return 0;
}

15、raise和abort函数

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>

int main()
{
    /*
        The  raise()  function sends a signal to the calling process or thread.
        In a single-threaded program it is equivalent to kill(getpid(), sig);
    */
    raise(SIGKILL);
    //  abort - cause abnormal process termination
    abort();
    return 0;
}

16、alarm函数

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>

int main()
{
    /*
        alarm()  arranges  for  a SIGALRM signal to be delivered to the calling
        process in seconds seconds.
        alarm() returns the number of seconds remaining  until  any  previously
        scheduled alarm was due to be delivered, or zero if there was no previ‐
        ously scheduled alarm.
    */
    alarm(5);
    int remain = alarm(1);
    // time ./a.out
    //  剩余时间为IO资源等待
    //  real    0m1.140s    实际消耗时间
    //  user    0m0.020s    用户空间消耗时间
    //  sys     0m0.171s    内核空间消耗时间

    for (int i = 0; ; ++i) printf("%d\n", i);
    return 0;
}

17、setitimer函数

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/time.h>
int main()
{
    /*
        int setitimer(int which, const struct itimerval *new_value,
                    struct itimerval *old_value);
    */
    /*
        参数:which:指定定时方式
            ① 自然定时:ITIMER_REAL → 14)SIGLARM                     
            ② 虚拟空间计时(用户空间):ITIMER_VIRTUAL → 26)SIGVTALRM     
            ③ 运行时计时(用户+内核):ITIMER_PROF → 27)SIGPROF      

    */
    // 捕捉信号
    signal(SIGALRM, [](int sig) {printf("Catch Signal pid : %d.\n",getpid()); });
    struct itimerval it;
    it.it_value.tv_sec = 1;
    it.it_value.tv_usec = 200;
    it.it_interval.tv_sec = 5;
    it.it_interval.tv_usec = 0;
    setitimer(ITIMER_REAL, &it, nullptr);
    printf("Main Process pid : %d.\n", getpid());
    for (int i = 0; ; ++i) {
        sleep(3);
        printf("%d\n", i);
    }
    return 0;
}

18、信号集操作

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/time.h>
int main()
{

    /*
        sigset_t  set;      // typedef unsigned long sigset_t; 
        将某个信号集清0            成功:0;失败:-1
        int sigemptyset(sigset_t *set);         
        将某个信号集置1            成功:0;失败:-1
        int sigfillset(sigset_t *set);          
        将某个信号加入信号集          成功:0;失败:-1
        int sigaddset(sigset_t *set, int signum);
        将某个信号清出信号集          成功:0;失败:-1
        int sigdelset(sigset_t *set, int signum);       
        判断某个信号是否在信号集中   返回值:在集合:1;不在:0;出错:-1
        int sigismember(const sigset_t *set, int signum);
    */
    sigset_t  sigset, sigset_ped;
    sigemptyset(&sigset);
    sigaddset(&sigset, SIGALRM);
    //  1.SIG_BLOCK:    set表示需要屏蔽的信号。相当于 mask = mask | set
    //  2.SIG_UNBLOCK : set表示需要解除屏蔽的信号。相当于 mask = mask & ~set
    //  3.SIG_SETMASK : set表示用于替代原始屏蔽及的新屏蔽集。相当于 mask = set
    //  若,调用sigprocmask解除了对当前若干个信号的阻塞,则在sigprocmask返回前,
    //  至少将其中一个信号递达。

    //  用来屏蔽信号、解除屏蔽
    sigprocmask(SIG_BLOCK, &sigset, nullptr);
    int remain = alarm(1);
    sleep(2);
    //sigprocmask(SIG_UNBLOCK, &sigset, nullptr);
    // 获取未决信号集
    sigpending(&sigset_ped);

    for (int i = 1; i <= 32; ++i) {
        if (sigismember(&sigset_ped, i))printf("1");
        else printf("0");
    }
    printf("\n");
    return 0;
}

19、使用signal函数捕捉信号

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/time.h>
int main()
{
    // 捕捉信号
    auto handler = signal(SIGALRM, [](int sig) {printf("Catch Signal pid : %d.\n", getpid()); });
    if (handler == SIG_ERR);
    struct itimerval it;
    it.it_value.tv_sec = 1;
    it.it_value.tv_usec = 200;
    it.it_interval.tv_sec = 5;
    it.it_interval.tv_usec = 0;
    setitimer(ITIMER_REAL, &it, nullptr);
    printf("Main Process pid : %d.\n", getpid());
    for (int i = 0; ; ++i) {
        sleep(3);
        printf("%d\n", i);
    }
    return 0;
}

20、使用sigaction函数捕捉信号

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/time.h>
int main()
{
    auto fn = [](int sig) {printf("Catch Signal pid : %d.\n", getpid()); sleep(5); };
    struct sigaction act;
    act.sa_handler = fn;
    sigemptyset(&act.sa_mask);
    // 执行信号捕捉函数期间的信号屏蔽字
    sigaddset(&act.sa_mask, SIGINT);
    // 执行信号捕捉函数期间同一信号再次到来处理方式,0表示忽略
    act.sa_flags = 0;
    sigaction(SIGALRM, &act, nullptr);
    struct itimerval it;
    it.it_value.tv_sec = 1;
    it.it_value.tv_usec = 200;
    it.it_interval.tv_sec = 5;
    it.it_interval.tv_usec = 0;
    setitimer(ITIMER_REAL, &it, nullptr);
    printf("Main Process pid : %d.\n", getpid());
    for (int i = 0; ; ++i) {
        sleep(3);
        printf("%d\n", i);
    }
    return 0;
}

21、信号捕捉特性

  1. 进程正常运行时,默认PCB中有一个信号屏蔽字,假定为☆,它决定了进程自动屏蔽哪些信号。当注册了某个信号捕捉函数,捕捉到该信号以后,要调用该函数。而该函数有可能执行很长时间,在这期间所屏蔽的信号不由信号来指定。而是用sa_mask来指定。调用完信号处理函数,再恢复为☆。
  2. XXX信号捕捉函数执行期间,XXX信号自动被屏蔽。
  3. 阻塞的常规信号不支持排队,产生多次只记录一次。(后32个实时信号支持排队)

22、内核信号捕捉原理

23、pause函数

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/time.h>
int main()
{
    /*
        pause()  causes the calling process (or thread) to sleep until a signal
        is delivered that either terminates the process or causes  the  invoca‐
        tion of a signal-catching function.
        pause()  returns  only when a signal was caught and the signal-catching
        function returned.  In this case, pause() returns -1, and errno is  set
        to EINTR.
    */
    // 模拟sleep函数
    signal(SIGALRM, [](int signal_code) {});
    alarm(2);
    pause();
    printf("after sleep 2s\n");
    return 0;
}

24、时序竞态分析

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/time.h>
int main()
{
    signal(SIGALRM, [](int signal_code) {});
    alarm(2);//这里失去2s+CPU会导致问题
    pause();
    printf("after sleep 2s\n");
    return 0;
}

25、使用sigsuspend函数解决时序竞态

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/time.h>
int main()
{
    /*
        sigsuspend()  temporarily  replaces  the  signal  mask  of  the calling
        process with the mask given by mask and then suspends the process until
        delivery  of  a signal whose action is to invoke a signal handler or to
        terminate a process.
    */
    sigset_t mask,old_set,suspend_set;
    sigemptyset(&mask);
    sigaddset(&mask, SIGALRM);
    signal(SIGALRM, [](int signal_code) {});
    // 保存
    sigprocmask(SIG_BLOCK, &mask, &old_set);
    alarm(2); 
    suspend_set = old_set;
    sigdelset(&suspend_set, SIGALRM);   // 这里为了保险
    sigsuspend(&suspend_set);           // 使用指定的屏蔽字
    // 恢复
    sigprocmask(SIG_BLOCK, &old_set, nullptr);
    printf("after sleep 2s\n");
    return 0;
}

26、全局变量异步IO

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/time.h>
// 程序功能,两个进程交替数数,通过信号同步
int n ,flag = 0;
void sigcat(int signo) 
{
    printf("pid %d , %d\n", getpid(),n+=2); 
    flag = 1;//当前状态可以发送信号
}
void run(pid_t pd) 
{
    while (true) 
    {
        if (flag == 1) {
            kill(pd, SIGALRM);
            flag = 0;// 信号发送完毕
        }
    }
}
int main()
{
    pid_t pd = fork();

    if (pd == 0) { // sub process
        n = -1; flag = 0;
        signal(SIGALRM,sigcat);
        run(getppid());
    } else {
        n = 0; flag = 1;
        signal(SIGALRM, sigcat);
        // 等待子进程绑定好信号处理函数
        sleep(1);
        run(pd);
    }
    return 0;
}

运行上面程序可以发现程序会死锁

示例中,通过flag变量标记程序实行进度。flag置1表示数数完成。flag置0表示给对方发送信号完成。

问题出现的位置,在父子进程kill函数之后需要紧接着调用 flag,将其置0,标记信号已经发送。但在这期间很有可能被kernel调度,失去执行权利,而对方获取了执行时间,通过发送信号回调捕捉函数,从而修改了全局的flag。

如何解决该问题呢?可以使用“锁”机制。当操作全局变量的时候,通过加锁、解锁来解决该问题。也可以避免使用全局变量。

这里提供一种解决方案:避免使用全局变量。

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/time.h>
// 程序功能,两个进程交替数数,通过信号同步
int n ,flag = 0;
pid_t pd;
void sigcat(int signo) 
{
    printf("pid %d , %d\n", getpid(),n+=2); 
    if(flag == 1)kill(pd, SIGALRM);
    else kill(getppid(), SIGALRM);
}
int main()
{
    pd = fork();
    if (pd == 0) { 
        n = -1; flag = 0;
        signal(SIGALRM,sigcat);
        while (true);// 类似消息循环
    } else {
        n = 0; flag = 1;
        signal(SIGALRM, sigcat);
        sleep(1);
        kill(pd, SIGALRM);
        while (true);
    }
    return 0;
}

27、可重入函数与不可重入函数

可重入函数主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;而不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。信号处理函数应该写成可重入函数

  1. 定义可重入函数,函数内不能含有全局变量及static变量,不能使用malloc、free
  2. 信号捕捉函数应设计为可重入函数
  3. 信号处理程序可以调用的可重入函数可参阅man 7 signal
  4. 没有包含在上述列表中的函数大多是不可重入的,其原因为:
    1. 使用静态数据结构
    2. 调用了malloc或free
    3. 是标准I/O函数

28、使用SIGCHLD信号回收子进程

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/time.h>

int main()
{
    // 若在循环内注册 一定要先阻塞SIGCHLD,避免没有注册完成时
    // 子进程死亡,导致没有捕捉到信号
    signal(SIGCHLD, [](int signo) {
        int status;
        sigset_t sigset_ped;
        sigpending(&sigset_ped);
        //信号一旦抵达 未决信号直接置0 该处理函数会重入
        printf("SIGCHLD %d\n", sigismember(&sigset_ped, SIGCHLD));
        // 如果使用if将可能出现僵尸进程 使用ps aux命令查看
        while (waitpid(-1, &status, WNOHANG) > 0) {
            if (WIFEXITED(status)) {
                printf("RETURN VALUE: %d\n", WEXITSTATUS(status));
            }
        }
    });
    for (int i = 0; i < 10; ++i) {
        pid_t pd = fork();
        if (pd == 0) { sleep(1); return i; }
    }
    while (true);
    return 0;
}

29、信号传参

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/time.h>

int main()
{
    struct sigaction act;
    sigemptyset(&act.sa_mask);
    act.sa_flags = SA_SIGINFO;
    act.sa_sigaction = [](int signo, siginfo_t* info, void* args) {
        printf("passed value %d\n",info->si_int);
    };
    sigaction(SIGALRM, &act, nullptr);
    sigval_t val;
    val.sival_int = 5;
    sigqueue(getpid(), SIGALRM, val);
    while (true);
    return 0;
}

30、系统调用

系统调用可分为两类:慢速系统调用和其他系统调用。

  1. 慢速系统调用:可能会使进程永远阻塞的一类。如果在阻塞期间收到一个信号,该系统调用就被中断,不再继续执行(早期);也可以设定系统调用是否重启。如,read、write、pause、wait...

  2. 其他系统调用:getpid、getppid、fork...

可修改sa_flags参数来设置被信号中断后系统调用是否重启。SA_INTERRURT不重启。 SA_RESTART重启。

sa_flags还有很多可选参数,适用于不同情况。如:捕捉到信号后,在执行捕捉函数期间,不希望自动阻塞该信号,可将sa_flags设置为SA_NODEFER,除非sa_mask中包含该信号。

31、进程组概念

进程组,也称之为作业。BSD于1980年前后向Unix中增加的一个新特性。代表一个或多个进程的集合。每个进程都属于一个进程组。在waitpid函数和kill函数的参数中都曾使用到。操作系统设计的进程组的概念,是为了简化对多个进程的管理。

当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。进程组ID==第一个进程ID(组长进程)。所以,组长进程标识:其进程组ID==其进程ID

可以使用kill -SIGKILL -进程组ID(负的)来将整个进程组内的进程全部杀死。

组长进程可以创建一个进程组,创建该进程组中的进程,然后终止。只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关。

进程组生存期:进程组创建到最后一个进程离开(终止或转移到另一个进程组)。

一个进程可以为自己或子进程设置进程组ID

32、进程组操作函数

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

int main(void)
{
    pid_t pid;

    if ((pid = fork()) < 0) {
        perror("fork");
        exit(1);
    } else if (pid == 0) {
        printf("child PID == %d\n",getpid());
        printf("child Group ID == %d\n",getpgid(0)); // 返回组id
        //printf("child Group ID == %d\n",getpgrp()); // 返回组id
        sleep(7);
        printf("----Group ID of child is changed to %d\n",getpgid(0));
        exit(0);

    } else if (pid > 0) {
        sleep(1);
        setpgid(pid,pid);           //让子进程自立门户,成为进程组组长,以它的pid为进程组id

        sleep(13);
        printf("\n");
        printf("parent PID == %d\n", getpid());
        printf("parent's parent process PID == %d\n", getppid());
        printf("parent Group ID == %d\n", getpgid(0));

        sleep(5);
        setpgid(getpid(),getppid()); // 改变父进程的组id为父进程的父进程
        printf("\n----Group ID of parent is changed to %d\n",getpgid(0));

        while(1);
    }

    return 0;
}

33、创建会话

创建一个会话需要注意以下6点注意事项:

  1. 调用进程不能是进程组组长,该进程变成新会话首进程(session header)
  2. 该进程成为一个新进程组的组长进程。
  3. 需有root权限(ubuntu不需要)
  4. 新会话丢弃原有的控制终端,该会话没有控制终端
  5. 该调用进程是组长进程,则出错返回
  6. 建立新会话时,先调用fork, 父进程终止,子进程调用setsid
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    pid_t pid;

    if ((pid = fork())<0) {
        perror("fork");
        exit(1);

    } else if (pid == 0) {

        printf("child process PID is %d\n", getpid());
        printf("Group ID of child is %d\n", getpgid(0));
        printf("Session ID of child is %d\n", getsid(0));

        sleep(10);
        // 创建一个会话,并以自己的ID设置进程组ID,同时也是新会话的ID
        //子进程非组长进程,故其成为新会话首进程,且成为组长进程。该进程组id即为会话进程
        setsid();       

        printf("Changed:\n");

        printf("child process PID is %d\n", getpid());
        printf("Group ID of child is %d\n", getpgid(0));
        //获取进程所属的会话ID
        printf("Session ID of child is %d\n", getsid(0));

        sleep(20);

        exit(0);
    }

    return 0;
}

34、守护进程

创建守护进程模型

  1. 创建子进程,父进程退出
    1. 所有工作在子进程中进行形式上脱离了控制终端
  2. 在子进程中创建新会话
    1. setsid()函数
    2. 使子进程完全独立出来,脱离控制(主要是脱离终端)
  3. 改变当前目录为根目录
    1. chdir()函数
    2. 防止占用可卸载的文件系统
    3. 也可以换成其它路径
  4. 重设文件权限掩码
    1. umask()函数
    2. 防止继承的文件创建屏蔽字拒绝某些权限
    3. 增加守护进程灵活性
  5. 关闭文件描述符
    1. 继承的打开文件不会用到,浪费系统资源,无法卸载
  6. 开始执行守护进程核心工作

创建守护进程代码

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
// 登录注销不会产生影响
int main()
{
    pid_t pid = fork(), sid;
    if (pid == 0) {
        sid = setsid(); // 脱离控制
        chdir("/");       // 改变目录
        umask(0002);    // 改变掩码
        // 关闭文件描述符
        close(STDIN_FILENO);
        open("/dev/null",O_RDWR);
        dup2(STDIN_FILENO, STDOUT_FILENO);
        dup2(STDIN_FILENO, STDERR_FILENO);
        while (1);// dosomething
    }
    return 0;
}

35、查看进程里的线程

 ps -Lf 42616
 UID         PID   PPID    LWP  C NLWP STIME TTY      STAT   TIME CMD
pouee     42616      1  42616 11   63 08:46 tty2     Rl+    0:05 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42622  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42623  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42624  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42625  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42626  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42627  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42628  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42629  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42630  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42631  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42632  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42633  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42634  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42635  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42636  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42638  0   63 08:46 tty2     SNl+   0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42641  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42642  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42644  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42645  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42646  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42647  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42648  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42649  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42650  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42651  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42655  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42658  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42659  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42660  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42661  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42662  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42663  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42664  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42665  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42666  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42667  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42668  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42672  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42673  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42675  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42676  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42677  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42679  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42695  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42696  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42697  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42707  0   63 08:46 tty2     SNl+   0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42708  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42709  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42711  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42715  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42716  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42717  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42718  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42719  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42720  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42721  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42723  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42738  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42760  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window
pouee     42616      1  42764  0   63 08:46 tty2     Sl+    0:00 /usr/lib/firefox/firefox -new-window

Linux内核通过LWP分配时间轮片。

36、线程的共享资源

  1. 文件描述符表
  2. 每种信号的处理方式
  3. 当前工作目录
  4. 用户ID和组ID
  5. 内存地址空间 (.text/.data/.bss/heap/共享库)

37、线程的独享资源

  1. 线程id
  2. 处理器现场和栈指针(内核栈)
  3. 独立的栈空间(用户空间栈)
  4. errno变量
  5. 信号屏蔽字
  6. 调度优先级

38、创建线程

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
int main()
{
    /* pthread_self - obtain ID of the calling thread */
    printf("Thread ID: %u PID: %d\n", pthread_self(), getpid());
    pthread_t tid;
    /* pthread_create - create a new thread */
    int ret = pthread_create(&tid, nullptr, [](void* args)->void* {
        printf("Thread ID: %u PID: %d\n", pthread_self(), getpid());
        return nullptr;
    },nullptr);
    if (ret != 0) {
        // return string describing error number
        fprintf(stderr,"%s",strerror(ret));
        return -1;
    }
    sleep(1);
    return 0;
}

39、创建线程时传递参数

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, [](void* args)->void* {
        printf("ARGS %d\n", args);
        return nullptr;
    },(void *)77);
    sleep(1);
    return 0;
}

40、使用pthread_exit结束线程

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, [](void* args)->void* {
        sleep(1);
        // 这行代码会执行
        printf("ARGS %d\n", args);
        return nullptr;
    },(void *)77);
    // terminate calling thread
    pthread_exit((void*)0);
    return 0;
}

41、使用pthread_join阻塞等待线程退出

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
int main()
{
    pthread_t tid;
    int* ret;
    pthread_create(&tid, nullptr, [](void* args)->void* {
        sleep(1);
        int* p = new int;
        *p = 100;
        printf("ARGS %d\n", args);
        return p;
    },(void *)77);
    // 阻塞等待线程执行完毕 线程之间也可以互相等待
    pthread_join(tid, (void **)&ret);
    printf("return value: %d\n",*ret);
    delete ret;
    return 0;
}

42、使用pthread_detach分离线程

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
int main()
{
    pthread_t tid;
    /*
        创建一个线程默认的状态是joinable, 如果一个线程结束运行
        但没有被join,则它的状态类似于进程中的Zombie Process,
        即还有一部分资源没有被回收(退出状态码),所以创建线程者
        应该调用pthread_join来等待线程运行结束,并可得到线程的
        退出代码,回收其资源(类似于wait,waitpid)
        但是调用pthread_join(pthread_id)后,如果该线程没有运
        行结束,调用者会被阻塞,在有些情况下我们并不希望如此,比
        如在Web服务器中当主线程为每个新来的链接创建一个子线程进
        行处理的时候,主线程并不希望因为调用pthread_join而阻塞
        (因为还要继续处理之后到来的链接),这时可以在子线程中加
        入代码 pthread_detach(pthread_self())或者父线程调用
        pthread_detach(thread_id)(非阻塞,可立即返回)这将
        该子线程的状态设置为detached,则该线程运行结束后会自动释
        放所有资源。
    */
    pthread_create(&tid, nullptr, [](void* args)->void* {
        sleep(1);
        int* p = new int;
        *p = 100;
        printf("ARGS %d\n", args);
        return p;
    },(void *)77);
    // 阻塞等待线程执行完毕
    pthread_detach(tid);
    return 0;
}

43、使用pthread_cancel杀死线程

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, [](void* args)->void* {
        while (true) {
            // sleep(1);
            // printf("ARGS %d\n", args);
            // 设置取消点
            pthread_testcancel();
        }
        return nullptr;
    },(void *)77);
    // 阻塞等待线程执行完毕
    sleep(2);
    /*
        The  pthread_cancel()  function  sends  a  cancellation  request to the
        thread thread.  Whether and when the target thread reacts to  the  can‐
        cellation  request depends on two attributes that are under the control
        of that thread: its cancelability state and type.
    */
    // 异步发
    int ret = pthread_cancel(tid);
    printf("cancel called! %d\n",ret);
    pthread_join(tid,nullptr);
    return 0;
}

44、在线程创建的时候设置线程属性

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
int main()
{
    pthread_t tid;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    // 属性分离属性
    // 这里要注意的一点是,如果设置一个线程为分离线程,
    // 而这个线程运行又非常快,它很可能在pthread_create
    // 函数返回之前就终止了,它终止以后就可能将线程号和系统
    // 资源移交给其他的线程使用,这样调用pthread_create
    // 的线程就得到了错误的线程号。
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    // 
    // pthread_attr_setstack();
    pthread_create(&tid, &attr, [](void* args)->void* {

        return nullptr;
    },(void *)77);
    return 0;
}

45、使用互斥量保护共享资源

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
pthread_mutex_t mutex;
void* fun(void *args) 
{
    while (true) {
        sleep(1);
        pthread_mutex_lock(&mutex);
        printf("HELLO");
        sleep(1);
        printf(" WORLD\n");
        pthread_mutex_unlock(&mutex);
    }
    return nullptr;
}
int main()
{
    pthread_t tid;
    pthread_mutex_init(&mutex, nullptr);
    pthread_create(&tid, nullptr, fun, nullptr);
    while (true) {
        sleep(1);
        pthread_mutex_lock(&mutex);
        printf("hello");
        sleep(1);
        printf(" world\n");
        pthread_mutex_unlock(&mutex);
    }

    pthread_mutex_destroy(&mutex);
    return 0;
}

46、使用读写锁

/* 3个线程不定时 "写" 全局资源,5个线程不定时 "读" 同一全局资源 */
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

int counter;                          //全局资源
pthread_rwlock_t rwlock;
void* th_write(void* arg)
{
    int t;
    int i = (int)arg;

    while (1) {
        t = counter;
        usleep(1000);
        pthread_rwlock_wrlock(&rwlock);
        printf("=======write %d: %lu: counter=%d "
            "++counter=%d\n", i, pthread_self(), t, ++counter);
        pthread_rwlock_unlock(&rwlock);
        usleep(5000);
    }
    return NULL;
}

void* th_read(void* arg)
{
    int i = (int)arg;
    while (1) {
        pthread_rwlock_rdlock(&rwlock);
        printf("----------------------------"
            "read %d: %lu: %d\n", i, pthread_self(), counter);
        pthread_rwlock_unlock(&rwlock);
        usleep(900);
    }
    return NULL;
}

int main(void)
{
    int i;
    pthread_t tid[8];
    pthread_rwlock_init(&rwlock, NULL);
    for (i = 0; i < 3; i++)
        pthread_create(&tid[i], NULL, 
            th_write, (void*)i);
    for (i = 0; i < 5; i++)
        pthread_create(&tid[i + 3], NULL,
            th_read, (void*)i);
    for (i = 0; i < 8; i++)
        pthread_join(tid[i], NULL);
    pthread_rwlock_destroy(&rwlock);            //释放读写琐

    return 0;
}

47、使用条件变量实现生产者消费者

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
int resource = 0 ;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
void* produce(void *args) 
{
    while (true) {
        pthread_mutex_lock(&mutex);
        while (resource >= 5) pthread_cond_wait(&has_product, &mutex);
        ++resource;
        printf("++resource remain: %d\n", resource);
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&has_product);
        sleep(rand()%3);
    }
    return nullptr;
}
void* consume(void *args)
{
    while (true) {
        pthread_mutex_lock(&mutex);
        while (resource == 0) pthread_cond_wait(&has_product, &mutex);
        --resource;
        printf("--resource remain: %d\n",resource);
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&has_product);
        // pthread_cond_broadcast(&has_product);
        sleep(rand() % 5);
    }
    return nullptr;
}
int main(void)
{
    pthread_t tid;
    pthread_create(&tid, nullptr, produce, nullptr);
    pthread_create(&tid, nullptr, consume, nullptr);
    //pthread_create(&tid, nullptr, consume, nullptr);
    pthread_join(tid, nullptr);

    return 0;
}

48、绝对时间

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

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
int main(void)
{
    struct timespec ts;
    ts.tv_sec = time(nullptr) + 10;
    pthread_cond_timedwait(&has_product, &mutex, &ts);
    return 0;
}

49、使用信号量实现生产者消费者

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
sem_t sem_a, sem_b;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void* produce(void* args)
{
    int cur;
    while (true) {
        sem_wait(&sem_a);
        sem_post(&sem_b);
        sem_getvalue(&sem_b, &cur);
        printf("++resource remain: %d\n", cur);
        sleep(1);
    }
    return nullptr;
}
void* consume(void* args)
{
    int cur;
    while (true) {
        sem_wait(&sem_b);
        sem_post(&sem_a);
        sem_getvalue(&sem_b, &cur);
        printf("--resource remain: %d\n", cur);
        sleep(rand() % 7);
    }
    return nullptr;
}
int main(void)
{
    pthread_t tid;
    sem_init(&sem_a, 0, 5);
    sem_init(&sem_b, 0, 0);
    pthread_create(&tid, nullptr, produce, nullptr);
    pthread_create(&tid, nullptr, consume, nullptr);
    pthread_create(&tid, nullptr, consume, nullptr);
    pthread_join(tid, nullptr);

    return 0;
}

50、使用文件锁

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

void sys_err(char *str)
{
    perror(str);
    exit(1);
}

int main(int argc, char *argv[])
{
    int fd;
    struct flock f_lock;

    if (argc < 2) {
        printf("./a.out filename\n");
        exit(1);
    }

    if ((fd = open(argv[1], O_RDWR)) < 0)
        sys_err("open");

    f_lock.l_type = F_WRLCK;        /*选用写琐*/
//    f_lock.l_type = F_RDLCK;      /*选用读琐*/ 

    f_lock.l_whence = SEEK_SET;
    f_lock.l_start = 0;
    f_lock.l_len = 0;               /* 0表示整个文件加锁 */

    fcntl(fd, F_SETLKW, &f_lock);
    printf("get flock\n");

    sleep(10);

    f_lock.l_type = F_UNLCK;
    fcntl(fd, F_SETLKW, &f_lock);
    printf("un flock\n");

    close(fd);

    return 0;
}