Linux线程间的同步和互斥 进程间传统通信方式 5.16

news2024/11/26 16:31:24

Linux线程间的同步和互斥

同步:有顺序的考虑

按照约定的顺序相互配合完成一件事情(红绿灯)

{释放 产生 资源+1(V操作);;申请 资源-1(p操作)}

信号量代表某一类资源;;

异步:你做你的我干我的

同步

1.先全局定义信号量sem_t sem_id;
2.在main函数中初始化信号量
3.p/v操作

1.线程间的通信方式------全局变量

2.线程间通信时,必须 使用同步和互斥机制

	1、同步通信:多个任务,按照约定的顺序共同完成

​	2、posix中的同步机制:

​			无名信号量(基于内存的信号量)

​			有名信号量

​	3、信号量:

​			系统中资源的数量。

​	4、信号量操作方式,必须通过以下函数


sem_init

​ a)int sem_init(sem_t sem,int pshared,unsigned int value);

​ 功能:

​ 初始化信号量,指定信号量的使用范围和信号量的初始值。

​ 头文件:

​ #include <semaphore.h>

​ 参数:

​ sem:信号量首地址

​ pshared:信号量的使用范围(0:线程间 非0:进程间)

​ value:信号量的初始值

​ 返回值:

​ 成功:0

​ 失败:-1;

sem_wait

​ b) int sem_wait(sem_t *sem);

功能:

​ p操作,申请资源 。(如果有资源,资源-1;若没有则阻塞等待)

参数:

​ sem:信号量首地址

返回值:

​ 成功:0

​ 失败:-1

sem_post

​ c)int sem_post(sem_t *sem);

功能:

​ V操作,释放资源,(如果有任务等待该资源,则唤醒任务,若没有任务,则资源+1)

参数:

​ sem:信号量首地址

返回值:

​ 成功:0

​ 失败:-1

/*===============================================
*   文件名称:pthread_test.c
*   创 建 者:memories 
*   创建日期:2023年05月16日
*   描    述:
================================================*/

在一个进程中创建两个线程,用线程1获取键盘上的数据,线程2打印键盘上的数据到终端,并且当线程1不输入时,线程2等待线程1输入
    
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
void *thread1(void *arg);
void *thread2(void *arg);
sem_t sem_id; //定义全局的信号量 
char buf[32]={0};
//获取键盘输入的数据
void *thread1(void *arg)
{
    while(1)
    {
       // scanf("%s",buf);
    fgets(buf,sizeof(buf),stdin);
   // V操作,释放资源(如果有等待该资源的线程将被唤醒;若没有则资源+1)
    sem_post(&sem_id);
    if(strncmp(buf,"quit",4)==0)
        break;
    }
    pthread_exit(NULL);

}

//打印其数据
void *thread2(void *arg)
{
    while(1)
    {
        //p操作,申请资源(如果有资源就资源数-1,若没有则等待)
        sem_wait(&sem_id);
    printf("buf=%s\n",buf);
    if(strncmp(buf,"quit",4)==0)
        break;
    }
    pthread_exit(NULL);
}



int main(int argc, char *argv[])
{ 
    //创建线程之前,初始化信号量
    //参数1:信号量首地址 参数2:指定信号量的使用范围(0:线程间)
    if(sem_init(&sem_id,0,0)<0)
    {
        perror("init");
        return -1;
    }
    pthread_t tid1,tid2;
    if(pthread_create(&tid1,NULL,thread1,NULL)<0)
    {
        printf("create thread1 failed\n");
        return -1;
    }

    pthread_detach(tid1);
    if(pthread_create(&tid2,NULL,thread2,NULL)<0)
    {
        printf("create thread2 failed\n");
        return -1;
    }
    pthread_detach(tid2);//非阻塞等待
    while(1);
    return 0;
} 


hqyj@ubuntu:~/5.16$ gcc pthread_test.c -lpthread
hqyj@ubuntu:~/5.16$ ./a.out 
asdaf
buf=asdaf

互斥

保护资源完整性

互斥机制:不能同时进行,当一个任务进行中,另一个任务阻塞等待

1、临界资源:

		多个任务共同抢占的  全局资源。

2、由于多个任务抢占同一临界资源,会导致 资源数据不完整,会使用互斥机制来保护 临界资源的完整性。

