嵌入式第二十四天


复习: 

进程 :
父子进程
孤儿进程 
僵尸进程 

进程的状态: 
运行态 
阻塞态 
暂停态 
僵死态 
消亡态 

命令: 
ps aux   查看进程号 
ps -ef   查看进程号
top      查看cpu,内存占用率
pstree   linux倒立的树形结构
bg 1      使程序在后台运行
fg 1      使程序在前台运行
ctrl + c 终止进程
ctrl + z 暂停进程
ctrl + \ 

函数:
getpid();获取子进程的pid   getppid();获取父进程的pid  
fork();  创建子进程
exit();  清理io缓存区并退出   _exit();退出,但是不清理io缓存区
wait(&t);收尸函数,&t存储子进程的返回状态   waitpid(pid,&t,WNOHANG);收尸函数,&t存储子进程pid的返回状态,WNOHANG
不阻塞,0阻塞。
execl();第二个参数以列表的形式写出    execv();第二个参数传递为指针数组   execvp();第一个参数为文件名

指针数组; 是数组。   int *a[3];  |int *|int *|int *|   *a[0]
数组指针: 是指针。 int (*p)[4];

守护进程: 
1. fork();   父进程: exit(0);使父进程脱离终端的控制 
2. setsid();  创建新会话。 脱离进程组,原会话组的控制
3. chdir("/tmp");//创建目录,灵活操作 
4. umask(0);掩码为0,保留子进程继承父进程的权限
5. close(0/1/2);  关闭文件描述符。 关闭标准输入,标准输出,标准错误输出

多进程: 
int main()
{
    int i;
    for(i=0; i<3; i++)
    {
        fork();
    }
}
i=0;   父   子1  
i=1;   子2  子1-1  
i=2;   子3  子1-2 子2-1  子1-1-1


进程间通信: 信号、 管道、 共享内存、消息队列、信号量、 socket套接字

一、 信号    
1. 什么是信号:    在 windows 中,如果无法结束一个进程,可以通过任务处理器结束。 
                同样的功能,在linux和unix中,使用信号这种机制来处理。 
                一个信号的产生叫生成、信号的接收叫捕获。 
                信号:是进程间通信机制中唯一的异步通信机制。 
    
2. 信号的种类: man 7 signal   (kill -l)

        Signal     Value     Action   Comment
       ──────────────────────────
       SIGINT        2       Term    ctrl + c  中断信号
       SIGQUIT       3       Core    ctrl + \  终止进程信号
       SIGILL        4       Core    非法指令
       SIGABRT       6       Core    abort 函数产生的终止信号 
       SIGFPE        8       Core    浮点异常 
       SIGKILL       9       Term    必杀
       SIGSEGV      11       Core    段错误 -- 地址非法使用 
       SIGPIPE      13       Term    管道破裂
       SIGALRM      14       Term    闹钟信号 
       SIGTERM      15       Term    kill 产生的信号 
       SIGCHLD   20,17,18    Ign     子进程终止后,会向父进程发送此信号 
       SIGTSTP   18,20,24    Stop    ctrl + z 将一个运行着的进程扔到后台,并暂停

3. 信号处理方式
1) 捕获信号  signal(SIGINT,fun);  /当SIGINT 信号被捕获,执行 fun 函数。  
2) 忽略信号  signal(SIGTSTP,SIG_IGN);  
3) 默认处理  signal(SIGILL,SIG_DFL);


4. 处理信号的函数:  signal
函数原型:        typedef void (*sighandler_t)(int); // 将 sighandler_t 这个指针,typedef 成为一个类型:函数指针类型。 
                sighandler_t signal(int signum, sighandler_t handler);
头文件:          #include <signal.h>
参数:           参数一: 指定信号代码。 
              参数二: 对于信号的处理方式。
返回值:  函数指针
功能:    对信号做处理。 信号处理函数。 
说明: typedef void (*sighandler_t)(int); 
    void (*sighandler_t)(int);  这是一个函数指针的定义。 定义的指针名为 sighandler_t. 
    当这个定义前,加上 typedef ,这个指针就不再是一个指针,而是重定义成类型名了。
    所以得到的结论是: sighandler_t 是一个类型名。 可以用它来定义此种类型的变量。 

