Linux进程2

本文详细介绍了Linux进程中的守护进程创建流程与实例,包括如何脱离终端并重定向I/O。此外,还深入探讨了进程中程序替换的system函数和exec函数族,提供多个实例说明其使用。最后,讲解了多线程的概念、创建方法及执行特性,包括线程的内存空间问题、pthread函数的使用等。
摘要由CSDN通过智能技术生成

目录

1.守护进程(daemon)

1.1什么是守护进程

1.2守护进程创建的流程

1.3守护进程的实例

2.进程中程序替换函数

2.1system函数

2.1.1system函数接口介绍

2.1.2system函数使用实例

2.2exec函数族

2.2.1execl/execv函数API介绍

2.2.2execl/execv函数实例

2.2.3execlp/execvp函数API介绍

2.2.4execlp/execvp函数实例

2.2.5execle/execvpe函数API介绍

2.2.6execle/execvpe函数实例

3.多线程

3.1线程的概念

3.2线程创建API接口的介绍

3.3多线程创建实例(不传递参数)

3.4多线程创建实例(传递参数)

3.5多线程执行的顺序

3.6多线程内存空间问题

3.7多线程pthread_self函数

3.8多线程pthread_exit函数

3.9多线程pthread_join函数

3.10多线程pthread_cancel函数

3.11多线程pthread_detach函数


1.守护进程(daemon)

1.1什么是守护进程

守护进程是后台运行的进程,脱离某个终端,随着系统的启动而运行,

随着系统的终止而终止。例如:windows上的各种服务。

1.2守护进程创建的流程

  1. 创建一个孤儿进程

    (孤儿进程和终端脱离了,终端关闭孤儿进程不会结束)

  2. 设置孤儿进程的会话id和组id

    pid_t setsid(void);
    功能:设置一个进程的会话id和组id和pid相同
    参数:
        @无
    返回值:成功返回会话id,失败返回-1置位错误码
    
  3. 切换进程对应的路径为根路径

    int chdir(const char *path);
    功能:切换路径
    参数:
        @path:想切换到的路径
    返回值:成功返回0,失败返回-1置位错误
    
  4. 对标准输入、输出、出错重定向

    以后在进程内对标准输入、输出、出错重定向到日志文件中,如何对文件描述符进行重定向dup/dup2

    int dup(int oldfd);
    功能:拷贝oldfd生成newfd,新旧fd都能操作文件,它们共用光标
    参数:
        @oldfd:旧的文件描述符
    返回值:成功返回新的文件描述符(最小未使用原则),失败返回-1置位错误码
            
    
    int dup2(int oldfd, int newfd);
    功能:dup2()系统调用执行与dup()相同的任务,但它不是使
        用编号最低的未使用文件描述符,而是使用在newfd中指
        定的文件描述符编号。如果文件描述符newfd以前是打开
        的,那么在重用它之前会以静默方式关闭它。
    参数:
        @oldfd:旧的文件描述符
     @newfd:新的文件描述符
    返回值:成功返回新的文件描述符,失败返回-1置位错误码
    

    dup函数使用

    #include <head.h>
    
    int main(int argc,const char * argv[])
    {
        int ofd,nfd;
        char ch;
        if((ofd = open("./hello.txt",O_RDWR))==-1)
            PRINT_ERR("open file error");
    
        nfd = dup(ofd);
    
        printf("ofd = %d,nfd = %d\n",ofd,nfd);
     
        //通过改变oldfd的光标,能够影响newfd的光标
        lseek(ofd,5,SEEK_SET);
    
        read(nfd,&ch,1);
    
        printf("ch = %c\n",ch);
    
        close(ofd);
        close(nfd);
    
        return 0;
    }
    

    dup2实例

    #include <head.h>
    
    int main(int argc, const char* argv[])
    {
        int fd;
        char buf[] = "i am ok ................\n";
        if ((fd = open("./hello.txt", O_RDWR)) == -1)
            PRINT_ERR("open file error");
    
        //dup2会先将0 1 2三个文件描述符关闭掉
        //然后通过dup2进行文件描述符重定向
        //以后在对0 1 2操作的时候都是对fd对应的文件操作
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
    
        //将printf标准IO打印的数据写入到文件中,终端上不会显示
        printf("hello DC22071 everyone!!!!!\n");
        printf("hello DC22071 everyone!!!!!\n");
        printf("hello DC22071 everyone!!!!!\n");
        printf("hello DC22071 everyone!!!!!\n");
        fflush(stdout);
    
        fprintf(stderr,"12345678!!!!!!!!!!!!\n");
    
    
        write(1,buf,strlen(buf));
    
        close(fd);
    
        return 0;
    }
    
  5. 开启自己的服务即可