3、临界区:

访问、操作临界资源的代码行20,21.....

4、锁粒度:

临界区越大,锁粒度越强;

5、同一个临界资源的所有临界区必须同时加锁和解锁 同一把互斥锁

在临界区之前加锁,临界区结束时必须解锁
6、死锁:
	a)多个临界区时,加锁不解锁,就会形成死锁。
	b)多个互斥锁A和B,任务1申请A锁,释放B锁,申请B锁,释放A锁。(交叉 加锁解锁)
7、如何避免死锁?
	a)临界区加锁后必须解锁
	b)尽量避免使用临界资源
	c)同一临界资源只用一把锁
1.先全局定义互斥锁pthread_mutex_t mutex;
2.在main函数中初始化互斥锁
3.lock/unlock操作

pthread_mutex_init

a)int pthread_mutex_init(pthread_mutex_t *mutex,pthread_mutexattr_t *attr)

功能:

​ 初始化互斥锁

参数:

​ mutex:互斥锁

​ attr:互斥锁属性//NULL表示缺省属性

返回值:

​ 成功:0;

​ 失败:-1//返回错误码

pthread_mutex_lock

b)int pthread_mutex_lock(pthread_mutex_t *mutex);

功能:

​ 申请互斥锁

参数:

​ mutex:互斥锁

返回值:

​ 成功:0

​ 失败:返回错误号

pthread_mutex_unlock

c)pthread_mutex_unlock(pthread_mutex_t *mutex);

功能:

​ 释放互斥锁

参数:

​ mutex:互斥锁

返回值:

​ 成功:0

​ 失败:返回错误号

/*===============================================
*   文件名称:pthread_test1.c
*   创 建 者:memories 
*   创建日期:2023年05月16日
*   描    述:
================================================*/

1.创建两个线程thread1,thread2,一个线程赋值,另一个线程打印
2.因为当第一个线程value1=count时会消耗时间资源,此时第二个线程有可能因没有count赋值给value1和value2时,有可能value1==value2所以会打印出来。。
3.所以利用互斥锁
    将线程1完成赋值完全之后,再让线程2作用
    

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

void *thread1(void *arg);
void *thread2(void *arg);

pthread_mutex_t mutex;
int value1,value2,count=0;

void *thread1(void *arg)
{
    while(1)
    {
        pthread_mutex_lock(&mutex);//申请互斥锁,加锁
        count++;
        value1=count;
        value2=count;
        pthread_mutex_unlock(&mutex);//释放互斥锁,解锁
    }
}
void *thread2(void *arg)
{
    while(1)
    {
        pthread_mutex_lock(&mutex);//申请互斥锁,加锁
        if(value1 != value2)
        {
            printf("value1=%d value2=%d count=%d\n",value1,value2,count);
        }
        pthread_mutex_unlock(&mutex);//释放互斥锁,解锁
    }
}

int main(int argc, char *argv[])
{ 
    if(pthread_mutex_init(&mutex,NULL)<0)
    {
        printf("mutex_init failed\n");
        return -1;
    }
    pthread_t tid1,tid2;
    if(pthread_create(&tid1,NULL,thread1,NULL)<0)
    {
        printf("thread1 create failed\n");
        return -1;
    }
    if(pthread_create(&tid2,NULL,thread2,NULL)<0)
    {
        printf("thread2 create failed\n");
        return -1;
    }
    while(1);
    return 0;
} 


未使用互斥锁
    value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
value1=304798371 value2=304798370 count=304798371
^C

使用互斥锁:
    hqyj@ubuntu:~/5.16$ gcc pthread_test1.c -lpthread
hqyj@ubuntu:~/5.16$ ./a.out 
^C

进程间传统通信方式

  1. 通信方式
传统进程间通信: 无名管道pipe、有名管道:fifo 信号:signal

system v IPC对象:共享内存(share memory),消息队列(message queue),信号灯集(semaphore)

BSD:套接字(socket)

无名管道pipe

  1. 无名管道 pipe

pipe()

int fd[2];

int pipe(int fd[2])

功能:

​ 管道的创建

参数:包含两个元素的整型数组

返回值:

​ 成功:0;

​ 失败:-1;

