您的位置:首页 > 编程语言

Unix高级编程:环境变量(续)、管道、文件重定向、信号基础

2017-01-15 22:53 639 查看
一、环境变量(续)

"setenv"(3)

#include <stdlib.h>

int setenv(const char *name, const char *value, int overwrite);

功能:改变或增加一个环境变量

参数:

"name" 要添加或改变的环境变量名

"value" 环境变量的值

"overwrite" 环境变量存在,值取 0 环境变量值不变;值取非 0 环境变量值被改为value

返回值:

成功 - 返回 0

失败 - 返回 -1,errno被设置

"unsetenv"(3)

int unsetenv(const char *name);

功能:删除环境变量(环境变量不存在,函数执行成功,环境变量不变)

参数:"name" 环境变量名

返回值:

成功 - 返回 0

失败 - 返回 -1,errno被设置

/*举例验证,为进程添加/获取/打印/改变/再打印/删除环境变量,setenv.c*/

#include <stdio.h>

#include <stdlib.h>

int main(void/*int argc, char *argv[], char *envp[]*/) {

    setenv("name", "laowang", 0);//设置环境变量

    printf("name = %s\n", getenv("name"));

    setenv("name", "gebi", 0);//第三个参数为0环境变量值不变

    printf("name = %s\n", getenv("name"));

    setenv("name", "gebi", 1);//修改环境变量的值

    printf("name = %s\n", getenv("name"));

    unsetenv("name");//删除环境变量

    if(getenv("name")) 

        printf("name = %s\n", getenv("name"));

    return 0;

}

/*综合execle和环境变量使用演示,exec_env.c*/

sudo mv ../1208/myenv /bin 移动文件

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

int main(void) {

    const char *ps_envp[] = {"name=laowang", NULL};

    execle("/bin/myenv", "myenv", NULL, ps_envp);//添加环境变量

    return 0;

}

二、管道 //鸟哥私房菜

管道分为"有名管道"和"无名管道"两种。

"无名管道",单向的(双向需创建两个管道)

"pipe"(2) //无名管道,应用于有亲缘关系的两个进程间的通讯

#include <unistd.h>

int pipe(int pipefd[2]);

功能:创建管道

参数:"pipefd[2]" 含两个整形数元素的数组

pipefd[0] 负责写数据的进程关闭管道的"读端文件描述符"

pipefd[1] 负责读数据的进程关闭管道的"写端文件描述符"

返回值:

成功 - 返回 0

失败 - 返回 -1,errno被设置

/*举例验证,创建管道实现进程间的通信,pipe.c*/

父进程: 子进程

1)pipe创建管道 --

2)fork创建子进程 子进程被创建

3)close(pipefd[0]); close(pipefd[1]);

4)向管道写入内容 从管道里读取内容/输出读取到的内容

5)wait(NULL); 退出

6)退出

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/wait.h>

int main(void) {

    int fd[2];

    char buf[20] = {0};

    int r = pipe(fd);//创建管道

    if(r == -1) {

        perror("pipe");

        return 1;

    }   

    pid_t pid = fork();//创建子进程

    if(pid == -1) {

        perror("fork");

        return 2;

    }   

    if(pid == 0) { //子进程执行的代码

        close(fd[1]);//关闭子进程写端,留出读端

        int size = read(fd[0], buf, 12);

        write(1, buf, size);//标准输出到显示器

        write(1, "\n", 2); 

        exit(0);

    } else { //父进程执行的代码

        close(fd[0]);//关闭父进程读端,留出写端

        write(fd[1], "hello,world!", 12);

        wait(NULL); //不需要知道子进程死亡状态,NULL

    }

    return 0;

}

"有名管道" //用的少,了解即可

有名管道通过文件实现进程间的通讯,两个进程不必具有亲缘关系。

"mkfifo"(3)

#include <sys/types.h>

#include <sys/stat.h>

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

功能:创建一个FIFO文件,用于进程间的通信

