Chinaunix首页 | 论坛 | 博客
  • 博客访问: 351236
  • 博文数量: 112
  • 博客积分: 5245
  • 博客等级: 大校
  • 技术积分: 1120
  • 用 户 组: 普通用户
  • 注册时间: 2007-11-07 09:20
个人简介

静下来,定好方向,好好干。

文章分类
文章存档

2017年(1)

2012年(1)

2011年(5)

2010年(6)

2009年(16)

2008年(59)

2007年(24)

我的朋友

分类: LINUX

2007-11-09 21:46:10

系统调用, 产生软中断,从用户级别切换到特权级别, 程序跳转到中断处理函数; 分级管理, 内核态由操作系统管理,有利于安全控制;
程序是磁盘上的文件, 进程是内存里运行的程序;
程序的各段功能:
    代码段: 函数代码和变量;
    数据段: 全局变量, 静态变量;
    匿名anon空间: 堆空间, malloc使用的空间;
    stack栈: 局部变量;
操作系统实际就是一个大的中断处理函数, 主要做的事情:
    分配内存, 建立VA-PA转换表, 处理错误;  
mmu主要功能是地址转换和内存保护;
PCB中的信息有:
task_struct, mm_struct, pid, fd table, process_time, context, exit status, signals
程序真正的入口是_start, 进行必要的准备后调用main函数;
int main(int argc, char *argv[])   当中,argv的最后一项为NULL;

进程:
#include
void exit (int status);   ANSI_C定义的,会关闭文件然后退出;
#include
void _exit(int status);   POSIX1.1定义的,直接退出;

exit(n) 等同于 return(n);   n为指定的exit status;
exit status未定义的情况:
调用exit, _exit不带参数; main执行无返回值的return; main执行隐式返回, 无exit 或 return;

全局变量environ指向环境表, 使用方法如下:
#include
#include
extern char **environ;
int main()
{
     while (*environ)
          printf ("%s\n", *environ++);
}

环境变量操作函数:
#include
char *getenv(const char *name);   返回指向与name关联的value的指针, 未找到为NULL;
int putenv(const char *str);   返回成功为0,出错为非0; 参数形式:"name=value"; 若name存在,删除原来定义;
int setenv(const char *name, const char *value, int rewrite);   返回成功为0,出错为非0;
将环境变量name的值设置为value,若已经存在name, 若rewrite为0, 不删除原定义;若为非0, 删除原定义;
void unsetenv(const char *name);    删除name的定义,不存在name也不出错;

进程控制函数:
#include
#include
pid_t fork(void);   返回值子进程中为0,父进程中为子进程ID, 出错为-1;
fork失败的两个主要原因: 1.系统中有太多进程; 2.该用户ID的进程总数超过了系统限制;
#include
#include
pid_t wait(int *statloc);   调用者分配的内存, 若statloc不是空指针, 终止状态存在此处;
pid_t waitpid(pid_t pid, int *statloc, int options);
返回值, 成功为子进程的PID, 出错为-1;
option选项有:   WNOHANG   WUNTRACED   WCONTINUED
终止状态使用定义在中的宏 WEXITSTATUS(status) 来读取;
关于退出状态: WEXITSTATUS      可用命令grep 'WEXITSTATUS' /usr/include -nrs查看;
#define _WEXITSTATUS(status)   (((status) & 0xff00) >> 8)      define可用来代替一个简单函数;
wait和waitpid的区别:   wait在子进程终止前一直阻塞, 它等待第一个终止的子进程; 而waitpid可以控制阻塞或不阻塞, 并且等待的进程也可控制;

父进程fork出的子进程, 当子进程终止时, 父进程可以用wait或waitpid取得终止状态(若是正常退出,则取得exit函数的参数值,如果异常退出,由内核产生一个指示退出原因的状态值);
如果父进程在子进程之前终止,则他的子进程全部由init进程领养,reparent;
如果子进程在父进程之前终止,内核会为终止的子进程保持一定量的信息, 至少包括进程ID, 终止状态, 使用的CPU时间等信息, 父进程可用wait或waitpid取得, 若父进程(仍然在运行)不作处理, 则产生僵死进程zombie, 资源已经释放了, 但PCB还在, ps查看的状态为Z;
当一进程正常或异常终止时, 内核就向其父进程发出SIGCHLD信号. 系统的默认动作是忽略它;
wait或waitpid可能出现阻塞, 带子进程(已经终止)终止状态立即返回, 出错立即返回, 这么三种状态;

