进程

进程环境

进程的终止

  • 正常终止
    从main函数返回
    调用exit
    调用_exit或者_Exit
    最后一个线程从其启动例程返回
    最后一个线程调用pthread_exit

  • 异常终止
    调用abort
    接到一个信号并终止
    最后一个线程对其取消请求作出响应

钩子函数 atexit

  • int atexit(void (*function)(void));
    注册函数、函数将在程序正常终止后被调用
    All functions registered with atexit(3) and on_exit(3) are called,in the reverse order of their registration.

命令行参数的分析

  • int getopt(int argc, char * const argv[], const char *optstring);
    extern char *optarg;
    extern int optind, opterr, optopt; //optind(当前找到的下标)
    如果option被成功找到,返回option,否则返回-1
  1. 实现-y year -m math -d day -H hour -M minute -S second
    ./mydate -M -S
    ./mydate -MS
   int c;
    char fmtstr[SIZE];
    fmtstr[0] = '\0';
    c = getopt(argc, argv, "HMSymd");
    if (c < 0) {
        return;
    }
    swtich(c) {
    case 'H':
        strncat(fmtstr, "%H", SIZE);
        break;
    case 'M':
        strncat(fmtstr, "%M", SIZE);
        break;
    ....
    }
    strftime(timestr, TIMESIZE, fmtstr, tm);
  1. 实现带参选项
    ./mydate -y 4 -H 12
   c = getopt(argc, argv, "H:MSy:md");
    if (c < 0) {
        return;
    }
    swtich(c) {
    case 'H':
        if (strcmp(optarg, "12") == 0) {
            strncat(fmtstr, "%I(%p)", SIZE);
        } else if (strcmp(optarg, "24") == 0) {
            strncat(fmtstr, "%H", SIZE);
       } else {
            fprintf(strerr, "invalid argument");
       }
       break;
    ....
    }
  1. 解析命令行
while(1) {
    c = getopt(argc,argv,"lt-a"); // - 识别非选项的传参
    if (c < 0){
        break;
    }
    
    switch (c){
        case 'l':
            f.filesize = flen(argv[1]);
            strncat(fmtstr,"filesize:%ld ",FMTSTRSIZE-1);
            break;
        case 't':
            f.filetype = ftype(argv[1]);
            strncat(fmtstr,"filetype:%c ",FMTSTRSIZE-1);
            break;
        case 'a':
            PathParse(argv[optind]);
            break;
        default:
            break;
    }
}

C程序的存储空间布局

32位系统 虚拟空间4G (64位 128T)
4G---------------------
kernel
3G------ 0xc000000
argv、env



未初始化数据段
已初始化数据段
text代码段
0------------------------

函数跳转

适用场景:
在树结构中查找元素,找到后直接回到第一次调用处(跨函数),不用一层一层返回

  • setjmp() 长返回、长跳转,可以跨函数跳转
  • longjmp()

进程

PID

进程标识符 类型 pid_t,顺次向下使用,非优先使用当前范围可用最小的

  • ps axf 查看进程树
  • ps axm
  • ps ax -L 以linux特有方式查看
  • ps -ef
    pid_t getpid(void); 返回当前进程pid
    pid_t getppid(void);返回当前进程父进程pid

进程的产生

  • fork()
    通过复制当前进程来创建进程
    返回值:
    fork成功将在父进程返回子进程pid,在子进程返回0;fork失败将在父进程返回-1,无子进程创建。
    fork 后父子进程除以下几点其余都一样 :
    1)fork 的返回值不一样
    2)pid、ppid不同
    3)未决信号与文件锁不继承
    4)资源利用量清0

  • init进程 是所以进程的祖先进程 pid == 1

  • 调度器的调度策略来决定哪个进程先运行

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

int main()
{
    printf("%d start !\n",getpid());
    fflush(NULL);//记得刷新 否则begin放到缓冲区 父子进程的缓冲区里各有一句begin

    pid_t pid = fork();

    if (pid == 0){
        printf("child %d\n",getpid());
    }else{
        printf("parent %d\n",getpid());
    }
    getchar();
    printf("pid %d end\n",getpid());
    return 0;
}

注意点:

  • fork前要刷新该刷新的流:标准输出设备默认是行缓冲,’\n’刷新。文件默认是全缓冲,fllush刷新。
  • 子进程exit(0)结束
  • fork写时拷贝:fork时父子进程共用同一数据模块,若父子进程对数据库都只读不写,不会拷贝数据。如果有进程要改数据块,要改的数据的进程应该拷贝数据库,并修改拷贝后的数据块
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

#define LEFT 2
#define RIGHT 200