参数:

"pathname" 文件的路径/文件名

"mode" 文件的权限

返回值:

成功 - 返回 0

失败 - 返回 -1,errno被设置

/*举例验证,实现进程间的通信,通过有名管道实现 fifoA.c fifoB.c*/

/*fifoA.c*/

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

int main(void) {

    int r = mkfifo("hello", 0664);//创建有名管道

    if(r == -1) {

        perror("mkfifo");

        return 1;

    }   

    int fd = open("hello", O_WRONLY);//打开管道文件

    if(fd == -1) {

        perror("open");

        return 2;

    }   

    write(fd, "hello,world!", 12);

    sleep(5);

    close(fd);

    return 0;

}

/*fifoB.c*/

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

int main(void) {

    char buf[20] = {0};

    int fd = open("hello", O_RDONLY);//打开管道文件

    if(fd == -1) {

        perror("open");

        return 2;

    }   

    int size = read(fd, buf, 20);

    write(1, buf, size);

    write(1, "\n", 2); 

    close(fd);

    return 0;

}

三、文件重定向的实现 //鸟哥私房菜

运用进程管理、文件管理完成重定向的实现

"EOF" 使用库函数操作文件的时候,文件的结束标志

"toupper"(3)

#include <ctype.h>

int toupper(int c);

int tolower(int c);

功能:转换字母大小写格式 upper大写,lower小写

参数:"c" 字符

返回值:

成功 - 返回字符的值(ASCII码)

从键盘输入"ctrl + D" 代表了 "EOF" 文件结束。

/*实现代码参见 upper.c*/

#include <stdio.h>

#include <ctype.h>

int main(void) {

    int ch; 

    while((ch = getchar()) != EOF) { //EOF:文件结束标识

        putchar(toupper(ch));//全部输入转换大写

    }   

    return 0;

}

"标准输入重定向"

/*文件输入重定向,代码参见 wrapper.c*/

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

int main(int argc, char *argv[]) {

    int fd = open(argv[1], O_RDONLY);//打开文件

    if(fd == -1) {

        perror("open");

        return 1;

    }   

    dup2(fd, STDIN_FILENO);//将fd文件描述符拷贝给标准输入

    close(fd);//关闭文件

    execl("./upper", "upper", NULL);//在当前进程里加载upper的映像

    perror("execl");

    return 0;

}

补充:

"bash"

bash下有两种命令:

内部命令:命令的实现代码写在bash程序中(不许要fork创建子进程)

外部命令:命令的实现代码独立于bash程序代码(fork-execl-子进程)

查看命令的类型:type 命令 //如type ls  如type touch 如type cd

四、vfork的使用

vfork创建子进程以后,子进程和父进程共享PCB

vfork一般用于创建子进程后,在子进程中加载进程映像,执行新的程序。开辟了一条新的执行路线,并没有分配资源。

/*举例验证,子进程有父进程的进程映射的一份拷贝, fork_pcb.c */

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <sys/wait.h>

int main(void) {

    int var_i = 30; 

    pid_t pid = fork();

    if(pid < 0) {

        perror("fork");

        return 1;

    }   

    if(pid == 0) {

        printf("child var_i_1 = %d\n", var_i);//30

        var_i = 10; 

        printf("child var_i_2 = %d\n", var_i);//10

        exit(1);

    } else {

        wait(NULL);

        printf("parent var_i_1 = %d\n", var_i);//30

        var_i = 80; 

        printf("parent var_i_2 = %d\n", var_i);//80

    }   

    return 0;

}

"vfork"(2)

#include <sys/types.h>

#include <unistd.h>

pid_t vfork(void);

功能:创建一个子进程,并且阻塞父进程

参数:void 不需要参数

返回值:

成功 - 返回 0 是子进程,非 0 是父进程

失败 - 返回 -1,errno被设置

/* 代码参见 vfork.c */

#include <stdio.h>

#include <sys/types.h>

#include <unistd.h>

