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语言的其他重要的基础知识,感谢大家支持懒大王!

相关文章
|
4天前
|
存储 编译器 C语言
c++的学习之路:5、类和对象(1)
c++的学习之路:5、类和对象(1)
19 0
|
4天前
|
C++
c++的学习之路:7、类和对象(3)
c++的学习之路:7、类和对象(3)
19 0
|
4天前
|
消息中间件 Unix Linux
Linux的学习之路:17、进程间通信(1)
Linux的学习之路:17、进程间通信(1)
20 1
|
4天前
|
C语言 C++
c++的学习之路:4、入门(3)
c++的学习之路:4、入门(3)
18 0
|
4天前
|
编译器 C++
c++的学习之路:23、多态(2)
c++的学习之路:23、多态(2)
17 0
|
4天前
|
存储 安全 Linux
Linux的学习之路:9、冯诺依曼与进程(1)
Linux的学习之路:9、冯诺依曼与进程(1)
18 0
|
3天前
|
设计模式 Java C++
【C++高阶(八)】单例模式&特殊类的设计
【C++高阶(八)】单例模式&特殊类的设计
|
3天前
|
编译器 C++
【C++基础(八)】类和对象(下)--初始化列表,友元,匿名对象
【C++基础(八)】类和对象(下)--初始化列表,友元,匿名对象
|
7天前
|
存储 安全 C语言
【C++】string类
【C++】string类
|
存储 编译器 Linux
标准库中的string类(中)+仅仅反转字母+字符串中的第一个唯一字符+字符串相加——“C++”“Leetcode每日一题”
标准库中的string类(中)+仅仅反转字母+字符串中的第一个唯一字符+字符串相加——“C++”“Leetcode每日一题”