LinuxC应用开发学习笔记(十)—多进程

进程

1、进程标识符 pid

类型 pid_t
命令 ps
进程号是顺次向下使用
getpid();
getppid();

2、进程的产生

(1)fork();
	注意理解关键字:duplicating 意味着拷贝、克隆、一模一样等含义
	fork后父子进程的区别:fork的返回值不一样,pid不同,ppid不同,未决信号和文件锁不继承,资源利用量归0.
	init进程:1号,是所有进程的祖先进程。
2022.1.3
1、永远不要猜测父子进程谁先调度,调度器的调度策略来决定哪个进程先运行。
2、ps axf 调用之后 可以查看到进程关系,顶格写的才是1号init进程。
3、刷新所有成功打开的流。
(2)vfork();
	读时共享,写时拷贝,基本被废弃了,被整合到了fork。
进程创建实例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    __pid_t pid;
    /*1、永远不要猜测父子进程谁先调度,调度器的调度策略来决定哪个进程先运行*/
    printf("[%d]Begin!\n",getpid());

    /*3、刷新所有成功打开的流*/
    fflush(NULL);/*!!!在Begin之后没来得及放入全缓冲区文件中,就fork一次,因此会输出2次Begin*/

    pid = fork();
    if (pid<0)
    {
        perror("fork()");
        exit(1);
    }
    if (pid == 0)  //child
    {
         printf("[%d]:child is working!\n",getpid());
    }
    if (pid > 0)  //parent
    {
         printf("[%d]:parent is working!\n",getpid());
    }
    
    /*2、ps axf 调用之后 可以查看到进程关系,顶格写的才是1号init进程*/
    //getchar();

    printf("[%d]End!\n",getpid());
    exit(0);
}
质数版本1—进程的创建fork
僵尸未收尸,变成孤儿进程,被init接管。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define LEFT  30000000
#define RIGHT 30000200

int main()
{
    long long i,j,mark;
    __pid_t pid ; 
    
    for (i = LEFT; i <=RIGHT; i++)
    {
        pid = fork();
        if (pid<0)
        {
            perror("fork()");
            exit(1);
        }
        if (pid == 0)  //child
        {
            mark = 1;
            for (j = 2; j < i/2; j++)
            {
                if (i%j == 0)
                {
                    mark = 0;
                    break;
                }
            }
            if (mark)
                printf("%lld is a primer\n",i);
            //sleep(1000);//父进程退出,子进程全部变成孤儿进程,init接管孤儿进程。
            exit(0);//进程结束
        }
    }
    sleep(1000);
    exit(0);
}

3、进程的消亡及释放资源

wait();//死等,阻塞性 相当于wait(-1,&status,0);的封装
waitpid(pid_t pid,int *status,int options);//有options可以让waitpid成为非阻塞模式。

分块法:把需要的200个进程分为N份。

进程分配之交叉分配法的实现。

一般交叉分配法比分块法更优秀。
	
waitid();
wait3();
wait4();
质数版本2—线程的回收wait
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

#define LEFT  30000000
#define RIGHT 30000200

int main()
{
    int i,j,mark;
    __pid_t pid ;
    for (i = LEFT; i <=RIGHT; i++)
    {
        pid = fork();
        if (pid<0)
        {
            perror("fork()");
            exit(1);
        }
        if (pid == 0)  //child
        {
            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),否则系统一直创建进程,直到创建满为止
            exit(0);//进程结束,退出进程
        }
    }
    for (i = LEFT; i <= RIGHT; i++)
        wait(NULL);//不关心回收进程的状态,直接全部收尸。
    exit(0);
}
质数版本3–交叉分配法
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

#define LEFT  30000000
#define RIGHT 30000200
#define N     3

int main()
{
    int i,n,j,mark;
    __pid_t pid ; 
    //创建N个进程
    for (n = 0; n < N; n++)
    {
        pid =  fork();
        if (pid<0)
        {
            perror("fork()");
            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] %d is a primer\n",n,i);
            }
            exit(0);//进程结束
        }
    }
    for (n = 0; n <N; n++)
        wait(NULL);   
    exit(0);
}

4、exec函数族

execl();
execlp();
execle();
execv();//变参函数
execvp();//变参函数

注意fflush()
exec函数实例

pid不变。

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

int main()
{
    puts("Begin!");
    /*在exec之前,刷新缓冲区*/
    fflush(NULL);

    execl("/bin/date","date","+%s",NULL);
    perror("execl()");
    exit(1);

    puts("End!");
    
    exit(0);
}
fork,exec,wait联合说明
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

int main()
{
    pid_t pid;

    puts("Begin!");

    fflush(NULL);

    pid = fork();
    if (pid<0)
    {
        perror("fork()");
        exit(1);
    }
    if (pid == 0)
    {
        execl("/bin/date","date","+%s",NULL);
        perror("execl()");
    }
    
    wait(NULL);

    puts("Begin!");
    
    exit(0);
}
自制shell–P174–命令实现
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <glob.h>

#define  DELIMS " \t\n" 
struct cmd_st
{
    glob_t globres;
};

static void prompt(void)
{
    printf("mysh-0.1$");
}
static  void parse(char *line,struct cmd_st *res)
{
    char *tok;
    glob_t globres;
    int i = 0;

    while (1)
    {
        tok =  strsep(&line,DELIMS);
        if (tok == NULL)
            break;
        if (tok[0] == '\0')
            continue;
    	glob(tok,GLOB_NOCHECK|GLOB_APPEND*i,NULL,&res->globres);
        i = 1;    
    }
}