在内核中开辟的管道空间,该管道通过文件描述符进行操作
数据单向传递->[-----]->
当管道建立时,得到fd[0],fd[1]
具备固定的读端fd[0]和写端fd[1],(只有父进程创建好的,子进程才能使用)
/*===============================================
 *   文件名称:pthread_test2.c
 *   创 建 者:memories 
 *   创建日期:2023年05月16日
 *   描    述:
 ================================================*/

进程间不能使用全局变量
    
创建两个子进程,其中子进程1输入数据,子进程2打印数据
    如果不用pipe管道,则会出现,在子进程1stdin向buf写入数据,子进程2无法获取已经存入数据的buf,即使buf为全局变量。因为在创建子进程的过程中会复制父进程main中的process1和process2,但不会开辟空间,只是复制名字。
    
    所以我们需要单独在内核开辟一个空间,让子进程1和子进程2都可以访问这个空间,数据就可以共同调用。则我们需要在内核中创建一个无名管道去传输数据-------------
    子进程12在用户空间。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
//读数据
void process1();
//写数据
void process2();

int fd[2];
char buf[32]={0};
//读数据
void process1()
{
    close(fd[0]);
    //由于该进程用作写入数据,因此必须关闭读端
    while(1)
    {
    fgets(buf,sizeof(buf),stdin);
    //将数据写入管道的写端
    write(fd[1],buf,strlen(buf));
    }
}
//写数据
void process2()
{
    //由于该进程用作读取数据,因此必须关闭写端
    close(fd[1]);
    while(1)
    {
    //将管道的数据读取到buf缓存
    read(fd[0],buf,sizeof(buf));
    printf("buf=%s\n",buf);
    sleep(1);
    }
}

int main(int argc, char *argv[])
{ 
    //创建无名管道
    if(pipe(fd)<0)
    {
        perror("pipe");
        return -1;
    }
    printf("fd[0]=%d fd[1]=%d\n",fd[0],fd[1]);

    pid_t pid=fork();
    if(pid<0)
    {
        perror("fork1");
        return -1;
    }
    else if(pid==0)
    {
        process1();
    }
    else
    {
        pid_t pid2=fork();
        if(pid2<0)
        {
            perror("fork2");
            return -1;
        }
        else if(pid2==0)
        {
            process2();
        }
        else
        {
            //父进程关闭读端和写端,因为内存中共有3个读端,而一个管道只能有一个读端
            close(fd[0]);
            close(fd[1]);
            while(1);
        }
    }

    return 0;
} 


​ 无名管道的特点:

1.只能在具有亲缘关系的进程间使用
2.具有固定的读端和写端,属于半双工通信
3.管道可以看做一个特殊的文件,无文件名只存在于内核当中

注意:
1.只有写端存在,读端才能读取数据,否则将会直接返回
2.写端存在,管道无数据,读端将会阻塞等待写入数据
3.当读端不存在,写端写入数据会出现,管道缓冲区满了,阻塞住
只有当缓冲区有空闲的字节,写端才能写入数据
4.只有管道的读端存在时,向管道中写入数据才有意义
否则,向管道中写入数据的进程将受到内核传来的SIGPIPE信号(通常Broken pipe错误),进程将被终止
/*===============================================
 *   文件名称:pipe2_demo.c
 *   创 建 者:memories 
 *   创建日期:2023年05月16日
 *   描    述:
 ================================================*/

子进程从键盘接收数据,父进程打印数据,利用进程间的通信(pipe无名管道)


#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

void child_process();

int fd[2];
char buf[32]={0};
void child_process()
{
    close(fd[0]);
    while(1)
    {
        memset(buf,0,sizeof(buf));
        fgets(buf,sizeof(buf),stdin);
        write(fd[1],buf,strlen(buf));
    }
}


int main(int argc, char *argv[])
{ 
    if(pipe(fd)<0)
    {
        perror("pipe");
        return -1;
    }

    pid_t pid1=fork();
    if(pid1<0)
    {
        perror("fork");
        return -1;
    }
    else if(pid1==0)
    {
        child_process();
    }
    else
    {   
        close(fd[1]);
        while(1)
        {
            memset(buf,0,sizeof(buf));
            read(fd[0],buf,sizeof(buf));
            printf("buf=%s\n",buf);
        }
    }

    return 0;
} 