示例: 

#include <stdio.h>
#include <signal.h>
void fun(int sig_num)
{
    switch(sig_num)
    {
    case SIGINT:
        printf("lalala ~\n");
        break;
    case SIGTSTP:
        printf("hello world!\n");
        break;
    case SIGQUIT:
        printf("I'm a teacher\n");
        break;
    default:
        break;
    }
    return;
}
int main()
{
    signal(SIGINT,fun);
    signal(SIGTSTP,fun);
    signal(SIGQUIT,fun);
    while(1);
    return 0;
}

//练习:编写程序 向数组中输入数据 当收到信号(SIGINT)时 统计出数组中元素的个数 sleep(1);  
#include <stdio.h>
#include <signal.h>
int count = 0;
void fun(int sig_num)
{
    printf("count : %d\n",count+1);
    return ;
}
int main()
{
    signal(SIGINT,fun);
    int a[1000] = {0};
    for(count=0; count<1000; count++)
    {
        a[count] = 66;
        sleep(1);
    }
    return 0;
}

5. raise
函数原型: int raise(int sig);
参数:   信号的编号。 
功能: 只能给自己发送信号。 
返回值: 成功 0 
         失败 -1 

6. 发送信号的函数:   kill
函数原型:    int kill(pid_t pid, int sig);
头文件:        #include <sys/types.h>
                #include <signal.h>
参数;  参数一: pid  > 0   发送信号给 进程号为 pid 的这个进程。 
                 pid  == 0  发送信号给进程组的所有进程。 
                 pid  == -1  发送信号给除了init 的所有进程。 
                 pid  < -1  发送信号给进程组号为 -pid 的每一个进程。 
        参数二: sig: 信号类型。 
返回值: 成功: 0 
         失败: -1 
示例:
文件1:sig.c
#include <stdio.h>
#include <signal.h>
void fun(int sig_num)
{
    printf("I'm not afraid of you\n");
    return;
}
int main()
{
    printf("%d\n",getpid());
    signal(SIGINT,fun);
    while(1);
    return 0;
}
//文件2: kill.c 
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
int main(int argc,char *argv[])
{
    int pid = atoi(argv[1]); //将传递过来的字符串转换为整型。 
    kill(pid,SIGINT);
    return 0;
}
//
执行过程: 
1) gcc sig.c    
2) gcc kill.c -o mykill  
3) ./a.out   //等信号  
4) ./mykill 15678  //给进程号为 15678 的这个进程发送信号。  


7. 闹钟信号函数:  alarm
函数原型:   unsigned int alarm(unsigned int seconds);
头文件:     #include <unistd.h>
参数:     seconds 无符号整型的秒数。 经过 seconds 秒,发送 SIGALRM 信号。 
返回值:   如果之前设置过时间,本次会返回上次设置时间后,剩余的秒数。
           如果之前没有设置过时间,则返回 0 。 
示例:
/// 
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
void fun(int s)
{
    printf("Time out !\n");
    return ;
}
int main()
{
    signal(SIGALRM,fun);
    alarm(5);
    int i;
    for(i=0; i<10; i++)
    {
        printf("Hello\n");
        sleep(1);
    }
    return 0;
}

//练习:测试分钟打字速度
1 随机产生字符  rand()%26+'a'
2 signal(SIGALRM,fun)
//
#include <signal.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int count = 0;
void fun(int signum)
{
    printf("Your speed is %d\n",count*3);
    exit(0);
}
int main()
{
    srand(time(0));
    signal(SIGALRM,fun);
    alarm(20);
    char c,input;
    while(1)
    {
        c = rand()%26 + 'a';
        printf("%c\n",c);
        input = getchar();
        getchar();
        if(input == c)
            count ++;
    }
}


    管道 
二、 无名管道 
1. 创建管道函数:  pipe  
描述符:int pfd[2];    pfd[0]:读端   pfd[1]:写端
函数原型:  int pipe(int pipefd[2]);
头文件:    #include <unistd.h>
参数:   文件描述符。
返回值: 成功: 0 
         失败: -1 
