C/C++进程超详细详解【下部分】(系统性学习day8)

简介: C/C++进程超详细详解【下部分】(系统性学习day8)

前言

上篇博客对C/C++进程的中部分进行了详细讲解,本篇博客将继续讲解和补充关于线程的知识点。


一,有名管道通信

1 .概念

1.由于无名管道只能用于具有亲缘关系的进程之间,这就限制了无名管道的使用范围。

2.而有名管道可以使互不相关的两个进程互相通信。有名管道可以通过路径名来指出,并且在文件系统中可见

3.进程通过文件IO来操作有名管道

2 .创建有名管道

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
//参数1 ---- 管道的名称
//参数2 ---- 管道的权限
//返回值 -----成功:0,失败:-1
例如:
int main(int argc ,char **argv)
    {
        if(argc != 2){
            fprintf(stderr,"Usage: %s <fifoname>\n",argv[0]);
            exit(1);
        }
        //创建有名管道
        if(mkfifo(argv[1],0666) < 0){   //管道权限= 0666 & ~umusk
            perror("mkfifo");
            exit(1);
        }
        return 0;
    }

实例代码如下:

//从管道读10个整数,然后排序,并打印
int main(int argc ,char **argv)
{
    int fd;
    int a[10],i,j,flag;
    if(argc != 2){
        fprintf(stderr,"Usage: %s <fifoname>\n",argv[0]);
        exit(1);
    }
 
    //判断管道文件是否存在,如果不存在则创建,存在则直接打开
    if(access(argv[1],F_OK)){
        //创建有名管道
        if(mkfifo(argv[1],0666) < 0){   //管道权限= 0666 & ~umusk
            perror("mkfifo");
            exit(1);
        }
    }
 
    //打开管道
    if((fd = open(argv[1],O_RDWR)) < 0){
        perror("open");
        exit(1);
    }
 
    //从管道中读10个整数
    if(read(fd,a,sizeof(a)) < 0){
        perror("read");
        exit(1);
    }
 
    //排序
    for(i = 0; i < 9; i++){
        flag = 1;
        for(j = 0; j < 9-i; j++)
            if(a[j] > a[j+1]){
                a[j] = a[j] + a[j+1];
                a[j+1] = a[j] - a[j+1];
                a[j] = a[j] - a[j+1];
                flag = 0;
            }
        if(flag)
            break;
    }
    for(i = 0; i < 10; i++){
        printf("%d  ",a[i]);
        fflush(stdout);
        sleep(1);
    }
    printf("\n");
    close(fd);
 
    return 0;
}
//从键盘输入10个整数,并写入管道
int main(int argc ,char **argv)
{
    int fd;
    int a[10],i;
    if(argc != 2){
        fprintf(stderr,"Usage: %s <fifoname>\n",argv[0]);
        exit(1);
    }
 
    //判断管道文件是否存在,如果不存在则创建,存在则直接打开
    if(access(argv[1],F_OK)){
        //创建有名管道
        if(mkfifo(argv[1],0666) < 0){   //管道权限= 0666 & ~umusk
            perror("mkfifo");
            exit(1);
        }
    }
 
    //打开管道
    if((fd = open(argv[1],O_RDWR)) < 0){
        perror("open");
        exit(1);
    }
 
    //键盘输入10个整数
    printf("请输入10个整数:");
    for(i = 0; i < 10; i++){
        scanf("%d",&a[i]);
    }
 
    //向管道中写10个整数
    write(fd,a,sizeof(a));
 
    close(fd);
 
    return 0;
}

二、信号通信

1 .概念

信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式

信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。

如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程

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

(1)忽略信号:

   对信号不做任何处理,但是有两个信号不能忽略:即SIGKILL及SIGSTOP。

(2)捕捉信号:

   定义信号处理函数,当信号发生时,执行相应的处理函数。

(3)执行缺省操作:

   Linux对每种信号都规定了默认操作

//查看linux系统中的信号---kill

peter@ubuntu:~/2308/proc/day03_code$ 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

3. 用户进程对常用信号的缺省操作

信号名 含义 默认操作
SIGHUP

该信号在用户终端连接(正常或非正常)结束时发出,通常是在终端的控   

制进程结束时,通知同一会话内的各个作业与控制终端不再关联。            

终止
SIGINT 该信号在用户键入INTR字符(通常是Ctrl-C)时发出,终端驱动程序发送                 
此信号并送到前台进程中的每一个进程。                
终止
SIGQUIT 该信号和SIGINT类似,但由QUIT字符(通常是Ctrl-\)来控制。 终止
SIGILL 该信号在一个进程企图执行一条非法指令时(可执行文件本身出现错误,                     
或者试图执行数据段、堆栈溢出时)发出。    
终止
SIGFPE