exec函数, 进程中每个打开的文件都有一个close-on-exec标志, 若有效则exec后关闭该描述符, 系统默认是无效;
#include
int execl(const char *pathname, const char * arg0, ...);
int execv(const char *pathname, char *const argv[]);
int execle(const char *pathname, const char *arg0, ...);
int execve(const char *pathname, char *const argv[], char * const envp[]);
int execlp(const char *filename, const char *arg0, ...);
int execvp(const char *filename, char *const argv[]);
若出错返回-1, 成功则不返回;
pathname是路径名, filename则分两种情况,1. filename中包含/, 则将其视为路径名,否则, 2. 按照PATH环境变量,搜寻可执行文件; 若文件是一个脚本文件, 则实际执行的程序是脚本解释器, 而不是脚本.
名称带l的, 表示参数传递是list方式, 并以空指针结尾;
名称带v的, 则应先构造一个指向各参数的指针数组作为参数;
名称带p的, 用PATH环境变量寻找可执行文件filename;
名称带e的, 表示该函数取envp[]数组, 而不是使用当前环境;

使用举例:
#include
char *const ps_argv[] = { "ps", "ax", 0};
char *const ps_envp[] = { "PATH=/bin:/usr/bin", "TERM=console", 0};
/*possible calls to exec function*/
execl("/bin/ps", "ps", "ax", 0);
execv("/bin/ps", ps_argv);
execle("/bin/ps", "ps", "ax", 0, ps_envp);
execve("/bin/ps", ps_argv, ps_envp);
execlp("ps", "ps", "ax", 0);
execvp("ps", ps_argv);

#include
int system(const char *cmdstring);
? 如果cmdstring是空指针, 若shell程序不可用时,返回0;   可用于判断本地系统是否支持system函数;
返回值:
1. fork失败或waitpid返回除EINTR之外的错误, 返回-1, errno中设置了错误类型;
2. exec失败, 表示不能执行shell, 返回exit(127);
3. 三个函数(fork, exec, waitpid)都成功, 返回是shell的终止状态, 格式与waitpid的statloc同;

管道:
pipe是UNIX IPC (InterProcess Communication)的一种古老机制, 它有两种限制:
1. 半双工, 数据只能在一个方向上流动;
2. 只能在具有公共祖先的进程间使用.如父 , 子进程之间使用;

#include
int pipe(int filedes[2]);  
成功返回0, 出错返回-1;    filedes参数返回2个文件描述符, fd[0]从管道读, fd[1]写入管道;

当管道的一端被关闭后, 下列规则起作用:
    当读一个写端被关闭的管道时, 所有数据被读取后, read返回0, 表示结束;
    当写一个读端被关闭的管道时, 产生信号SIGPIPE, write出错返回, errno设置为EPIPE.
写管道时, PIPE_BUF定义了内核中管道缓存的大小, 也就是一次性写的长度, 原子的写操作;

FIFO是命名管道, 不相关的进程也能交换数据.   以一种特殊的文件形式存在文件系统中, 文件类型为p;
#include
#include
int mkfifo(const char *pathname, mode_t mode);    创建一个FIFO文件, 若成功返回0, 出错返回-1;
mode参数含义与open函数中的mode相同;
创建之后, 其他进程只要知道这个文件的pathname, 就可以对这个文件进行读写来通信; FIFO在磁盘上只存储一个inode而没有数据块, 数据通过内核的缓冲区来传递;
mkfifo命令可以创建一个FIFO文件, 然后用重定向可以对其存取;
FIFO现在应用较少, 现在广泛使用的是UNIX Domain socket通信机制.

IPC通信机制汇总:
1. fork或exec, 父进程将已经打开的描述符传给子进程;
2. UNIX Domain socket, 应用较广泛;
3. 管道;
4 FIFO;
5. 几个进程读写某个共享文件, 可通过文件加锁来同步;
6. 进程间发信号, 一般使用SIGUSR1, SIGUSR2实现用户自定义功能;
7. mmap函数, 几个进程映射同一内存区域进行通信;
8. SYS V IPC, 消息队列, 信号量, 共享内存, 现在基本不用;

一些例程:
/*wait.c*/

#include
#include
#include
#include
#include