特点:  
1) 半双工通信           
2) 无名管道,只能用在有亲缘关系的进程之间。 
3) 不是文件。不属于任何文件系统,只存在于内存中。 
4) 只要管道中信息被读取,被读取的内存就释放。可以继续存放数据。
5) 管道传输信息时,没有类型。 所以双方在读写时,要事先约定好类型。 

示例: 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
    int pfd[2];
    int ret = pipe(pfd);
    if(ret < 0)
    {
        perror("pipe");
        exit(-1);
    }
    pid_t id = fork();
    if(id < 0)
    {
        perror("fork");
        exit(-1);
    }
    else if(id == 0)
    {
        sleep(2);
        close(pfd[1]);
        int n;
        read(pfd[0],&n,sizeof(n));
        close(pfd[0]);
        printf("%d \n",n);
    }
    else 
    {
        close(pfd[0]);
        int num;
        puts("input a num:");
        scanf("%d",&num);
        write(pfd[1],&num,sizeof(num));
        close(pfd[1]);
        wait(NULL);

    }
    return 0;
}

//练习:父进程从键盘获取一个字符串 传递给子进程 
子进程 收到串后 转为大写 在输出到屏幕

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void change(char *p)
{
    while(*p)    //直到字符串结束。 
    {
        if(*p >= 'a' && *p <= 'z')   //如果在小写字母范围。 换成大写 。
            *p = *p - 'a' + 'A';
        p++;
    }
    return ;
}
int main()
{
    int pfd[2];
    int ret = pipe(pfd);    //先创建管道。 
    if(ret < 0)
    {
        perror("pipe");
        exit(-1);
    }
    pid_t id = fork();   //然后创建进程。 
    if(id < 0)
    {
        perror("fork");
        exit(-1);
    }
    else if(id == 0)         //子进程  
    {
        sleep(2);
        close(pfd[1]);     //关闭写端。 
        char string[100] = "\0";
        read(pfd[0],string,sizeof(string));   //读取信息到变量中。 
        close(pfd[0]);
        change(string);         //大小写转换。 
        printf("%s \n",string);   //输出最终结果。 
    }
    else 
    {
        close(pfd[0]);       //关闭读端。 
        char buf[100] = {0};
        puts("input a string:");
        gets(buf);                   
        write(pfd[1],buf,sizeof(buf));   //向管道文件中写内容。 
        wait(NULL);
    }
    return 0;
}


三 有名管道:
1. 创建有名管道:  mkfifo  
函数原型:        int mkfifo(const char *pathname, mode_t mode);
头文件:   #include <sys/types.h>
           #include <sys/stat.h>
参数:     参数一: 管道文件所在路径,一直写到管道文件名称。 
           参数二: 管道文件的权限。 
返回值:   成功: 0 
           失败: -1 

2. 删除文件: unlink  
函数原型:        int unlink(const char *pathname);
头文件:      #include <unistd.h>
参数:       pathname : 管道文件所在路径,一直要写到管道文件名字结束。 
返回值:    成功: 0 
            失败: -1

3. 步骤: 
1) mkfifo  
2) open 
3) read/write 
4) close  
5) unlink 

4. 与无名管道的区别: 
a. 有名管道是个管道文件。 
b. 有名管道可以用在任意两个进程之间。这两个进程不必有亲缘关系。 

示例:
write.c   
//
#include "my.h"    
int main()
{
    int ret = mkfifo("./myfifo",0664);
    if(ret < 0)
    {
        perror("mkfifo");
        exit(-1);
    }
    int fd = open("./myfifo",O_WRONLY);
    if(fd < 0)
    {
        perror("open");
        exit(-1);
    }
    int n = 122;
    write(fd,&n,sizeof(n));
    close(fd);
    return 0;
}
read.c 
///
#include "my.h"
int main()
{
    int fd = open("./myfifo",O_RDONLY);
    if(fd < 0)
    {
        perror("open");
        exit(-1);
    }
    int n;
    read(fd,&n,sizeof(n));
    printf("n = %d \n",n);
    close(fd);
    unlink("./myfifo");
    return 0;
}
///
执行过程: 
1) gcc write.c -o w  
2) gcc read.c -o r 
3) ./w     (有可能在阻塞等待)
4) ./r     读取文件信息。 


