[email protected] 文章目录 进程/线程相关 3 线程相关 5 background information background information 7 pthread create pthread create 30 pthread join pthread join 96 pthre...">

当前位置:网站首页>Sandbox中的进程/线程相关-1

Sandbox中的进程/线程相关-1

2022-08-09 12:45:00 养猪去

[email protected]

进程/线程相关

线程相关

background information

线程和进程的理论概念不再赘述。
Linux 中,系统是不认识线程还是进程的,它只认识 task。

下面的阐述都是 Unix like 下的有关线程的语义。

主线程和子线程

  • 共享: 用户区内,除了栈区是不共享的,其余都是共享的。
  • 不共享: 栈区(当有 1 主 + 4 子线程时候,栈区会被平分为 5 份)

多进程共享的资源(fork、clone出的子进程和父进程):

  • 代码

  • 文件描述符

  • 内存映射区 –mmap
    多线程共享的资源:

  • 全局变量

  • 线程号和线程 ID 是有区别的
    查看方式: 找到程序的进程 ID后, ps -Lf $(pid),LWP那一列即为线程ID。

pthread_create

  • 头文件:
#include<pthread.h>

pthread非linux系统的默认库, 需手动链接-线程库 -lpthread

  • 函数说明:
    返回成功时,由 tidp 指向的内存单元被设置为新创建线程的线程ID。a
    ttr参数用于指定各种不同的线程属性。
    新创建的线程从start_rtn函数的地址开始运行,该函数只有一个万能指针参数arg。
    如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构体中,然后把这个结构的地址作为arg的参数传入。

  • 函数定义

int pthread_create(pthread_t *tidp,const pthread_attr_t *attr, void *(*start_rtn)(void*),void *arg);
  • 参数说明:
    第一个参数为指向线程标识符的指针。
    第二个参数用来设置线程属性。
    第三个参数是线程运行函数的起始地址。
    最后一个参数是运行函数的参数。

  • 返回值
    若线程创建成功,则返回0。若线程创建失败,则返回出错编号,并且*thread中的内容是未定义的。

  • 代码

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

int num = 13; //设置为全局变量,在全局区域,共享

void* myfun(void* arg);

int main(int argc, char *argv[])
{
    
    void* p = (void *)&num;  //传一个地址进去(voi* 也是 4 个字节)
    pthread_t id[5] = {
    0};
    for (int i = 0; i < 5; i++) {
    
        pthread_create(&(id[i]), NULL, myfun, p);
        printf("i = %d, thread id: %ld\n", i, id[i]);
    }
    
    return 0;
}

void* myfun(void* arg)
{
    
    printf("num = %d, child thread id: %ld\n", (*((int *)arg))++, pthread_self());
    return NULL;
}

pthread_join

pthread_join()即是子线程合入主线程,主线程阻塞等待子线程结束,然后回收子线程资源。

注意,默认情况下,资源是不会随着子线程的exit或return而回收的。

  • 头文件:
#include <pthread.h>
  • 函数说明:
    thread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。
    如果线程已经结束,那么该函数会立即返回。并且thread指定的线程必须是 joinable 的。
  • 函数定义
int pthread_join(pthread_t thread, void **retval);

thread: 线程标识符,即线程ID,标识唯一线程。
retval: 用户定义的指针,用来存储被等待线程的返回值。

  • 返回值
    0代表成功。 失败,返回的则是错误号。

  • 示例代码:

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

void *thread_function(void *arg) {
    
    int i;
    for (i = 0; i < 8; i++) {
    
        printf("Thread working... %d \n", i);
        sleep(1);
    }
    return NULL;
}

int main(void) {
    
    pthread_t mythread;

    if (pthread_create(&mythread, NULL, thread_function, NULL)) {
    
        printf("error creating thread.");
        abort();
    }
    if (pthread_join(mythread, NULL)) {
    
        printf("error join thread.");
        abort();
    }

    printf("thread done! \n");
    return 0;
}
/* 输出 Thread working...! 0 Thread working...! 1 Thread working...! 2 Thread working...! 3 Thread working...! 4 Thread working...! 5 Thread working...! 6 Thread working...! 7 thread done! */

如果去掉pthread_join的调用的话,
输出为:

thread done! 
Thread working... 0 
Process finished with exit code 0