1.3守护进程的实例

#include <head.h>

int main(int argc, const char* argv[])
{
    pid_t pid;

    pid = fork();
    if (pid == -1) {
        PRINT_ERR("fork error");
    } else if (pid > 0) {
        printf("父进程退出\n");
        exit(EXIT_SUCCESS);
    } else {
        // 1创建孤儿进程
        // 2.设置组id和会话id
        if (setsid() == -1)
            PRINT_ERR("set sid error");
        // 3.切换路径
        if (chdir("/"))
            PRINT_ERR("change dir error");
        // 4.创建日志文件
        int fd;
        if ((fd = open("daemon.log", O_RDWR | O_CREAT | O_APPEND, 0666)) == -1)
            PRINT_ERR("open daemon.log error");
        // 5.重定向
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);

        // 6.开启自己的服务
        while (1) {
            printf("i am test daemon code....\n");
            fflush(stdout);
            sleep(1);
        }
    }
    return 0;
}

2.进程中程序替换函数

#include <stdlib.h>
int system(const char *command);

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[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);

2.1system函数

2.1.1system函数接口介绍

#include <stdlib.h>
int system(const char *command);
功能:在c语言中执行可执行程序(shell命令),system=fork+execl实现的
参数:
    @command:要执行的命令字符串
返回值:
    *如果command为NULL,那么如果shell可用,则为非零值,如果没有shell可用,则为0。
    *如果无法创建子进程,或者无法检索其状态,则返回值为-1。
    *如果shell不能在子进程中执行,那么返回值就好像子进程通过调用_exit(2)来终止127.
    *如果所有系统调用都成功,那么返回值是用于执行命令的子shell的终止状态。
        (shell的终止状态是它执行的最后一个命令的终止状态。)

2.1.2system函数使用实例

#include <head.h>

int main(int argc,const char * argv[])
{
    //system的执行的过程就是首先先fork出来一个进程
    //然后将b.out替换到这个进程中,这个进程在执行的时候
    //就是执行的b.out的代码
    // if(system("ls -lh")){
    if(system("./b.out")){
        printf("system exec error");
        return -1;
    }

    return 0;
}

2.2exec函数族

2.2.1execl/execv函数API介绍

int execl(const char *path, const char *arg, .../* (char  *) NULL */);
功能:替换当前进程的可执行程序
参数:
    @path:可执行程序的路径及名字 "/bin/ls"
 @arg:可变参数     "ls","-lh",NULL
返回值:失败返回-1,置位错误码

 int execv(const char *path, char *const argv[]);
功能:替换当前进程的可执行程序
参数:
    @path:可执行程序的路径及名字 "/bin/ls"
 @arg:指针数组      argv[]  = { "ls","-lh",NULL};
返回值:失败返回-1,置位错误码

2.2.2execl/execv函数实例

execl实例

#include <head.h>

int main(int argc, const char* argv[])
{
    pid_t pid;
    pid = fork();
    if (pid == -1) {
        PRINT_ERR("fork error");
    } else if (pid == 0) {
        // if(execl("/bin/ls","ls","-lh",NULL)==-1)
         if(execl("./b.out","b.out",NULL)==-1)
            PRINT_ERR("execl error");
    } else {
        //父进程等待回收子进程的资源
        wait(NULL);
    }
    return 0;
}

execv实例

#include <head.h>