该信号在发生致命的算术运算错误时发出。这里不仅包括浮点运算错误,                   

 还包括溢出及除数为0等其它所有的算术的错误。    

终止
SIGKILL 该信号用来立即结束程序的运行,并且不能被阻塞、处理和忽略。 终止
SIGALRM 该信号当一个定时器到时的时候发出。 终止
SIGSTOP 该信号用于暂停一个进程,且不能被阻塞、处理或忽略。 暂停进程
SIGTSTP 该信号用于暂停交互进程,用户可键入SUSP字符(通常是Ctrl-Z)发出这个信号。 暂停进程
SIGCHLD 子进程改变状态时,父进程会收到这个信号 忽略
SIGABORT 该信号用于结束进程 终止

4. 信号处理流程

5. 信号相关函数(系统调用)

5.1 kill - 给指定进程发送信号

#include <sys/types.h>
    #include <signal.h>
    int kill(pid_t pid, int sig);
    //参数1  -----信号发送的目标进程的ID
                 参数1取值分四种
                 pid > 0 ,给进程号为pid的进程发送信号
                 pid = 0 , 给当前进程组中每一个进程发送信号
                 pid = -1, 发送给进程表中所有的进程
                 pid < -1, 给指定进程组中的每一个进程发送信号,该进程组的ID为-pid
    //参数2  -----要发送的信号
    //返回值----成功:0,失败:-1
实例代码如下:
int main(int argc,char **argv)
{
#if 1
    pid_t pid;
 
    if(argc != 2){
        fprintf(stderr,"Usage: %s <pid>\n",argv[0]);
        exit(1);
    }
 
    pid = atoi(argv[1]);   //atoi将字符串转为整数
    if(kill(pid,SIGKILL) < 0){
        perror("kill");
        exit(1);
    }
#else
    pid_t pid1,pid2;
    int i;
 
    if((pid1 = fork()) < 0){
        perror("fork");
        exit(1);
    }else if(!pid1){   //子进程1
        for(i = 0; ; i++){
            printf("pid = %d\n",getpid());
            sleep(1);
        }
    }else{
        if((pid2 = fork()) < 0){
            perror("fork");
            exit(1);
        }else if(!pid2){  //子进程2
            for(i = 0; ; i++){
                printf("pid = %d\n",getpid());
                sleep(1);
            }
        }else{  //父进程
            for(i = 0; ; i++){
                printf("pid = %d\n",getpid());
                sleep(1);
                if(i == 5)
                    //kill(0,SIGKILL); //0--给进程组中每一个进程发送信号
                    kill(pid2,SIGKILL);  //给pid2发送信号
            }
        }
    }
 
#endif
    return 0;
}

5.2 raise() --给当前进程发送信号  

#include <signal.h>
int raise(int sig);
//参数 ----信号
//返回值----成功:0,失败:-1
实例代码如下:
int main(void)
{
    int i;
    for(i = 0; ; i++){
        printf("pid = %d\n",getpid());
        sleep(1);
        if(i == 7)
            raise(SIGKILL);
    }
    return 0;
}

5.3 alarm() 在进程中设置定时器(闹钟)  

#include <unistd.h>
unsigned int alarm(unsigned int seconds);
//参数 ---- 要定时的秒数
//返回值 ----如果第一次调用该函数,返回值为0,
            如果调用之前已经设置了定时器,则返回上次定时剩余的秒数
实例代码如下:
int main(void)
{
    int i,ret;
 
    ret = alarm(20);  // 从该语句开始计时,7秒之后发送SIGALRM信号给当前进程
    printf("ret = %d\n",ret);
 
    for(i = 0; ; i++){
        printf("pid = %d,i = %d\n",getpid(),i);
        sleep(1);
        if(i == 7){
            ret = alarm(5);
            printf("ret = %d\n",ret);
        }
    }
    return 0;
}

5.4 pause() 使进程挂起  

int pause(void);

//使进程挂起 ,直到进程收到任意一个信号则返回。

实例代码如下:
//信号处理函数
void fun(int signo)
{
    int i;
    for(i = 0; i < 3; i++){
        printf(GREEN "正在吃饭\n"NONE);
        sleep(1);
    }
}
 
int main(void)
{
    int i,j;
 
    //注册信号SIGALRM
    signal(SIGINT,fun);
 
 
    for(i = 0; ; i++){
        pause();  //使进程挂起,直到收到信号为止
        for(j = 0; j < 5; j++){
            printf("正在睡觉\n");
            sleep(1);
        }
    }
    return 0;
}