int main()
{
    char *linebuf = NULL;

    size_t linebuf_size = 0;

    glob_t globres;

    struct cmd_st cmd;

    pid_t pid;

    while (1)
    {
       prompt();

       if(getline(&linebuf,&linebuf_size,stdin))
            break;

       parse(linebuf,&cmd);

       if (0)//内部命令
       {
       }
       else  //外部命令
       {
            pid = fork();
            if(pid<0)
            {    
            }
            if(pid==0)          //children
            {              execvp(cmd.globres.gl_pathv[0],cmd.globres.gl_pathv);
                perror("execvp()");
                exit(1);
            }
            else            //parent
            {
                wait(NULL);
            }
       }
   }  
    exit(0);
}

5、用户权限以及组权限(u+s,g+s)

如果一个可执行文件拥有u+s 权限,那么当别的用户调用当前可执行权限的文件,它的权限会变成当前user权限来执行。

如果一个可执行文件拥有g+s 权限,那么当别的用户调用当前可执行权限的文件,它的权限会变成当前同组身份权限来执行。
!!!慎重下放权限。

getuid();//获取当前进程id
geteuid();//获取当前进程有效id
getgid();//获取当前进程组id
getegid();//获取当前进程有效组id

setuid();//设置进程id
setgid();//设置进程组id

setreuid();//交换real effective uid(原子化的交换)
setregid();//交换real effective gid(原子化的交换)

seteuid();//更改有效用户id
setegid();//更改有效组id
自制sudo—P177,10min
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <glob.h>

int main(int argc,char **argv)
{
    pid_t pid;
    if (argc < 3)
    {
        fprintf(stderr,"Usage...\n");
        exit(1);
    }
    pid = fork();
    if (pid < 0)
    {
        perror("fork()");
        exit(1);
    }
    if (pid == 0)//子进程
    {
        setuid(atoi(argv[1]));//设置有效id

        execvp(argv[2],argv+2);//变参execvp

        perror("execvp()");

        exit(1);
    }
    if (pid > 0)
    {   
    }
    exit(0);
}

6、观摩课:解释器文件(脚本文件)

脚本文件
#!/bin/cat

ls
whoami
ps

7、system()

理解成  fork、exec和wait的封装
系统函数0–执行一个shell命令
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
   system("date +%s > /tmp/out");
   exit(0);
}
系统函数1
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

int main()
{
    pid_t pid;
    
    pid = fork();
    if (pid<0)
    {
        perror("fork()");
        exit(1);
    }
    if (pid == 0)//子进程执行操作
    {
        execl("/bin/sh","sh","-c","date","+%s",NULL);
        perror("execl()");
    }
    
    wait(NULL);
    
    exit(0);
}

8、进程会计

acct();进程结束填充字段、不可移植

9、进程时间

times();获取进程时间,结构体。

10、守护(精灵)进程

会话 session(),标识 sid
终端
setsid(); //只能是非leader 的进程调用
getpgid();
setpgid();
单实例守护进程:锁文件 /var/run/name.pid
启动脚本文件:/etc/rc*...
守护进程日志实例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <glob.h>
#include <syslog.h>
#include <errno.h>

#define FNAME "tmp/out" 

static int daemonize()
{
    pid_t pid;
    int fd;
    pid = fork();
    if (pid<0)
        return -1;
            
    if (pid>0)  //parent
    {
        exit(0);
    }
    fd = open("/dev/null",O_RDWR);
    if (fd<0)
        return -1;

    dup2(fd,0);
    dup2(fd,1);
    dup2(fd,2);
    if (fd > 2)
        close(fd);
   
    setsid();
    chdir("/");
    return 0;
}

int main()
{
    FILE *fp;
    int i;
    
    if (daemonize())
    {
        exit(1);
    }
    else
    {
    
    }
    
    fp = fopen(FNAME,"w");
    if (fp == NULL)
    {
    		 perror("fopen()");
        exit(1);
    }

    for (i = 0; ; i++)
    {
        fprintf(fp,"%d\n",i);
        fflush(fp);
        sleep(1);
    }
    
    fclose(fp);
    
    closelog();
    
    exit(0);
}

11、系统日志书写

syslog服务
openlog()
syslog()
closelog()
进程日志实例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <glob.h>
#include <syslog.h>
#include <errno.h>

#define FNAME "tmp/out" 

static int daemonize()
{
    pid_t pid;
    int fd;
    pid = fork();
    if (pid<0)
        return -1;

    
    if (pid>0)  //parent
    {
        exit(0);
    }
    fd = open("/dev/null",O_RDWR);
    if (fd<0)
        return -1;

    dup2(fd,0);
    dup2(fd,1);
    dup2(fd,2);
    if (fd > 2)
    {
        close(fd);
    }
    
    setsid();
    chdir("/");
    return 0;
}


int main()
{
    FILE *fp;
    int i;

    openlog("mydaemon",LOG_PID,LOG_DAEMON);

    if (daemonize())
    {
        syslog(LOG_ERR,"daeonize() failed!");
        exit(1);
    }
    else
    {
        syslog(LOG_INFO,"daemonize() successded!"); 
    }
    
    fp = fopen(FNAME,"w");
    if (fp == NULL)
    {
        syslog(LOG_ERR,"fopen():%s",strerror(errno));
        exit(1);
    }

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

    for (i = 0; ; i++)
    {
        
        fprintf(fp,"%d\n",i);
        fflush(fp);
        syslog(LOG_DEBUG,"%d is printed",i);
        sleep(1);
    }
    
    fclose(fp);
    closelog();
    
    exit(0);
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值