char *const arg[] = {"ls","-lh",NULL};

int main(int argc, const char* argv[])
{
    pid_t pid;
    pid = fork();
    if (pid == -1) {
        PRINT_ERR("fork error");
    } else if (pid == 0) {
        if(execv("/bin/ls",arg)==-1)
            PRINT_ERR("execv error");
    } else {
        //父进程等待回收子进程的资源
        wait(NULL);
    }
    return 0;
}

2.2.3execlp/execvp函数API介绍

execlp/execvp可以执行PATH环境变量中指定的任意可执行程序,而参数不用写路径了

sudo vi /etc/environment 如果想添加自己的路径就修改environment文件即可

 int execlp(const char *file, const char *arg, ... /* (char  *) NULL */);
功能:替换当前进程的可执行程序(指定可执行程序路径的PATH环境变量)
参数:
    @path:可执行程序的路径及名字 "ls"
 @arg:可变参数     "ls","-lh",NULL
返回值:失败返回-1,置位错误码

 int execvp(const char *file, char *const argv[]);
功能:替换当前进程的可执行程序(指定可执行程序路径的PATH环境变量)
参数:
    @path:可执行程序的路径及名字 "ls"
 @arg:指针数组      argv[]  = { "ls","-lh",NULL};
返回值:失败返回-1,置位错误码

2.2.4execlp/execvp函数实例

execlp实例

#include <head.h>

int main(int argc, const char* argv[])
{
    pid_t pid;
    pid = fork();
    if (pid == -1) {
        PRINT_ERR("fork error");
    } else if (pid == 0) {
        if(execlp("ls","ls","-lh",NULL)==-1)
            PRINT_ERR("execlp error");
    } else {
        //父进程等待回收子进程的资源
        wait(NULL);
    }
    return 0;
}

execvp实例

#include <head.h>

char *const arg[] = {"ls","-lh",NULL};

int main(int argc, const char* argv[])
{
    pid_t pid;
    pid = fork();
    if (pid == -1) {
        PRINT_ERR("fork error");
    } else if (pid == 0) {
        if(execvp("ls",arg)==-1)
            PRINT_ERR("execvp error");
    } else {
        //父进程等待回收子进程的资源
        wait(NULL);
    }
    return 0;
}

2.2.5execle/execvpe函数API介绍

execle/execvpe可以向程序传递任意的环境变量

int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);
功能:替换当前进程的可执行程序(传递任意的环境变量)
参数:
    @path:可执行程序的路径及名字 "/bin/ls"
 @arg:可变参数     "ls","-lh",NULL,
    @envp:自己传递的环境变量
        char * const envp[] = {"AA=123","BB=456",NULL};
返回值:失败返回-1,置位错误码

int execvpe(const char *file, char *const argv[],char *const envp[]);
功能:替换当前进程的可执行程序(传递任意的环境变量)
参数:
    @path:可执行程序的路径及名字 "/bin/ls"
 @arg:指针数组      argv[]  = { "ls","-lh",NULL};
 @envp:环境变量数组 char * const envp[] = {"AA=123","BB=456",NULL};
返回值:失败返回-1,置位错误码

2.2.6execle/execvpe函数实例

myshell.sh

#!/bin/bash

echo $0
echo $1
echo $2
echo $3
echo $@ 
echo $#
echo $AA
echo $BB

execle实例

#include <head.h>
char * const envp[] = {
    "AA=123",
    "BB=456",
    NULL
};
int main(int argc, const char* argv[])
{
    pid_t pid;
    pid = fork();
    if (pid == -1) {
        PRINT_ERR("fork error");
    } else if (pid == 0) {
        if(execle("./myshell.sh","myshell.sh","11","22","33",NULL,envp)==-1)
            PRINT_ERR("execle error");
    } else {
        //父进程等待回收子进程的资源
        wait(NULL);
    }
    return 0;
}

execvpe的实例