5.5 signal 注册信号  

#include <signal.h>
typedef void (*sighandler_t)(int);  //定义函数指针类型名称
sighandler_t signal(int signum, sighandler_t handler);
//参数1 ---- 要注册的信号
//参数2 ---- 信号的响应方式:
                SIG_IGN   ----- 忽略信号
                SIG_DFL   ----- 对信号进行缺省操作
        信号处理函数的指针    ----- 捕捉信号,当收到信号,则会执行信号处理函数
        void xxx_fun(int)
        {
        
        }
//返回值 ----成功:信号处理函数指针,失败:SIG_ERR
实例代码如下:
//信号处理函数
void eat(int signo)
{
    int i;
    for(i = 0; i < 3; i++){
        printf(GREEN "正在吃饭\n"NONE);
        sleep(1);
    }
}
 
int main(void)
{
    int i;
 
    //注册信号SIGALRM
    signal(SIGALRM,eat);
 
 
    for(i = 0; ; i++){
        if(i % 8 == 0){
            alarm(8);  // 从该语句开始计时,7秒之后发送SIGALRM信号给当前进程
            printf("开始睡觉,设置闹钟\n");
        }
        printf("正在睡觉......\n");
        sleep(1);
    }
    return 0;
}


总结

       本篇文章针对进程进行最后的超详细讲解和补充,希望能够帮到大家!

       以后还会给大家展现更多关于嵌入式和C语言的其他重要的基础知识,感谢大家支持懒大王!

相关文章
|
1月前
麒麟系统mate-indicators进程占用内存过高问题解决
【10月更文挑战第7天】麒麟系统mate-indicators进程占用内存过高问题解决
162 2
|
1月前
|
资源调度 Linux 调度
Linux c/c++之进程基础
这篇文章主要介绍了Linux下C/C++进程的基本概念、组成、模式、运行和状态,以及如何使用系统调用创建和管理进程。
34 0
|
20天前
|
编译器 C语言 C++
配置C++的学习环境
【10月更文挑战第18天】如果想要学习C++语言,那就需要配置必要的环境和相关的软件,才可以帮助自己更好的掌握语法知识。 一、本地环境设置 如果您想要设置 C++ 语言环境,您需要确保电脑上有以下两款可用的软件,文本编辑器和 C++ 编译器。 二、文本编辑器 通过编辑器创建的文件通常称为源文件,源文件包含程序源代码。 C++ 程序的源文件通常使用扩展名 .cpp、.cp 或 .c。 在开始编程之前,请确保您有一个文本编辑器,且有足够的经验来编写一个计算机程序,然后把它保存在一个文件中,编译并执行它。 Visual Studio Code:虽然它是一个通用的文本编辑器,但它有很多插
|
1月前
|
消息中间件 Linux API
Linux c/c++之IPC进程间通信
这篇文章详细介绍了Linux下C/C++进程间通信(IPC)的三种主要技术:共享内存、消息队列和信号量,包括它们的编程模型、API函数原型、优势与缺点,并通过示例代码展示了它们的创建、使用和管理方法。
29 0
Linux c/c++之IPC进程间通信
|
1月前
|
Linux C++
Linux c/c++进程间通信(1)
这篇文章介绍了Linux下C/C++进程间通信的几种方式,包括普通文件、文件映射虚拟内存、管道通信(FIFO),并提供了示例代码和标准输入输出设备的应用。
23 0
Linux c/c++进程间通信(1)
|
1月前
|
Linux C++
Linux c/c++之进程的创建
这篇文章介绍了在Linux环境下使用C/C++创建进程的三种方式:system函数、fork函数以及exec族函数,并展示了它们的代码示例和运行结果。
31 0
Linux c/c++之进程的创建
|
1月前
|
Java 编译器 C++
c++学习,和友元函数
本文讨论了C++中的友元函数、继承规则、运算符重载以及内存管理的重要性,并提到了指针在C++中的强大功能和使用时需要注意的问题。
19 1
|
2月前
|
监控
MASM32写的免费软件“ProcView/系统进程监控” V1.4.4003 说明和下载
MASM32写的免费软件“ProcView/系统进程监控” V1.4.4003 说明和下载
|
2月前
|
C++
【C++案例】一个项目掌握C++基础-通讯录管理系统
这篇文章通过一个通讯录管理系统的C++项目案例,详细介绍了如何使用C++实现添加、显示、删除、查找、修改和清空联系人等功能。
42 3
|
1月前
麒麟系统mate-indicators进程占用内存过高问题解决
【10月更文挑战第5天】麒麟系统mate-indicators进程占用内存过高问题解决
125 0