也就是说,子线程来不及执行它的函数,就因为父线程的死亡而被迫终结了。

pthread_detach

  • 头文件:
#include <pthread.h>
  • 函数说明:
    pthread_join()函数的替代函数,可回收创建时detachstate属性设置为PTHREAD_CREATE_JOINABLE的线程的存储空间。
    该函数不会阻塞父线程。
    pthread_join()函数用于只是应用程序在线程tid终止时回收其存储空间。

  • 函数定义

int pthread_detach(pthread_t tid);
  • 返回值
    thread_detach() 在调用成功完成之后返回零。其他任何返回值都表示出现了错误。

注意,即使如此,父线程退出时,子线程仍然会强制退出。

pthread_cancel

  • 函数说明:
    发送终止信号给thread线程,如果成功则返回0,否则为非0值。发送成功并不意味着thread会终止。

  • 函数定义:

int pthread_cancel(pthread_t thread)
  • 示例代码:
/** * @Author: 吉松阳 * @Date: 2021/9/26 * @Description: */

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

void print_message_function(void *ptr);

int main() {
    
    pthread_t thread1;
    pthread_create(&thread1, NULL, (void *) &print_message_function, (void *) 0);
    sleep(3);
    printf("main thread\n");
    pthread_cancel(thread1);
    sleep(7);
    exit(0);
}

void print_message_function(void *ptr) {
    
    pthread_detach(pthread_self());
    sleep(6);
    printf("child thread\n");
    pthread_exit(0);
}
// 实验证明 pthread_exit 确实起作用了

信号处理相关

raise

  • 头文件:
#include <signal.h>
  • 函数说明:
    C 库函数, 会促使生成信号 sig。sig 参数与 SIG 宏兼容。

  • 函数定义

// sig -- 要发送的信号码。
int raise(int sig)

查看所有信号:使用 kill -l

[email protected]:~$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2

值得注意的是,
当一个进程调用fork时,因为子进程在开始时复制父进程的存储映像,信号捕捉函数的地址在子进程中是有意义的,所以子进程继承父进程的信号处理方式。
但是当子进程调用exec后,因为exec运行新的程序后会覆盖从父进程继承来的存储映像。
那么信号捕捉函数在新程序中已无意义,所以exec会将原先设置为要捕捉的信号都更改为默认动作。

  • 返回值
    如果成功该函数返回零,否则返回非零。

signal

  • 头文件:
#include <signal.h>
  • 函数说明:
    C 库函数,设置一个函数来处理信号,即带有 sig 参数的信号处理程序。

  • 函数定义

void (*signal(int sig, void (*func)(int)))(int)

参数说明:

  • sig – 在信号处理程序中作为变量使用的信号码。下面是一些重要的标准信号常量

  • func – 一个指向函数的指针。它可以是一个由程序定义的函数,也可以是下面预定义函数之一。

    • SIG_DFL 默认的信号处理程序。
    • SIG_IGN 忽视信号。
  • 返回值
    该函数返回之前的信号处理程序

  • 实例代码

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

void signal_catchfunc(int);

int main() {
    
    int ret;
    signal(SIGINT, signal_catchfunc);
    printf("开始生成一个信号\n");
    ret = raise(SIGINT);
    if (ret != 0) {
    
        printf("错误,不能生成SIGINT信号\n");
        exit(0);
    }
    printf("退出....\n");
    return 0;
}

void signal_catchfunc(int signal) {
    
    printf("捕获信号\n");
}

sandbox使用的信号

信号的共达60余个,这里只介绍一下sandbox中使用的信号。

SIGUSR1/SIGUSR2

SIGUSR1 用户自定义信号 默认处理:进程终止;
SIGUSR2 用户自定义信号默认处理:进程终止。

SIGSEGV

在POSIX兼容的平台上,SIGSEGV是当一个进程执行了一个无效的内存引用,或发生段错误时发送给它的信号。
SIGSEGV的符号常量在头文件signal.h中定义。
因为在不同平台上,信号数字可能变化,因此最好使用符号信号名。通常,它是信号#11。
SIGSEGV维基百科

原网站

版权声明
本文为[养猪去]所创,转载请带上原文链接,感谢
https://song-yang-ji.blog.csdn.net/article/details/121443272