int main()
{
     pid_t pid;
     char *message;
     int n;
     int exit_code;

     printf("fork program starting\n");
     pid = fork();
     switch (pid)
     {
          case -1:
          perror("fork failed");
          exit(1);
          case 0:
          message = "This is the child";
          n = 5;
          exit_code = 37;
          break;
          default:
          message = "This is the parent";
          n = 3;
          exit_code = 0;
          break;
     }
     for(; n > 0; n--)
     {
          puts(message);
          sleep(1);
     }

     if (pid != 0)
     {
          int stat_val;
          pid_t child_pid;
          child_pid = wait(&stat_val);
          printf("Child has finished: PID = %d\n", child_pid);
          if (WIFEXITED(stat_val))
               printf("Child exited with code 0X%x\n",
                    stat_val);
          else
               printf("Child terminated abnormally\n");
     }
     exit(exit_code);
}

/*pipedemo.c    实验用管道实现进程间通信
* pipe得到的文件描述符通过exec参数传递,
* 或通过把pipe得到的描述符复制保存在STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO中, 再执行exec
*子进程中不能直接用pipe得到的描述符吗? 在执行exec前, 子进程可以使用.
*/

#include
#include
#include
#include

int main()
{
     int data_processed;
     int file_pipes[2];
     const char some_data[] = "123";
     char buffer[BUFSIZ + 1];
     pid_t fork_result;
     memset(buffer, '\0', sizeof(buffer));
     if (pipe(file_pipes) == 0)
     {
          fork_result = fork();
          if (fork_result == (pid_t)-1)
          {
               fprintf(stderr, "Fork failure");
               exit(EXIT_FAILURE);
          }
          if (fork_result == 0)
          {
               sprintf(buffer, "%d", file_pipes[0]);         //把数字转字符串
               (void)execl("pipe4", "pipe4", buffer, (char *)0);
               exit(EXIT_FAILURE);
          }
          else
          {
               data_processed = write(file_pipes[1], some_data,
                         strlen(some_data));
               wait();
               printf ("%d - wrote %d bytes\n", getpid(),
                         data_processed);
          }
     }
     exit(EXIT_SUCCESS);
}

/*pipe4.c*/

#include
#include
#include
#include

int main(int argc, char *argv[])
{
     int data_processed;
     char buffer[BUFSIZ + 1];
     int file_descriptor;
     memset(buffer, '\0', sizeof(buffer));
     sscanf(argv[1], "%d", &file_descriptor);         //把字符串转数字
     data_processed = read(file_descriptor, buffer, BUFSIZ);
     printf("%d - read %d bytes: %s\n", getpid(), data_processed,
               buffer);
     exit(EXIT_SUCCESS);
}


/*upper.c    包转程序调用upper程序,并不具体实现*/

#include
#include
#include

int main(int argc, char *argv[])
{
     int ch;
     printf ("%s\n", argv[0]);

     while ((ch = getchar()) != EOF)
     {
          putchar(toupper(ch));
     }
     exit(0);
}

/*wrapper.c*/

#include
#include
#include
#include

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

     if (argc != 2)
     {
          fprintf(stderr, "usage: wrapper file\n");
          exit(1);
     }
     filename = argv[1];

     in = open(filename, O_RDONLY);
     dup2(in, STDIN_FILENO);

     execl("./upper", "per", 0);
     perror("could not exec ./upper");
     exit(3);
}

/*toyshell.c 一个简单的shell程序*/

#include
#include
#include
#include
#include
#include

#define N 5 // the max option
char *cporder1[N]; 
char *cporder2[N]; 
char rdfile[30];
char wrfile[30];
char buf[100];

int getorder(char *cporder[], char *buf)
{
     char *cp = buf;
     int i, j, state;
    
     for (i = 0; i < N; i++)
     {
          state = 0;
          while (*cp == ' '|| *cp == '\t')
               cp++;
          if (*cp != ' ' && *cp != '\t' && *cp != '\n'
                    && *cp != '<' && *cp != '>')
               state = 1, cporder[i] = cp;
          while (*cp != ' ' && *cp != '\t' && *cp != '\n'
                    && *cp != '<' && *cp != '>')
               cp++;

          if (*cp == '\n' || *cp == '<' || *cp == '>')
          {
               if (*cp == '<')
               {
                    *cp = '\0';
                    cp++;
                    while (*cp == ' ' || *cp == '\t')
                         cp++;
                    for (j = 0; *cp != ' ' && *cp != '\t'
                              && *cp != '\n' && *cp != '>';j++)
                         rdfile[j] = *cp++;    
               }
               if (*cp == '>')
               {
                    *cp = '\0';
                    cp++;
                    while (*cp == ' ' || *cp == '\t')
                         cp++;
                    for (j = 0; *cp != ' ' && *cp != '\t'
                              && *cp != '\n' && *cp != '<';j++)
                         wrfile[j] = *cp++;    
               }
               if (*cp == '\n')
               {
                    if (0 == state)
                    {
                         *cp = '\0';
                         cporder[i] = NULL;
                         break;
                    }
               }
          }
          else
               *cp++ = '\0';
     }
     return i;
}
    