/*===============================================
 *   文件名称:pipe3_demo.c
 *   创 建 者:memories 
 *   创建日期:2023年05月16日
 *   描    述:
 ================================================*/


计算无名管道能存放多少数据
    当读端开放时:写数据内存大小会达65535
    当读端关闭时:写端会阻塞等待


#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>


int main(int argc, char *argv[])
{ 
    int fd[2]={0};
    if(pipe(fd)<0)
    {
        perror("pipe");
        return -1;
    }
   // close(fd[0]);//关读端写入数据
    int count=0;
    char buf[1024]={0};
    while(1)
    {
        int ret=write(fd[1],buf,sizeof(buf));
        count += ret;
        printf("count=%d\n",count);
    }

    return 0;
} 


	。
    。
    。
count=47104
count=48128
count=49152
count=50176
count=51200
count=52224
count=53248
count=54272
count=55296
count=56320
count=57344
count=58368
count=59392
count=60416
count=61440
count=62464
count=63488
count=64512
count=65536


有名管道fifo

  1. 有名管道

mkfifo()

int mkfifo(const char *filename,mode_t mode);

功能:

​ 创建管道文件

参数:

​ filename:要创建的管道

​ mode:指定管道的访问权限 0640

返回值:

​ 成功:0;

​ 失败:-1;一般没有返回值

1、有名管道特点:

1、既可以在亲缘关系的进程间通信,也可以在无亲缘关系的进程间进行通信。
2、有名管道可以通过路径名来指出,并且在文件系统中可见
3、进程通过文件I0来操作有名管道,但是不能使用lseek
4、有名管道遵循先进先出规则

2、注意:

1、管道中没有数据,读进程的read会阻塞。.
2、关闭写进程,读进程的read会立即返回。
3、有名管道的数据传递都是在内存中进行的。原因:open函数将磁盘搬到内核中去,所以磁盘中的fifo为0字节
/*===============================================
*   文件名称:fifo_write.c
*   创 建 者:memories 
*   创建日期:2023年05月16日
*   描    述:
================================================*/

通过有名管道fifo使得非亲缘关系的进程之间实现通信

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char *argv[])
{ 
    //在指定的路径中创建有名管道文件
    if(mkfifo("fifo.txt",0640)<0)
    {
        perror("mkfifo");
    }
    //打开有名管道文件,得到管道文件描述符
    int fd = open("fifo.txt",O_WRONLY);
    if(fd<0)
    {
        perror("open");
        return -1;
    }
    while(1)
    {
        char buf[32]={0};
        fgets(buf,sizeof(buf),stdin);
        //向管道文件中写入数据
        write(fd,buf,strlen(buf));
        if(strncmp(buf,"quit",4)==0)
            break;
    }
    close(fd);
    return 0;
} 


/*===============================================
*   文件名称:fifo_read.c
*   创 建 者:memories 
*   创建日期:2023年05月16日
*   描    述:
================================================*/    
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{ 
    if(mkfifo("fifo.txt",0640)<0)
    {
        perror("mkfifo");
    }
    int fd = open("fifo.txt",O_RDONLY);
    if(fd<0)
    {
        perror("open");
        return -1;
    }
    while(1)
    {
        char buf[32]={0};
        read(fd,buf,sizeof(buf));
        printf("buf=%s\n",buf);
        if(strncmp(buf,"quit",4)==0)
            break;
    }
    close(fd);
    return 0;
} 

hqyj@ubuntu:~/5.16$ gcc fifo_write.c 
hqyj@ubuntu:~/5.16$ ./a.out 
mkfifo: File exists
abcdefg

hqyj@ubuntu:~/5.16$ gcc fifo_read.c 
hqyj@ubuntu:~/5.16$ ./a.out 
mkfifo: File exists
buf=abcdefg


要分开编译,因为有两个主函数main,不能在一起gcc编译

![

](C:\Users\孤独memories\AppData\Roaming\Typora\typora-user-images\image-20230516191717480.png)

信号通信

  1. 信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式
    2. 信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用
    它来通知用户空间进程发生了哪些系统事件。
    3. 如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢
    复执行再传递给它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟
    直到其阻塞被取消时才被传递给进程
为什么使用信号通信:
1.后台进程需要使用信号
2.非亲缘关系进程,不能使用pipe
3.非文件IO操作进程,不能使用fifo
则我们选择信号通信---标准IO通信