int main()
{
    pid_t pid = 0;
    int i,j,mark;

    for (i = LEFT;i <= RIGHT;i++){
        pid = fork();
        if (pid == 0){
            mark = 1;
            for (j = 2;j < i/2;j++){
                if (i%j == 0){
                    mark = 0;
                    break;
                }
            }
            if (mark) {
                printf("%d is a primer\n",i);
            }
            exit(0);
        }
    }
    getchar();

    exit(0);
}

进程的消亡以及释放资源

  • pid_t wait(int *wstatus);
    等待进程状态发生变化,wait成功返回终止的进程pid,失败返回-1

  • pid_t waitpid(pid_t pid, int *wstatus, int options);

  • wstatus:进程退出状态
    WIFEXITED(wstatus) 返回子进程是否正常结束,that is, by calling exit(3) or _exit(2), or by returning from main().
    WEXITSTATUS(wstatus) 返回子进程结束状态码。WIFEXITED为真时才可以用。
    WIFSIGNALED(wstatus) 返回子进程是否由信号终止。
    WTERMSIG(wstatus) 返回signal number。WIFSIGNALED为真时才可以用。
    WCOREDUMP(wstatus) 返回子进程是否产生了coredump。WIFSIGNALED为真时才可以用。

  • option:选项
    WNOHANG 如果没有子进程exit,立即返回
    WUNTRACED
    WCONTINUED (since Linux 2.6.10)

  • pid:返回值
    < -1 meaning wait for any child process whose process group ID is equal to the absolute value of pid.
    -1 meaning wait for any child process.任何子进程
    0 meaning wait for any child process whose process group ID is equal to that of the calling process.任何同进程组的子进程
    > 0 meaning wait for the child whose process ID is equal to the value of pid. 指定pid

waitpid(-1, &status, 0)相当于wait(&status)

进程分配

分块、交叉分配、池

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

#define N 3
#define LEFT 100000002
#define RIGHT 100000200

//交叉算法计算 池类算法涉及到竞争
int main()
{
    printf("[%d] start !\n",getpid());
    fflush(NULL);//记得刷新 否则begin放到缓冲区 父子进程的缓冲区里各有一句begin
    pid_t pid = 0;
    int i,j,mark;

    for (int n = 0;n < N;n++){
        pid = fork();
        if (pid < 0){
            perror("fork");
            for (int k = 0;k < n;k++){
                wait(NULL);
            }
            exit(1);
        }

        if (pid == 0){
            for (i = LEFT+n;i <= RIGHT;i+=N){
                mark = 1;
                for (j = 2;j <= i/2;j++){
                    if (i%j == 0){
                        mark = 0;
                        break;
                    }
                }
                if (mark) {
                    printf("%d is a primer\n",i);
                }
            }
            printf("[%d] exit\n",n);
            exit(0);
        }

    }

    int st,n;
    for (n =0 ;n < N;n++){
        wait(&st);
        printf("%d end\n",st);
    }

    exit(0);
}

exec函数族

exec
执行一个文件,替换当前进程映像, pid不变
extern char **environ;
int execl(const char *path, const char arg, … / (char *) NULL */);
int execlp(const char *file, const char arg, … / (char *) NULL */);
int execle(const char *path, const char arg, … /, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);

执行data +%s
puts("Begin");
fflush(NULL);
pid = fork();
if (pid < 0) {
    exit(1);
}
if (pid == 0) {
    execl("/bin/date", "date", "+%s", NULL);
    perror("execl()");
    exit(1);
}
wait(NULL);
puts("End");

如果execl正确执行的话,不应该返回
注意:execl前要刷新该刷新的流

  • system()
    int system(const char *command);
    执行一个命令,相当于fork+exec+wait

  • glob, globfree
    int glob(const char *pattern, int flags, int (*errfunc) (const char *epath, int eerrno), glob_t *pglob);
    void globfree(glob_t *pglob);
    glob函数搜索匹配pattern中的参数,如/*是匹配根文件下的所有文件(不包括隐藏文件,要找的隐藏文件需要从新匹配)
    pglob 存放匹配出的结果存放到 pglob
    flags 选择匹配模式,如是否排序 GLOB_NOSORT,或者在函数第二次调用时,是否将匹配的内容追加到pglob中 GLOB_APPEND
    eerrno 查看错误信息用,一般置为NULL

         typedef struct {
             size_t   gl_pathc;    /* Count of paths matched so far  */
             char   **gl_pathv;    /* List of matched pathnames.  */
             size_t   gl_offs;     /* Slots to reserve in gl_pathv.  */
         } glob_t;
    
  • strsep
    char *strsep(char **stringp, const char *delim);
    strsep返回值为分割后的开始字符串,并将函数的第一个参数指针指向分割后的剩余字符串。
    因为函数内部会修改原字符串变量,所以传入的参数不能是不可变字符串(即文字常量区),否则编译可以通过但是运行时会报段错误