int main()
{
     char *cp;
     char *splitcp, *sbuf1, *sbuf2;
     int pid, n;
     int fd1, fd2;
     int file_pipes[2];
     pid_t fork_result, fork_result1;
    
     printf ("hello, you are welcome!\n");
     while (1)
     {
          //printf ("\n");
          printf ("akae@root# ");
          memset (buf, '\0', sizeof(buf));
          memset (rdfile, '\0', sizeof(rdfile));
          memset (wrfile, '\0', sizeof(wrfile));
          fd1 = fd2 = -1;
          if (fgets(buf, 50, stdin))
          {
                           
               if (strcmp(buf, "quit\n") == 0)
                   printf ("thank you, byebye!!!\n"), exit(0);

               if ((splitcp = strstr(buf, "|")) != NULL)
               {
                    *splitcp++ = '\n';
                    sbuf1 = buf;
                    sbuf2 = splitcp;
                    getorder(cporder1, sbuf1);
                    getorder(cporder2, sbuf2);
                   
                    if (pipe(file_pipes) == 0)
                    {
                         fork_result = fork();
                         if (fork_result == (pid_t)-1)
                         {
                              fprintf(stderr, "Fork failure");
                              exit(EXIT_FAILURE);
                         }
                         if (fork_result == 0)
                         {
                              if (*wrfile != '\0')
                              {
                                   fd2 = open(wrfile, O_WRONLY |
                                             O_CREAT,S_IRUSR | S_IWUSR);
                                   if (fd2 == -1)
                                   printf("Cannot open %s\n",wrfile);
                                   else
                                        dup2(fd2, STDOUT_FILENO);
                              }

                              close(file_pipes[1]);
                              dup2(file_pipes[0], STDIN_FILENO);
                              execvp(cporder2[0], cporder2);
                              exit(EXIT_FAILURE);
                         }
                         else
                         {
                              fork_result1 = fork();
               
                              if (fork_result1 == (pid_t)-1)
                              {
                                   fprintf(stderr, "Fork failure");
                                   exit(EXIT_FAILURE);
                              }
                              if (fork_result1 == 0)
                              {
                                   if (*rdfile != '\0')
                                   {
                                   fd1 = open(rdfile, O_RDWR);
                                   if (fd1 == -1)
                                   printf("Cannot open %s\n",rdfile);
                                   else
                                        dup2(fd1, STDIN_FILENO);
                                   }

                                   close (file_pipes[0]);
                                   dup2(file_pipes[1], STDOUT_FILENO);
                                   execvp(cporder1[0],cporder1);
                                   exit(EXIT_FAILURE);
                              }
                              else
                              {
                                   close(file_pipes[0]);
                                   close(file_pipes[1]);
                                   waitpid(fork_result, NULL, 0);
                                   waitpid(fork_result1, NULL, 0);
                              }
                         }
                    }
               }    
               else
               {
                    n = getorder(cporder1, buf);
                    pid = fork();
                    switch (pid)
                    {
                         case -1:
                              perror("fork failed");
                              exit(1);
                              break;
                         case 0:
                              if (*rdfile != '\0')
                              {
                                   fd1 = open(rdfile, O_RDWR);
                                   if (fd1 == -1)
                                   printf("Cannot open %s\n",rdfile);
                                   else
                                        dup2(fd1, STDIN_FILENO);
                              }
                              if (*wrfile != '\0')
                              {
                                   fd2 = open(wrfile, O_WRONLY |
                                             O_CREAT,S_IRUSR | S_IWUSR);
                                   if (fd2 == -1)
                                   printf("Cannot open %s\n",wrfile);
                                   else
                                        dup2(fd2, STDOUT_FILENO);
                              }
                              execvp (cporder1[0], cporder1);
                              exit(EXIT_FAILURE);
                              break;
                         default:
                              wait(NULL);
                              break;
                    }
               }
          }
     }
}

点滴信息:
nm a.out 查看符号表
虚拟地址是编译时决定的, 内核地址占1G;
man bash-buildins查看内建命令
0进程启动内核, 1进程启动;
$echo $?   可以获取上一命令的退出状态;


阅读(1120) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~