int main(void) {

    pid_t pid = vfork();//使用vfork创建子进程

    if(pid < 0) {//vfork出错

        perror("vfork");

        return 1;

    }   

    if(pid == 0) { //子进程执行的代码

        //子进程退出的时候不要使用exit和return

        printf("child pid %d\n", getpid());

        sleep(3);

        _exit(0);

    } else { //父进程执行的代码

        printf("parent pid %d\n", getpid());

    }   

    return 0;

}

/* 举例验证父进程局部变量的问题,vfork1.c*/

#include <stdio.h>

#include <unistd.h>

#include <sys/types.h>

int main(void) {

    int cnt = 10; 

    pid_t pid = vfork();

    if(pid < 0) {

        perror("vfork");

        return 1;

    }   

    if(pid == 0) { //子进程执行代码

        printf("cnt = %d\n", cnt);//10

        cnt++;

        printf("cnt++ = %d\n", cnt);//11

        _exit(0);

    } else { //父进程执行代码

        printf("cnt_p = %d\n", cnt);//11

        cnt++;

        printf("cnt_p++ = %d\n", cnt);//12

    }   

    return 0;

}

进程小结:

1)进程和程序的区别

2)每个进程都有自己的pid,还有自己的PCB

3)使用fork和vfork创建子进程

4)进程的同步(收尸:wait等待所有子进程/waitpid等待指定子进程)

5)加载新的程序映像

6)管道(无名管道,亲缘关系的进程通信-缓冲/有名管道,所有进程通信-文件)

7)环境变量(都是从父进程继承下来的)

8)使用文件和进程实现重定向的功能

五、信号的基础

"信号",就是软中断

"软中断",软件模拟中断的实现

"终端",中断处理程序

系统为我们提供的信号:

kill -l //查看系统提供的信号列表,总共提供了64个信号(实际62,无32,33)

1~31 不可靠信号(非实时信号)

34~64 可靠信号(实时信号)

信号有信号的名字和信号的编号。如 2) SIGINT

2) SIGINT 用户产生中断符(Ctrl + C)

3) SIGQUIT 用户产生退出符(Ctrl + \)

10) SIGUSR1 进程自定义的信号

12) SIGUSR2 进程自定义的信号

11) SIGSEGV 当程序运行产生段错误"segment fault"的时候,无效内存访问

9) SIGKILL 杀死进程

信号的处理过程:

"信号的产生" "信号阻塞" "信号的捕获" "信号的处理程序"

"信号的捕获"

当信号到达进程的时候,进程就捕获到了信号。进程需要对信号进行处理。

信号处理分为三种:

1)缺省处理("SIG_DFL"):大部分信号的缺省处理是使进程终止

2)信号忽略处理("SIG_IGN"):不用对到达的信号做任何的处理

3)用户自定义处理("自定"):信号的处理程序由用户自己定义

进程"默认从父进程继承信号处理"程序。

改变进程对信号的默认处理为自定处理。这时候需要用户向系统注册信号的处理函数。

当前进程怎么向系统注册信号的处理函数:

使用"signal"函数向系统注册。

"signal"(2)

#include <signal.h>

typedef void (*sighandler_t)(int); //函数指针(回调)

sighandler_t signal(int signum, sighandler_t handler);

功能:设置handler为信号signum的处理函数

参数:

"signum" 信号的编号或者信号名字

"handler" 信号的处理函数

返回值:

成功 - 返回以前的这个信号的处理函数 //以前的没用,可不用返回

失败 - 返回SIG_ERR,函数执行错误

/* 举例验证,用户自定义信号处理函数的注册,代码参见 signal.c*/

#include <stdio.h>

#include <signal.h>

void handler(int signum) { //用户定义的信号处理函数

    printf("hhhhhhh\n");

    return;

}

int main(void) {

    signal(2, handler); //向进程注册用户自定义的信号处理函数

    while(1);

    return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  编程 unix
相关文章推荐