信号的生存周期: 内核进程、用户进程---------------不包括信号处理
在这里插入图片描述

用户进程对信号的响应方式:

忽略信号:对信号不做任何处理,但是有两个信号不能忽略:即SIGKILL及SIGSTOP。 
捕捉信号:定义信号处理函数,当信号发生时,执行相应的处理函数。
执行缺省操作: Linux对每种信号都规定了默认操作

在这里插入图片描述
在这里插入图片描述

kill()

int kill(pid_t pid, int sig);

头文件:

​ include <signal.h>

​ inlcude <sys/types.h>

功能:

​ 发送信号给某一进程

参数:

​ pid:进程pid号

​ sig:指令信号

返回值:

​ 成功:0;

​ 失败:-1;

raise()

int raise(int sig);

头文件:

​ include <signal.h>

​ inlcude <sys/types.h>

功能:

​ 发送信号给当前进程

参数:

​ sig:指令信号

返回值:

​ 成功:0;

​ 失败:-1;

hqyj@ubuntu:~/5.16$ 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
63) SIGRTMAX-1	64) SIGRTMAX
/*===============================================
*   文件名称:signal_kill.c
*   创 建 者:memories 
*   创建日期:2023年05月16日
*   描    述:
================================================*/
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>

#include <unistd.h>
int main(int argc, char *argv[])
{ 
    printf("hello world\n");
    sleep(1);

    //kill向某个pid号对应的进程发送信号
   // kill(getpid(),SIGKILL);
    kill(getpid(),4);
    while(1)
    {
        printf("--------\n");
        sleep(1);
    }

    return 0;
} 

hqyj@ubuntu:~/5.16$ gcc signal_kill.c
hqyj@ubuntu:~/5.16$ ./a.out 
hello world
非法指令 (核心已转储)

alarm()和pause()

alarm()和pause()
alarm()也称为闹钟函数,它可以在进程中设置一个定时器。当定时器指定的时间到时,内核就向进程发送SIGALARM信号。
pause()函数是用于将调用进程挂起直到收到信号为止。

unsigned int alarm (unsigned int second);

int pause(void);

头文件:

​ #include <unistd.h>

功能:

​ 发送信号给当前进程

参数:

​ second:指定秒数

返回值:

​ 成功:0;

​ 失败:-1

/*===============================================
*   文件名称:signal_alarm.c
*   创 建 者:memories 
*   创建日期:2023年05月16日
*   描    述:
================================================*/
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{ 
    int ret = alarm(3);//设置一个闹钟,返回0
    printf("ret=%d\n",ret);
   // sleep(1);
    ret = alarm(3);//设置一个闹钟,返回第一个闹钟的剩余时间
    printf("ret=%d\n",ret);
    pause();//将当前进程挂起,直到收到内核的SIGALRM信号,终止进程,不会去打印下面的。
    printf("----------\n");
    return 0;
} 

hqyj@ubuntu:~/5.16$ gcc signal_alarm.c 
hqyj@ubuntu:~/5.16$ ./a.out 
ret=0
ret=3
闹钟

signal()

使用signal函数处理时,需要指定要处理的信号和处理函数

void (*signal (int signum, void ( *handler)(int)))(int);

头文件:

​ #include <signal.h>

功能:

​ 设定信号signum的对应方式为handler函数

参数:

​ signum:指定信号

​ handler:SIG_IGN:忽略该信号

​ 自定义的信号处理函数指针

返回值:

​ 成功:设置之前的信号处理方式;

​ 失败:-1

/*===============================================
*   文件名称:signal_ignore.c
*   创 建 者:memories 
*   创建日期:2023年05月16日
*   描    述:
================================================*/
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
    void handler(int no)//形参no表示调用该函数时,传递的信号数值;也就表示当前是哪一个信号被触发
    {
        switch(no)
        {
            case 2: printf("welcome to chengdu\n");
                    break;
            case SIGTSTP:printf("stop\n");
                         break;
        }
    }
int main(int argc, char *argv[])
{ 

   // signal(SIGINT,SIG_IGN); //注册信号,设置信号SIGINT的处理方式为SIG_IGN忽略方式
   
    signal(SIGINT,handler);//设置信号SIGINT处理方式为handler方式
    signal(SIGTSTP,handler);
    while(1)
    {
        printf("hello world\n");
        sleep(1);
    }

    return 0;
} 