shell命令实现

shell —fork() -----> shell(ls…) execl()
wait()

char *strtok(char *str, const char *delim);
char *strsep(char **stringp, const char *delim); //delim分隔符

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <wait.h>
#include <glob.h>
#include <string.h>

#define BUFSIZE 1024
#define DELIMS " \t\n"

extern char **environ;

static int parse(char *linebuf,glob_t *globres){
    char *tok;
    int flag = 0;
    
    while (1){
        tok = strsep(&linebuf,DELIMS);
        if (tok == NULL){
            break;
            return -1;
        } else if(strcmp(tok,"cd") == 0){
            char *path = strsep(&linebuf,DELIMS);
            return cd(path);
        }else if(tok[0] == '\0'){
            //判断返回值是否是空串
            continue;
        }

        glob(tok,GLOB_NOCHECK|GLOB_APPEND*flag,NULL,globres);
        //第一次不能append glob_argv中是随机值 GLOB_NOCHECK | (GLOB_APPEND*flag)==0 
        flag = 1;
    }
    return 1;
}

//之后记得 将 ctrl+c 转为 stdout:\n 将ctrl+d 转为 退出+再见标语
int main()
{
    pid_t pid;
    char *linebuf = NULL;
    size_t lienbuf_size = 0;
    glob_t globres;//解析命令行

    //读取配置文件
    //char *path = "/home/...";//填一个绝对路径
    //readrc(path);

    while(1){
        printf("mysh:");
        //获取命令
        getline(&linebuf,&lienbuf_size,stdin);
        //解析命令
        int ret = parse(linebuf,&globres);      
        if (ret == -1){
            
        }else if (ret == 0){//内部命令
            
        }else if (ret == 1){//外部命令
            fflush(NULL);
            pid = fork();
            if (pid < 0){
                perror("fork()");
                exit(1);
            }else if(pid == 0){
                execvp(globres.gl_pathv[0],globres.gl_pathv);
                perror("execl()");
                exit(1);
            }
        }
        waitpid(pid,NULL,0);
    } 
    
    exit(0);
}

用户权限以及组权限

  • u+s 当其他用户调用该可执行文件时,会切换成当前可执行文件的user的身份来执行

  • g+s
    chown root ./mysudo
    chmod u+s ./mysudo

  • getgid

  • getegid

  • setuid 设置effective

  • setgid 设置effective

  • setreuid 交换 r e //是原子的交换

  • setregid 交换 r e

守护进程

  • 会话 session 标识是sid
    pid_t setsid(void);
    创建一个会话,并设置进程组id,只能子进程调用
    调用的子进程会成为新会话的进程组leader PID = SID = PGID
    脱离控制终端 tty
    不需要父进程收尸(PPID = 1)

  • getpgrp() 返回当前进程所在的进程组id

  • getpgid() 返回指定进程PGID

  • setpgid()

  • tail -f /tmp/out 动态查看文件 kill 杀守护进程

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

#define FNAME "/tmp/out"

static int deamonize(void)
{
    int fd;
    pid_t pid;
    
    pid = fork();
    if (pid < 0) {
        perror("fork()";
        return -1;
    }
    if (pid > 1) {
        exit(0);
    }
    fd = open("/dev/null", O_RDWR);//输出都忽略
    if (fd < 0) {
        perror("open()");
        return -1;
    }
    //对0,1,2重定向
    dup2(fp, 0);
    dup2(fp, 1);
    dup2(fp, 2);
    if (fd > 2) {
        close(fd);//脱离终端
    }
    setsid();
    chdir("/");
    return 0;
}

int main()
{
    FILE* fp;

    //开启日志服务
    openlog("print i",LOG_PID,LOG_DAEMON);

    if (deamonize()){
        syslog(LOG_ERR,"init failed!");
    }else{
        syslog(LOG_INFO,"successded!");
    }

    fp = fopen(FNAME,"w+");
    if (fp == NULL){
        syslog(LOG_ERR,"write file failed!");
        exit(1);
    }

    syslog(LOG_INFO,"%s opened",FNAME);

    for(int i = 0; ;i++){
        fprintf(fp,"%d\n",i);
        fflush(NULL);
        syslog(LOG_DEBUG,"%d 写入",i);
        sleep(1);
    }

    closelog();
    fclose(fp);
    exit(0);
}

系统日志

  • syslogd 服务
    /var/log 配置文件 、etc/sysconfig syslog.cfg
  • void openlog(const char *ident, int option, int facility); 关联syslogd,facility消息来源
  • void syslog(int priority, const char *format, …);提交
  • void closelog(void);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值