查了好多资料,发现还是不全,干脆自己整理吧,至少保证在我的做法正确的,以免误导读者,也是给自己做个记录吧!
学号:sa×××310 姓名:××涛
环境:Ubuntu13.04 gcc4.7.3
1.进程管理
Linux中的进程主要由kernel来管理。系统调用是应用程序与内核交互的一种方式。系统调用作为一种接口,通过系统调用,应用程序能够进入操纵系统内核,从而使用内核提供的各种资源,比如操纵硬件,开关中断,改变特权模式等等。
罕见的系统调用:exit,fork,read,write,open,close,waitpid,execve,lseek,getpid...
用户态和内核态
为了使操纵系统提供一个很好的进程抽象,限制一个程序可以执行的指令和可以拜访的地址空间。
处置器平日是使用某个控制寄存器中的一个模式位来提供这类功能,该寄存器描述了进程以后享有的特权。当设置了模式位时,进程就运行在内核态,可以执行指令会合的任何指令,并且可以拜访系统中任何存储器位置。
没有设置模式位时,进程就运行在用户态,不允许执行特权指令,也不允许直接引用地址空间中内核区内的代码和数据。任何这样的实验都市致使致命的掩护故障,反之,用户程序必须通过系统调用接口间接地拜访内核代码和数据。
关于fork的分析,拜见。
waitpid
首先来了解一下僵尸进程,当一个进程由于某种原因终止时,内核并非当即把它从系统中清除。相反,进程被保存在一种已终止的状态中,直到它的夫进程回收。当父进程回收已终止的子进程时,内核将子进程的退出状态传递给父进程,然后摈弃已终止的进程,从此时开始,该进程就不存在了。一个终止了但还未被回收的进程称为僵尸进程。
如果父进程没有回收子进程就终止了,子进程就成了僵尸进程,即时没有运行,但仍然消耗系统的存储器资源。
一个进程可以通过调用waitpid函数来等待它的子进程终止或是停止。
函数原型如下:
pid_t waitpid(pid_t pid, int *status, int options)
如果成功,则为子进程的PID,如果WNOHANG,则为0,如果其他错误,则为-1.
看一个waitpid函数的例子。
#include"csapp.h"#include#define N 5int main(){ int status, i; pid_t pid; for(i=0; i 0) { if(WIFEXITED(status)) printf("Child %d exited normally with status=%d!\n",pid,WIFEXITED(status)); else printf("Child %d terminated abnormally!\n",pid); } if(errno != ECHILD) unix_error("waitpid error\n"); return 1;}
运行结果
waitpid的第一个参数是-1,则等待集合由父进程的所有子进程构成。大于0的话就是等待进程的pid。
waitpid的第三个参数是-1,则waitpid会挂起调用进程的执行,直到它的等待集合的一个子进程终止。如果等待集合中的一个进程终止了,那么waitpid就当即返回。
程序运行的结果就是waitpid函数不按照特定的次序回收僵死的子进程。
提一下wait函数,它就是waitpid函数的简单版本,原型如下:
pid_t wait(int *status)
等价于waitpid(-1, &status, 0)
execve
在Linux中要使用exec函数族来在 一个进程中启动另一个程序。系统调用execve()对以后进程停止替换,替换者为一个指定的程序,其参数包括文件名(filename)、参数列表(argv)以及环境变量(envp)。exec函数族当然不止一个,但它们大致雷同,在 Linux中,它们分别是:execl,execlp,execle,execv,execve和execvp,下面我只以execve为例,其它函数究竟与execlp有何区分,请通过man exec命令来了解它们的具体情况。
一个进程一旦调用exec类函数,它本身就"死亡"了,系统把代码段替换成新的程序的代码,放弃原有的数据段和堆栈段,并为新程序分配新的数据段与堆栈段,独一留下的,就是进程号,也就是说,对系统而言,还是同一个进程,不过已经是另一个程序了。(不过exec类函数中有的还允许继承环境变量之类的信息。)原型如下:
int execve(const char *filename, const char *argv[], const char *envp[]);
成功调用不会返回,犯错返回-1.
execve函数加载并运行可执行目标文件filename,且带参数列表argv和环境变量列表envp.只有当涌现错误时,例如找不到filename,execve才会返回到调用程序。所以,与fork一次调用返回两次,execve调用一次并从不返回。
argv的在内存中组织方式如下图:
argv[0]是可执行目标文件的名字。
envp的在内存中组织方式如下图:
环境变量的列表是由一个和指针数组类似的数据结构表示,envp变量指向一个以null开头的指针数组,其中每个指针指向一个环境变量串,其中每个串都是形如“NAME=VALUE”的键值对。
可以用下面的命令来打印命令行参数和环境变量:
#include"csapp.h"int main(int argc, char *argv[], char *envp[]){ int i; printf("Command line arguments:\n"); for(i=0; argv[i]!=NULL; i++) printf("argv[%2d]: %s\n", i, argv[i]); printf("\n"); printf("Environment variables:\n"); for(i=0; envp[i]!=NULL; i++) printf("envp[%2d]: %s\n", i, envp[i]); exit(0);}
2.简单的shell
#include#include"csapp.h"#define MAXARGS 128void eval(char *cmdline);int parseline(char *buf,char **argv);int builtin_command(char **argv);int main(){ char cmdline[MAXLINE]; while(1) { printf("> "); Fgets(cmdline,MAXLINE,stdin); if(feof(stdin)) exit(0); eval(cmdline); } //printf("Hello\n"); return 1;}int builtin_command(char **argv){ if(!strcmp(argv[0],"quit")) exit(0); if(!strcmp(argv[0],"&")) return 1; if(!strcmp(argv[0],"-help")) { printf("-help help infomation.\n"); printf("ls list files and folders of current path.\n"); printf("pwd show current path.\n"); return 1; } if(!strcmp(argv[0],"pwd")) { printf("%s\n",getcwd(NULL,0)); return 1; } return 0;}void eval(char *cmdline){ char *argv[MAXARGS]; char buf[MAXLINE]; int bg; pid_t pid; strcpy(buf, cmdline); bg = parseline(buf, argv); if(argv[0] ==NULL) return; if(!builtin_command(argv)) { if((pid = Fork()) == 0) { if(execve(argv[0],argv,environ) < 0) { printf("%s:Command not found.\n",argv[0]); exit(0); } } if(!bg) { int status; if(waitpid(pid,&status,0)<0) unix_error("waitfg:waitpid error"); } else printf("%d %s",pid, cmdline); } return;}int parseline(char *buf, char **argv){ char *delim; int argc; int bg; buf[strlen(buf)-1]=' '; while(*buf && (*buf==' ')) buf++; argc = 0; while((delim = strchr(buf,' '))) { argv[argc++] = buf; *delim = '\0'; buf = delim + 1; while(*buf && (*buf==' ')) buf++; } argv[argc] = NULL; if(argc == 0) return 1; bg = (*argv[argc-1] == '&'); if(bg !=0) argv[--argc] = NULL; return bg; }
#include实现思路:#include #include #include #include #include #include #include #include void do_ls(char[]);void dostat(char *);void show_file_info(char *,struct stat *);void mode_to_letters(int,char[]);char * uid_to_name(uid_t);char * gid_to_name(gid_t);void main(int argc,char *argv[]){ if(argc==1) do_ls("."); else printf("Error input\n");}void do_ls(char dirname[]){ DIR *dir_ptr; //Path struct dirent *direntp; //Struct to save next file node if((dir_ptr=opendir(dirname))==0) fprintf(stderr,"ls:cannot open %s\n",dirname); else{ while((direntp=readdir(dir_ptr))!=0) dostat(direntp->d_name); closedir(dir_ptr); }}void dostat(char *filename){ struct stat info; if(lstat(filename,&info)==-1) perror("lstat"); else show_file_info(filename,&info);}void show_file_info(char *filename,struct stat *info_p){ char modestr[11]; mode_to_letters(info_p->st_mode,modestr); printf("%-12s",modestr); printf("%-4d",(int)info_p->st_nlink); printf("%-8s",uid_to_name(info_p->st_uid)); printf("%-8s",gid_to_name(info_p->st_gid)); printf("%-8ld",(long)info_p->st_size); time_t timelong=info_p->st_mtime; struct tm *htime=localtime(&timelong); printf("%-4d-%02d-%02d %02d:%02d",htime->tm_year+1990,htime->tm_mon+1,htime->tm_mday,htime->tm_hour,htime->tm_min); printf(" %s\n",filename);}//cope with permissionvoid mode_to_letters(int mode,char str[]){ strcpy(str,"----------"); if(S_ISDIR(mode)) str[0]='d'; if(S_ISCHR(mode)) str[0]='c'; if(S_ISBLK(mode)) str[0]='b'; if(mode & S_IRUSR) str[1]='r'; if(mode & S_IWUSR) str[2]='w'; if(mode & S_IXUSR) str[3]='x'; if(mode & S_IRGRP) str[4]='r'; if(mode & S_IWGRP) str[5]='w'; if(mode & S_IXGRP) str[6]='x'; if(mode & S_IROTH) str[7]='r'; if(mode & S_IWOTH) str[8]='w'; if(mode & S_IXOTH) str[9]='x';}//transfor uid to usernamechar * uid_to_name(uid_t uid){ struct passwd *pw_str; static char numstr[10]; if((pw_str=getpwuid(uid))==NULL){ sprintf(numstr,"%d",uid); return numstr; } else return pw_str->pw_name;}//transfor gid to usernamechar * gid_to_name(gid_t gid){ struct group *grp_ptr; static char numstr[10]; if((grp_ptr=getgrgid(gid))==NULL){ sprintf(numstr,"%d",gid); return numstr; } else return grp_ptr->gr_name;}
3.信号
#include看一个接受信号的例子:typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);Returns: ptr to previous handler if OK, SIG_ERR on error (does not set errno)
#include "csapp.h"/* SIGINT handler */void handler(int sig){ return; /* Catch the signal and return */}unsigned int snooze(unsigned int secs) { unsigned int rc = sleep(secs); printf("Slept for %u of %u secs.\n", secs - rc, secs); return rc;}int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "usage: %s程序解析:\n", argv[0]); exit(0); } if (signal(SIGINT, handler) == SIG_ERR) /* Install SIGINT handler */ unix_error("signal error\n"); (void)snooze(atoi(argv[1])); exit(0);}
4.动态链接和静态链接
5.ELF文件格式与进程地址空间的联系
6.参考
文章结束给大家分享下程序员的一些笑话语录: 与女友分手两月有余,精神萎靡,面带菜色。家人介绍一女孩,昨日与其相亲。女孩果然漂亮,一向吝啬的我决定破例请她吃晚饭。
选了一个蛮贵的西餐厅,点了比较贵的菜。女孩眉开眼笑,与我谈得很投机。聊着聊着,她说:“我给你讲个笑话吧。”“ok” “一只螳螂要给一只雌蝴蝶介绍对象,见面时发现对方是只雄蜘蛛。见面后螳螂问蝴蝶‘如何?’,‘他长的太难看了’,‘别看人家长的丑,人家还有网站呢’。” “呵呵………”我笑。忽然她问:“你有网站吗?”--------------------------------- 原创文章 By
进程和信号---------------------------------