//练习:使用有名管道,进行进程间通信。 进程1从键盘获取一个字符串 传递给子进程 
进程2 收到串后 转为大写 在输出到屏幕

三个文件: 
create.c   (功能:创建有名管道)
//
#include <stdio.h>
int main(int argc,char *argv[])
{
    if(argc < 2)
    {
        printf("%s fifo_name\n",argv[0]);
        return 0;
    }
    int ret = mkfifo(argv[1],0664);
    if(ret < 0)
    {
        perror("mkfifo");
        return -1;
    }
    printf("OK\n");
    return 0;
}

write.c 
///
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char *argv[])
{
    if(argc < 2)
    {
        printf("%s  <fifo_name>\n",argv[0]);
        return -1;
    }
    int fd = open(argv[1],O_WRONLY);
    char buf[100] = {0};
    puts("input a string:");
    gets(buf);  
    write(fd,buf,sizeof(buf));
    close(fd);
    return 0;
}

read.c 
//
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void change(char *p)
{
    while(*p)
    {
        if(*p >= 'a' && *p <= 'z')
            *p = *p - 'a' + 'A';    
        p++;
    }
    return ;
}
int main(int argc,char *argv[])
{
    if(argc < 2)
    {
        printf("%s <fifo_name>\n",argv[0]);
        return -1;
    }
    int fd = open(argv[1],O_RDONLY);
    char string[100] = "\0";
    read(fd,string,sizeof(string));
    close(fd);
    change(string);
    printf("%s \n",string);
    return 0;
}
//
执行过程: 
gcc create.c -o creat  
gcc write.c -o w 
gcc read.c -o r 
./creat fifo
./w     fifo        (写入数据,如果空间不足,阻塞写)
./r     fifo      读取数据。 

//练习: 进程1,向管道中写一个学生信息: struct worker,进程2 读信息。
struct worker 
{
    char name[30];
    int age;
    float salary;
};

answer:   4 files 
(1) my.h
/
#ifndef _MY_H_ 
#define _MY_H_  1

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

typedef struct{
    char name[30];
    int age;
    float salary;
}worker_t;

#endif /* _MY_H_ */

(2)
create.c 
//
#include <stdio.h>
int main(int argc,char *argv[])
{
    if(argc < 2)
    {
        printf("%s fifo_name\n",argv[0]);
        return 0;
    }
    int ret = mkfifo(argv[1],0664);
    if(ret < 0)
    {
        perror("mkfifo");
        return -1;
    }
    printf("OK\n");
    return 0;
}

(3) write.c 
/
#include "my.h"
int main(int argc,char *argv[])
{
    if(argc < 2)
    {
        printf("%s  <fifo_name>\n",argv[0]);
        return -1;
    }
    int fd = open(argv[1],O_WRONLY);

    worker_t w; 
    puts("input your name age salary:");
    scanf("%s%d%f",w.name,&w.age,&w.salary);  
    write(fd,&w,sizeof(w));
    close(fd);
    return 0;
}

(4) read.c 
//
#include "my.h"
int main(int argc,char *argv[])
{
    if(argc < 2)
    {
        printf("%s <fifo_name>\n",argv[0]);
        return -1;
    }
    int fd = open(argv[1],O_RDONLY);
    worker_t w;
    read(fd,&w,sizeof(w));
    close(fd);
    unlink(argv[1]);
    printf("%s :--> %d %.2f \n",w.name,w.age,w.salary);
    return 0;
}
//
执行过程: 
gcc create.c -o creat 
gcc write.c -o write 
gcc read.c -o read  
./creat myfifo 
./write myfifo 
./read myfifo 


//作业:实现有名管道版本的cat:
    write.c 将磁盘文件中的数据  传递给进程read.c  
    read.c 将文件内容输出到屏幕

    


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值