#include <head.h>
int execvpe(const char *file, char *const argv[], char *const envp[]);
char * const envp[] = {
    "AA=123",
    "BB=456",
    NULL
};
char * const arg[] = {"myshell.sh","11","22","33",NULL};
int main(int argc, const char* argv[])
{
    pid_t pid;
    pid = fork();
    if (pid == -1) {
        PRINT_ERR("fork error");
    } else if (pid == 0) {
        if(execvpe("./myshell.sh",arg,envp)==-1)
            PRINT_ERR("execvpe error");
    } else {
        //父进程等待回收子进程的资源
        wait(NULL);
    }
    return 0;
}

3.多线程

3.1线程的概念

线程(LWP):线程又叫做轻量级的进程,进程是分配资源的最小单位,线程是调度的最小单位

线程本省并不占用资源(8K),同一个进程内所有的线程共享进程的资源。多线程没有多进程安全

多线程的是创建时间要比多进程创建的时间更少。多线程的切换,要比多进程的切换时间更短。

多线程的效率要比多进程的效率更高。线程的函数是第三方库提供的,所以以后在编译多线程的

程序的时候要加上-lpthread的库

//多线程的man手册安装
sudo apt-get install manpages-posix manpages-posix-dev

3.2线程创建API接口的介绍

#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                  void *(*start_routine) (void *), void *arg);
功能:创建线程
参数:
    @thread:线程号
 @attr:线程的属性,一般填写为NULL
 @start_routine:线程体(线程处理函数)
 @arg:向线程体传递参数
返回值:成功返回0,失败返回错误码
        
编译的时候一定要加上-lpthread

注:查看多线程的命令
        ps -eLf  

3.3多线程创建实例(不传递参数)

#include <head.h>
void *Task(void *arg)
{
    while(1){
        printf("i am thread....\n");
        sleep(1);
    }
}
int main(int argc,const char * argv[])
{
    pthread_t tid; //线程号

    //创建线程
    if(errno = pthread_create(&tid,NULL,Task,NULL))
        PRINT_ERR("pthread create error");
    
    //如果向看到线程的执行的过程,一定不能让进程退出
    while(1);    

    return 0;
}

3.4多线程创建实例(传递参数)

#include <head.h>

typedef struct{
    char name[20];
    int age;
}stu_t;

void *Task(void *arg)
{
    stu_t *t = (stu_t *)arg;
    printf("name = %s,age = %d\n",t->name,t->age);
}
int main(int argc,const char * argv[])
{
    stu_t st = {
        "zhangsan",23
    };

    pthread_t tid; //线程号

    //创建线程
    if(errno = pthread_create(&tid,NULL,Task,(void *)&st))
        PRINT_ERR("pthread create error");
    
    //如果向看到线程的执行的过程,一定不能让进程退出
    while(1);    

    return 0;
}

3.5多线程执行的顺序

多线程执行没有先后顺序,时间片轮询,上下文切换

3.6多线程内存空间问题

多线程共用同一个进程的内存空间,所以全局变量每个线程都能获取并使用

#include <head.h>
int a=10;
void* Task1(void* arg)
{
    //线程1在修改a的值
    while(1){
        a++;
        sleep(1);
    }
}

void* Task2(void* arg)
{
    //线程2能拿到线程1修改后的值
    while(1){
        printf("a = %d\r",a);
        fflush(stdout);
    }
}
int main(int argc, const char* argv[])
{
    pthread_t tid1, tid2; //线程号

    //创建线程1
    if (errno = pthread_create(&tid1, NULL, Task1, NULL))
        PRINT_ERR("pthread create error");

    //创建线程2
    if (errno = pthread_create(&tid2, NULL, Task2, NULL))
        PRINT_ERR("pthread create error");

    //如果向看到线程的执行的过程,一定不能让进程退出
    while (1);

    return 0;
}

3.7多线程pthread_self函数

pthread_t pthread_self(void);
功能:获取当前线程的线程号
参数:
    @无
返回值:成功返回线程号
#include <head.h>

void* Task1(void* arg)
{
    printf("child tid = %ld\n", pthread_self());
}

