回顾:
UC成长之路1
UC成长之路2
UC成长之路3
UC成长之路4
UC成长之路5
UC成长之路6
UC成长之路7
UC成长之路8
UC成长之路9
一、环境变量
- 每个进程都有自己的环境变量,以bash进程的环境为例来讲解。
- 如何查看bash的环境变量?使用
env
命令 - bash有私有变量(自定义变量)这种变量只能在当前进程使用,当前进程结束,这种变量的周期结束。
- 环境变量是可以被子进程继承的变量
- 系统维护着一个全局的变量,这个变量记录着环境变量列表的首地址,
extern char **environ;
eg:编写代码实现遍历进程的环境变量。代码参见tenv.c
,编译生成可执行文件tenv
#include <stdio.h>
int main(void)
{
extern char **environ;
int i;
for(i=0;environ[i]!=NULL;i++)
printf("%s\n", environ[i]);
return 0;
}
//以上的正真写法应该如下
#include <stdio.h>
int main(int argc, char* argv[], char *envp[])
{
int i;
for(i=0;envp[i]!=NULL;i++)
printf("%s\n", envp[i]);
return 0;
}
- 使用exec家族的函数结合环境变量举例
eg: exec_env.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void)
{
//創建一個子進程
pid_t pid = fork();
if(pid==-1){
perror("fork");
return -1;
}
if(pid==0){//子進程執行的代碼
//執行tenv可執行程序
execl("./tenv", "tenv", NULL);
}
else{//父進程執行的代碼
wait(NULL);//等待子進程結束
}
return 0;
}
//================================
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void)
{
char * const p_envp[] = {"key=value", NULL};
//創建一個子進程
pid_t pid = fork();
if(pid==-1){
perror("fork");
return -1;
}
if(pid==0){//子進程執行的代碼
//執行tenv可執行程序
execle("./tenv", "tenv", NULL, p_envp);
}
else{//父進程執行的代碼
wait(NULL);//等待子進程結束
}
return 0;
}
- 如何操作当前进程的环境变量?系统提供了一系列的函数:
getenv(3), setenv(3), unsetenv(3), putenv(3), clearenv(3)
#include <stdlib.h>
char *getenv(const char *name);
//功能:获取一个环境变量
//参数
//name:指定了要找的环境变量的名字
//返回值:失败返回NULL;成功返回环境变量的值的首地址
eg: 环境变量的操作
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *str="caption";
char* env=getenv(str);
if(!env){
printf("not found!!!\n");
return -1;
}
printf("%s=%s\n", str, env);
return 0;
}
#include <stdlib.h>
int setenv(const char *name, const char *value, int overwrite);
//功能:改变或增加一个环境变量
//参数
//name:指定环境变量的名字
//value:指定了环境变量的值
//overwrite:非0使用value的值更换原来环境变量的值;如果为0原来环境变量的值不改变
//返回值:成功返回0;错误返回-1,并设置errno
int unsetenv(const char *name);
//功能:删除指定的环境变量
//参数
//name:指定要删除环境变量的名字
//返回值:成功返回0;错误返回-1,并设置errno
eg:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *str="caption";
char* env=getenv(str);
if(!env){
printf("not found!!!\n");
return -1;
}
printf("%s=%s\n", str, env);
//向進程中增加name環境變量
str="name";
setenv(str, "tencent", 0);
env=getenv(str);
if(!env){
printf("not found!!!\n");
return -1;
}
printf("%s=%s\n", str, env);
//改變name環境變量的值,關鍵是overwrite的是0還是非0
//setenv(str, "google", 0);//1)overwrite=0
setenv(str, "google", 1);//2)overwrite=1
env=getenv(str);
if(!env){
printf("not found!!!\n");
return -1;
}
printf("after change %s=%s\n", str, env);
//刪除name環境變量
unsetenv(str);
env=getenv(str);
if(!env){
printf("not found %s!!!\n", str);
return -1;
}
printf("find %s=%s\n", str, env);
return 0;
}
#include <stdlib.h>
int clearenv(void);
//功能:清除环境变量,设置全局变量eviron为NULL
//返回值:成功返回0,失败返回非0
eg:
#include <stdio.h>
#include <stdlib.h>
extern char **environ;
int main(void)
{
char *str="caption";
char* env=getenv(str);
if(!env){
printf("not found!!!\n");
return -1;
}
printf("%s=%s\n", str, env);
//向進程中增加name環境變量
str="name";
setenv(str, "tencent", 0);
env=getenv(str);
if(!env){
printf("not found!!!\n");
return -1;
}
printf("%s=%s\n", str, env);
//改變name環境變量的值,關鍵是overwrite的是0還是非0
//setenv(str, "google", 0);//1)overwrite=0
setenv(str, "google", 1);//2)overwrite=1
env=getenv(str);
if(!env){
printf("not found!!!\n");
return -1;
}
printf("after change %s=%s\n", str, env);
int c = clearenv();
if(c){
printf("clearenv feild...\n");
}
if(!environ){
printf("clear seccuss!\n");
}
return 0;
}
#include <stdlib.h>
int putenv(char *string);//尽量不要使用,容易出错
//功能:增加或改变一个环境变量
//参数
//string:name=value,变量存在则改变其值,不存在则增加
//返回值:成功返回0,失败返回非0
eg:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern char **environ;
int main(void)
{
char buf[128];
char *str="name";
char* env=getenv(str);
if(!env){
printf("not found!!!\n");
return -1;
}
printf("%s=%s\n", str, env);
//1
strcpy(buf, "name=adair");
putenv(buf);
env=getenv(str);
if(!env){
printf("not found!!!\n");
return -1;
}
printf("%s=%s\n", str, env);
//2
strcpy(buf, "key=value");
putenv(buf);
env=getenv("key");
if(!env){
printf("not found!!!\n");
return -1;
}
printf("key=%s\n", env);
env=getenv(str);
if(!env){
printf("not found!!!\n");
return -1;
}
printf("%s=%s\n", str, env);
return 0;
}
二、管道
- 管道分为两种:无名管道和有名管道
- 无名管道:
#include <unistd.h>
int pipe(int pipefd[2]);
//功能:创建管道,
//参数
//int pipedf[2]:有两个整数的数组
//返回值:成功返回0;错误返回-1,errno被设置
//两个文件描述符,通过pipefd数组返回。
//pipefd[0]指向管道的读端,pipefd[1]指向管道的写端,
- 如何利用这个管道实现进程间通信?
- 使用pipe创建一个管道,返回两个文件描述符wfd、rfd
- 父进程创建子进程,子进程复制父进程的PCB,PCB中有文件描述符
- 子进程负责:向管道中写入内容,关闭读功能,子进程退出;父进程负责:从管道中读取内容,处理读取的数据,回收子进程资源。
- 要使用无名管道实现进程间的通讯,通讯的进程必须有亲缘关系(父子、兄弟关系)
步骤:
- 父进程创建管道
- 父进程创建子进程
- 父进程的责任
- 关闭管道写端
- 从管道中读取数据
- 对读取的数据处理
- 回收子进程的资源
- 子进程的责任
- 关闭管道读端
- 向管道写入数据
- 子进程退出
eg: pipe.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void)
{
int fd[2];
char buf[128];
char msg[]="today is clod!\n";
//1)創建管道
int pp = pipe(fd);
if(pp==-1){
perror("pipe");
return -1;
}
//2)創建子進程
pid_t pid = fork();
if(pid==-1){
perror("fork");
return -1;
}
if(pid==0){//子進程執行的代碼
close(fd[0]);//关闭读端
write(fd[1], msg, strlen(msg));
}
else{//父進程執行的代碼
close(fd[1]);//关闭写端
int n = read(fd[0], buf, 128);
write(1, buf, n);
wait(NULL);
}
return 0;
}
- 有名管道:实质是一个管道文件,进程间使用管道文件进行通讯。
- 管道文件只是起到进程间通讯的桥梁作用,这个文件并没有内容,尺寸是0.
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
//功能:参见一个有名管道
//参数:
//pathname:指定了管道文件的名字
//mode:指定了管道文件的权限
//返回值:成功返回0;错误返回-1,errno被设置
eg: fifo.c (fifo先进先出)
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char* argv[])
{
//創建管道
int pipe=mkfifo(argv[1], 0664);
if(pipe==-1){
perror("mkfifo");
return -1;
}
printf("makefifo %s success...\n", argv[1]);
return 0;
}
- 进程需要使用管道文件进行通讯
eg:程序pA.c打开管道文件hello,向管道文件写入消息;程序pB.c打开管道文件hello,从管道文件中读取消息,将读取到的消息输出到显示器。
pA.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char* argv[])
{
char *msg="today is so hot...\n";
int fd = open(argv[1], O_WRONLY);
if(fd==-1){
perror("open");
return -1;
}
write(fd, msg, strlen(msg));
close(fd);
return 0;
}
pB.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
char buf[128];
int fd = open(argv[1], O_RDONLY);
if(fd==-1){
perror("open");
return -1;
}
int n = read(fd, buf, 128);
write(1, buf, n);
close(fd);
return 0;
}
到这里进程结束
练习:编写代码实现bash的功能。编译生成可执行文件 tsh
提示:
#include <stdio.h>
int main(void)
{
char buf[128];
char *s;
while(1){
printf("%s$",getcwd(buf, 128));
s=gets(buf);
printf("%s\n", s);
}
return 0;
}
三、信号的基础
- 信号是软中断,软件模拟的中断机制就是软中断。
- 使用信号实现进程间的异步通讯。
- 系统为我们提供了哪些信号?使用命令:
kill -l
查看
- 信号是发送给进程的信号
- 信号会经过以下过程:信号的产生、未决状态、阻塞信号、信号的抵达、信号处理(信号捕获)
- 每个进程都会有对信号的默认处理函数,默认处理:终止当前进程。进程对信号的处理函数可以被子进程继承。SIG_DFL信号的默认处理函数。
- 程序员如何改变进程对信号的处理函数呢?使用signal(2)函数改变进程对信号的处理函数。
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
//功能:设置信号的处理函数
//参数
//signum:信号编号
//handler:有下面三种情况
//1)SIG_IGN:忽略
//2)SIG_DFL:默认的处理函数
//3)用户自定义的函数地址,用户可以自己编写符合用户的要求
//返回值:错误返回SIG_ERR; 成功返回原来的值
eg: 使用signal(2)设置进程信号的处理函数
#include <stdio.h>
#include <signal.h>
int main(void)
{
//設置進程忽略2號信號
signal(2, SIG_IGN);
while(1);
return 0;
}
#include <stdio.h>
#include <signal.h>
int main(void)
{
//設置進程忽略2號信號
signal(2, SIG_IGN);
signal(9, SIG_IGN);
while(1);
return 0;
}
#include <stdio.h>
#include <signal.h>
//信號處理函數
void doit(int n)
{
printf("receiver...%d\n", n);
return;
}
int main(void)
{
//設置進程忽略2號信號
signal(2, SIG_IGN);
signal(2, doit);
signal(9, SIG_IGN);
while(1);
return 0;
}
补充:Ctrl+c是2信号,Ctrl+\ 是3号信号
eg: 举例说明子进程继承父进程的信号处理函数
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
//信號處理函數
void doit(int n)
{
printf("receiver...%d\n", n);
return;
}
int main(void)
{
//設置進程忽略2號信號
signal(2, SIG_IGN);
signal(3, doit);
signal(9, SIG_IGN);
//創建子進程
pid_t pid = fork();
if(pid==-1){
perror("fork");
return -1;
}
if(pid==0){//子進程執行的代碼
printf("child pid is %d\n", getpid());
getchar();
exit(0);
}
else{//父進程執行的代碼
wait(NULL);//父進程等待子進程結束
}
return 0;
}