hqyj@ubuntu:~/5.16$ gcc signal_ignore.c 
hqyj@ubuntu:~/5.16$ ./a.out 
hello world
hello world
hello world
hello world
hello world
^Cwelcome to chengdu
hello world
hello world
^Zstop
hello world
hello world
hello world
hello world
hello world
^\退出 (核心已转储)

    ctrl \ 结束
将信号名SIGINT-->ctrl C  转变为handler函数的处理方式
将信号名SIGTSTP-->ctrl Z 转变为handler函数的处理方式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/533871.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RocketMQ整理

RocketMQ在阿里云上的商业版本,集成了阿里内部一些更深层次的功能及运维定制。开源版本,功能上略有缺失,但大体上是一样的。 使用Java开发,便于深度定制。最早叫MetaQ。消息吞吐量虽然依然不如Kafka,但是却比RabbitMQ高很多。在阿里内部,RocketMQ集群每天处理的请求数超过…

监控需求来源及主流方案对比

我们从开始了解监控系统来说&#xff0c;首先我们要先了解监控的需求来源&#xff0c;即监控系统都可以用于做什么? 监控需求来源 其实最初的需求很简单&#xff0c;即"系统出问题了我们要能及时感知"。后面随着技术的不断发展&#xff0c;我们对监控系统提出了更…

Linux的超级用户及权限

目录 一:Linux下的两个用户 二&#xff1a;权限 1&#xff1a;目录文件 文件创建的默认权限 2&#xff1a;普通文件 一:Linux下的两个用户 在使用Linux的时候会有两个身份,第一个是普通用户,普通用户在很多方面是受阻的,原因就是权限不够,在这种情况下就有一个超级用户,也…

iOS图片系列一 图片的基本属性

图片在项目的开发中使用频率很高&#xff0c;但是绝大部分都是作为普通的展示或者偶尔需要裁剪&#xff0c;并不需要对图片做什么特别的处理&#xff0c;最近做了一个项目对于图片的需求功能比较多&#xff0c;踩了很多坑的同时也对图片的使用有了更深的理解&#xff0c;整理下…

C++面经:初始化全局变量和未初始化全局变量有什么区别

全局变量初始化而且初始值不为0&#xff0c;那么这样的全局变量是放在内存的.data段的&#xff0c;如果全局变量初始值为0或者未初始化&#xff0c;那么这样的全局变量是放在.bss段的。 考点&#xff1a; 考察C/C语言内存模型&#xff0c;.data&#xff0c;.bss段存放的内容。 …

Windows shell环境: 从git bash切换到msys2

文章目录 1. 目的2. msys2 环境 (Environment)3. 升级 MSYS2: 使用 pacman 滚动式升级整个系统4. 在 Windows Terminal 中增加显示 MSYS25. 使用 zsh6. VSCode 中的配置增加 MSYS2 终端配置 git 路径 7. 安装 C/C 依赖库安装 ag查询 bison 和 flex 的安装目录 8. References 1.…

ES6模块化规范

在没有ES6模块化规范前&#xff0c;有像AMD、CMD这样的浏览器模块化规范&#xff0c;还有像CommonJS这样的服务端模块化规范。 2015年&#xff0c;JS终于推出了官方的模块化规范&#xff0c;为了统一各种规范&#xff0c;我们简称ES6 模块化。 ES6目前作为JS的内置模块化系统&a…

Spring的创建和使用,存储和读取Bean总结

目录 Spring项目创建和使用流程 1.创建一个 Spring 项目 2.存储 Bean 3.读取 Bean ApplicationContext和BeanFactory的区别 通过注解存储 Bean对象 五大类注解的关系 Java程序标准分层 方法注解Bean 注入Bean对象的三种方式 1.属性注入 2.Setter注入 3.构造方法注入…

【Java数据结构】Map和Set

Map和Set 搜索树概念操作 - 查找操作 - 插入操作 - 删除cur没有左树&#xff08;cur.left null&#xff09;cur没有右树&#xff08;cur.right null&#xff09;cur既有左树也有右树&#xff08;最困难的情况&#xff09;替罪羊删除法 操作代码性能分析和 java 类集的关系 搜…