int main(int argc, const char* argv[])
{
    pthread_t tid; //线程号

    //创建线程
    if (errno = pthread_create(&tid, NULL, Task1, NULL))
        PRINT_ERR("pthread create error");

    printf("parent tid=%ld,child  tid = %ld\n", pthread_self(),tid);

    //如果向看到线程的执行的过程,一定不能让进程退出
    while (1)
        ;

    return 0;
}

3.8多线程pthread_exit函数

在线程中不能使用exit(_exit)来退出线程,因为exit(_exit)是用来结束进程的。

void pthread_exit(void *retval);
功能:退出一个线程
参数:
    @retval:返回结束的状态值
返回值:无

3.9多线程pthread_join函数

int pthread_join(pthread_t thread, void **retval);
功能:阻塞等待回收thread线程的资源
参数:
    @thread:线程号
    @retval:就是pthread_exit给出的错误状态
返回值:成功返回0,失败返回错误码

实例如下:

#include <head.h>

void* Task1(void* arg)
{
    //这里的status如果不加static,在main函数获取的status值和预想的不一样
    //应为栈空间定义的变量,当函数调用结束的时候,内存就被释放了,就不能
    //通过指针获取它内部的成员。而加static之后变量就被放到.data,所以就
    //不会随函数的结束而释放空间了。所以在main函数就能够获取到这个结果了
    static int status=40;
    printf("child tid = %ld\n", pthread_self());
    while(1){
        sleep(1);
        // pthread_exit(&status);
        pthread_exit(NULL);
    }
}

int main(int argc, const char* argv[])
{
    pthread_t tid; //线程号

    //创建线程
    if (errno = pthread_create(&tid, NULL, Task1, NULL))
        PRINT_ERR("pthread create error");

    printf("parent tid=%ld,child  tid = %ld\n", pthread_self(),tid);

    int *status;
    //pthread_join会阻塞等待子线程的退出
    // pthread_join(tid,(void **)&status);
    // printf("*status = %d\n",*status);

    pthread_join(tid,NULL);

    return 0;
}

3.10多线程pthread_cancel函数

int pthread_cancel(pthread_t thread);
功能:一个线程给另外一个线程发送取消的请求
参数:
    @thread:接收取消请求的tid
返回值:成功返回0,失败返回错误码
#include <head.h>
pthread_t tid1, tid2; //线程号
void* Task1(void* arg)
{
    sleep(5);
    //给线程2 发送一个取消的信号,线程2将会结束掉
    pthread_cancel(tid2);
}

void* Task2(void* arg)
{
    // pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
    while (1) {
        printf("i am thread 2....\n");
        sleep(1);
    }
}
int main(int argc, const char* argv[])
{

    //创建线程1
    if (errno = pthread_create(&tid1, NULL, Task1, NULL))
        PRINT_ERR("pthread create error");

    //创建线程2
    if (errno = pthread_create(&tid2, NULL, Task2, NULL))
        PRINT_ERR("pthread create error");

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    return 0;
}

3.11多线程pthread_detach函数

线程是有两种状态的:结合态、分离态。

对于默认创建的线程就是结合态,结合态的线程的资源回收的时候需要通过pthread_join完成。

但是如果将一个线程标记为分离态,这个线程结束后资源会被自动回收。

int pthread_detach(pthread_t thread);
功能:标记线程为分离态
参数:
    @thread:线程号
返回值:成功返回0,失败返回错误码
#include <head.h>
pthread_t tid1, tid2; //线程号
void* Task1(void* arg)
{
    sleep(5);
}

void* Task2(void* arg)
{
    while (1) {
        printf("i am thread 2....\n");
        sleep(1);
    }
}
int main(int argc, const char* argv[])
{

    //创建线程1
    if (errno = pthread_create(&tid1, NULL, Task1, NULL))
        PRINT_ERR("pthread create error");

    //创建线程2
    if (errno = pthread_create(&tid2, NULL, Task2, NULL))
        PRINT_ERR("pthread create error");

    //将线程1和线程2标记为分离态,这个函数不会阻塞
    pthread_detach(tid1);
    pthread_detach(tid2);

    while(1);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值