程序员困局:去大城市进大厂却买不了房,回老家又没有高薪工作…

对于在外打拼的程序员来说&#xff0c;难的是进大厂&#xff0c;而不是买不起房。 进大厂的程序员&#xff0c;能不能买得起房&#xff1f; 进大厂的程序员的薪资&#xff0c;还是相当可观的。以阿里P6为例&#xff0c;年薪50万&#xff0c;到手40万左右&#xff0c;刨去10万…

【C++学习】C++11——新特性 | 右值引用 | 完美转发

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《C学习》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; C11——新特性 | 右值引用 | 完美转发 &#x1f440;列表初始化&#x1f9b4; std::initializer_list…

收藏!网络行业主流的六大技术认证全科普

大家好&#xff0c;我是老杨。你的年终总结做完了没&#xff1f;还没做完&#xff0c;点击“年终总结”&#xff0c;拿个模板&#xff0c;快速完成。 很多人在年末都会有列一个新年愿望清单&#xff0c;写写来年想要完成的事情。 不少网工在这两年的就业环境之下&#xff0c;…

Nginx-部署2个vue项目(多个项目)-二级域名设置代理

前言 最近在实际开发过程中&#xff0c;需要在服务器部署2个项目。需要nginx二级域名。 开始时候在网上查了一圈&#xff0c;各有说法&#xff0c;不是很全&#xff0c;很头大。这里把自己成功的二级域名代理记录一下。 网上有很多文章说要该router.js文件&#xff0c;要该vu…

自动备份交换机的配置到远程服务器

环境 交换机配置修改后及时备份相关配置&#xff0c;每次配置变化后需要在1分钟后自动进行保存&#xff0c;并且将配置上传至FTP服务器&#xff1b;每隔30分钟&#xff0c;交换机自动把配置上传到FTP服务器 配置命令&#xff1a; [huawei]set save-configuration delay 1 //…

单片机外围电路:电阻之上下拉电阻

1.基础概念 上拉就是将不确定的信号通过一个电阻钳位在高电平&#xff0c;电阻同时起限流作用。下拉同理&#xff0c;也是将不确定的信号通过一个电阻钳位在低电平。 2.基础应用 上下拉电阻在数字电路中使用&#xff0c;为了好理解一点&#xff0c;可把上下拉电阻大致分为两种…

货币简史:从物物交换到数字货币

货币简史 1. 物物交换阶段2. 一般等价物阶段3. 信用货币阶段4. 电子货币阶段5. 数字货币阶段 金钱是什么时候产生的呢&#xff0c;这取决于你如何定义金钱。 费利克斯马丁&#xff08;FelixMartin&#xff09;名为《金钱:未授权的传记》(Money:An Unauthorized Biography)书中…

19c rac环境修改pubic, vip,scan ip步骤

19c rac环境第一次修改public、vip和scan ip&#xff0c;和11g还是稍有不同。首先说明下环境 具体步骤如下 1、修改public地址&#xff0c;关闭实例后使用root用户操作 [rootdb1 ~]# ./oifcfg getif bond0 172.20.30.0 global public bond2 100.100.100.0 global clust…

【JVM】JVM堆内存(heap)详解

文章目录 前言一、堆内存划分二、为什么移除永久代&#xff1f;三、分代概念四、为什么分代&#xff1f;五、为什么survivor分为两块相等大小的幸存空间&#xff1f;六、JVM堆内存常用参数七、垃圾回收算法&#xff08;GC&#xff0c;Garbage Collection&#xff09;八、垃圾收…

java 反射及代理模式初步学习

java 反射及代理模式初步学习 0. 什么是反射&#xff1f; Java的反射&#xff08;reflection&#xff09;机制是指在程序的运行状态中&#xff0c;可以构造任意一个类的对象&#xff0c;可以了解任意一个对象所属的类&#xff0c;可以了解任意一个类的成员变量和方法&#xf…

小程序组件

swiper swiper 和 swiper-item 结合使用&#xff0c;&#xff0c; swiper有默认高度 300rpx 属性&#xff1a; autoplay &#xff1a; 自动播放circular &#xff1a; 循环播放indicator-dots &#xff1a; 显示指示点indicator-active-color &#xff1a; 轮播选中的颜色 &…