Linux 进程

2021-05-28  本文已影响0人  攻城老狮

一 环境变量

1 常见环境变量

#/home/ypw/bin:/home/ypw/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
PATH 
# /home/ypw
HOME 
# /bin/bash
SHELL 
# xterm-256color
TERM 
# en_US.UTF-8
LANG

2 程序获取环境变量

#include<stdio.h>
//导入存放环境变量的数组
extern char** environ;

int main(){
    //NULL作为哨兵结尾
    for(int i=0 ; environ[i]!=NULL ; i++){
        printf("%s\n",environ[i]);
    }
    return 0;
}

3 函数操作环境变量

char *getenv(const char *name);
//overwrite不为0重写
int setenv(const char *name, const char *value, int overwrite); 
int unsetenv(const char *name);
#include<stdio.h>
#include<stdlib.h>
// 获取环境变量,添加环境变量,删除环境变量
int main(){
    //获取环境变量
    const char* name = "VAR";
    char* val = getenv(name);
    printf("%s=%s\n",name,val);
    //添加环境变量
    setenv(name,"hello world",1);
    //获取环境变量
    val = getenv(name);
    printf("%s=%s\n",name,val);
    //删除环境变量
    int ret = unsetenv(name);
    printf("%d\n",ret);
    return 0;
}

二 进程控制

1 fork函数

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
// 使用fork创建子进程,子进程的fork()返回值为0,父进程的fork()返回值为子进程pid
int main(){
    pid_t pid;
    printf("---before fork---\n");
    pid = fork();
    if(pid == -1){
        perror("fork:");
        exit(1);
    }else if(pid == 0){
        printf("child process: pid:%u,ppid:%u\n",getpid(),getppid());
    }else{
        sleep(1);
        printf("parent process: pid:%u,ppid:%u\n",getpid(),getppid());
    }
    printf("---after fork---\n");
    return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
# 使用fork循环创建子进程
int main(){
    pid_t pid;
    printf("--before fork--\n");
    int i;
    for(i=0 ; i<5 ; i++){
        pid = fork();
        if(pid == -1){
            perror("fork: ");
            exit(1);
        }else if(pid == 0){
             // 避免子进程再创建子进程的子进程,需要跳出循环,表示子进程不再fork
            break;
        }
    }
    // 子进程
    if(i<5){
        sleep(i);
        printf("%dth child process ,pid=%u\n",i+1,getpid());
    }else{ // 父进程
        sleep(i);
        printf("parent process,pid=%u\n",getpid());
    }

    return 0;
}
# result
--before fork--
1th child process ,pid=60180
2th child process ,pid=60181
3th child process ,pid=60182
4th child process ,pid=60183
5th child process ,pid=60184
parent process,pid=60179

读时共享写时复制

# 默认跟踪父进程,在fork()函数调用之前设置,可以修改gdb跟踪的进程
set follow-fork-mode child
set follow-fork-mode parent

2 exec函数族

当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec不会创建新进程,所有调用exec前后该进程的id未被改变。

exec函数一旦调用成功即立即执行新的程序,不返回。只有失败才返回,错误值为-1。故通常在exec函数调用后直接调用perror()和exit(),无需if判断

/*
    l(list):命令行参数列表
    p(path):搜索file时,使用path变量
    v(vector):使用命令行参数数组
    e(environment):使用环境变量数组
*/
int execl();
int execlp();
int execle();
int execv();
int execvp();
int execve();
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
// 在子进程中执行ls命令
int main(){
    pid_t pid;
    //创建子进程
    pid = fork();
    if(pid == -1){
        perror("fork");
        exit(1);
    }else if(pid == 0){
        //子进程执行execlp函数
        execlp("ls","ls","-l","-a",NULL);
    }else{
        sleep(1);
        printf("parent\n");
    }
    return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
// 在子进程中执行ls命令
int main(){
    pid_t pid;
    //创建子进程
    pid = fork();
    if(pid == -1){
        perror("fork");
        exit(1);
    }else if(pid == 0){
        //子进程执行execl函数,可以执行自定义的程序
        execl("/bin/ls","ls","-l","-a",NULL);
    }else{
        sleep(1);
        printf("parent\n");
    }
    return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
//将当前系统的进程信息打印到文件中
int main(){
    //打开文件,只写,没有创建,有则截断
    int fd = open("ps.out",O_WRONLY|O_CREAT|O_TRUNC,0644);
    if(fd == -1){
        perror("open");
        exit(1);
    }
    //将fd的文件描述符复制到标准输出,标准输出写入到fd指向的文件
    dup2(fd,1);
    //执行ps
    execlp("ps","ps","aux",NULL);
    //成功不返回
    perror("ps");
    exit(1);
    //close(fd);
    return 0;
}

三 回收子进程

1 孤儿进程

孤儿进程是表示父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程。

由于我使用的环境是带有图形界面的ubuntu系统,所以最终并不是被我们所熟知的init进程收养,而是被一个名为/sbin/upstart的进程所收养。另外还可以观察到,该进程也是其他系统进程的父进程。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
//当父进程死亡后,子进程成为孤儿进程,被1685进程收养
int main(){
    pid_t pid;
    pid = fork();
    if(pid == -1){
        perror("fork");
        exit(1);
    }else if(pid == 0){
        while(1){
            sleep(1);
            printf("child process pid=%u,ppid=%u\n",getpid(),getppid());
        }
    }else{
        sleep(3);
        printf("parent process pid=%u,childPid=%u\n",getpid(),pid);
    }
    return 0;
}

2 僵尸进程

僵尸进程表示当进程终止后,父进程尚未回收,子进程残留资源(PCB)存放在内核中,变为僵尸进程。僵尸进程不能使用kill命令清除,因为kill只是用来终止进程的,而僵尸进程已经终止。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int main(){
    pid_t pid;
    pid = fork();
    if(pid == -1){
        perror("fork");
        exit(1);
    }else if(pid == 0){
            sleep(3);
            printf("child process pid=%u,ppid=%u\n",getpid(),getppid());
    }else{
        while(1){
            sleep(1);
            printf("parent process pid=%u,childPid=%u\n",getpid(),pid);
        }
    }
    return 0;
}
# result
ypw   62275  0.0  0.0   4352   656 pts/2    S+   19:46   0:00 ./zoombie
ypw   62276  0.0  0.0      0     0 pts/2    Z+   19:46   0:00 [zoombie] <defunct>

3 wait 函数

父进程调用wait函数可以回收子进程终止信息。三个功能:

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

int main(){
    
    pid_t pid,wpid;
    pid = fork();
    if(pid == -1){
        perror("fork");
        exit(1);
    }else if(pid == 0){
            sleep(3);
            printf("child process pid=%u,ppid=%u\n",getpid(),getppid());
    }else{
        wpid = wait(NULL); //阻塞,等待子进程结束,回收该僵尸进程,成功返回子进程的pid
        if(wpid == -1){
            perror("wait error");
            exit(1);
        }
        while(1){
            sleep(1);
            printf("parent process pid=%u,childPid=%u\n",getpid(),pid);
        }
    }
    return 0;
}

增强版,可以获取进程的状态信息

WIFEXITED(status):非零,进程正常结束
WEXITSTATUS(status):上面的宏为真,则使用该宏可以获取进程退出状态 (exit/return 的状态)
WIFSIGNALED(status):非零,进程异常终止
WTERMSIG(status):取得使进程终止的那个信号的编号 (例:kill -9 pid 获取到编号为9)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main(){
    
    pid_t pid,wpid;
    int status;
    pid = fork();
    if(pid == -1){
        perror("fork");
        exit(1);
    }else if(pid == 0){
            sleep(30);
            printf("child process pid=%u,ppid=%u\n",getpid(),getppid());
            exit(76); // return 100;
    }else{
        wpid = wait(&status);
        if(WIFEXITED(status)){
            printf("return or exit success: %d\n",WEXITSTATUS(status));
        }
        if(WIFSIGNALED(status)){
            printf("kill by the number: %d\n",WTERMSIG(status));
        }
        if(wpid == -1){
            perror("wait error");
            exit(1);
        }
        while(1){
            sleep(1);
            printf("parent process pid=%u,childPid=%u\n",getpid(),pid);
        }
    }
    return 0;
}

4 waitpid 函数

作用同wait,但可以指定清理的pid进程,可以非阻塞

// 参数:pid:>0 回收指定ID的子进程,-1 回收任意子进程(同wait)
// 返回:0,参数3为宏 WNOHANG,表示子进程正在运行
pid_t waitpid(pid_t pid, int *status, int options);
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main(){

    pid_t pid,pid_three,wpid;
    int i,n=5;
    for(i=1 ; i<=n ; i++){
        pid = fork();
        if(pid == -1){
            perror("fork");
            exit(1);
        } else if(pid == 0 ){
            break;
        } else{
            if(i==3){ //当fork第三个进程时候,记录进程号
                pid_three = pid;
            }
        }
    }
    
    if(i == n+1){
        sleep(i);
//      pid_three = waitpid(pid_three,NULL,0); //阻塞回收第三个进程
//      printf("waitpid=%d\n",pid_three);
        do{ //循环回收全部进程
            wpid = waitpid(-1,NULL,WNOHANG); //非阻塞式
            if(wpid>0){
                n--;
            }
        }while(n>0);
        printf("wait finish...\n");
        printf("parent id=%d\n",getpid());
        while(1);
    }else{
        sleep(i);
        printf("child%d id=%d\n",i,getpid());
    }
    return 0;
}

四 进程间通信

1 Pipe管道

//功能:使用pipe实现,父子进程通信,父写数据,子读数据
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>

void sys_err(const char* str,int errno){
    perror(str);
    exit(errno);
}

int main(){

    int fd[2];
    pid_t pid;
    char str[1024] = "Hello World\n";
    char buf[1024];
    int len;

    if(pipe(fd)<0){ //创建管道 fd[0]读端 fd[1]写端
        sys_err("pipe error",1);
    }
    
    pid = fork(); //创建子进程
    if(pid == -1){
        sys_err("fork error",1);
    }else if(pid > 0){ //父进程
        close(fd[0]); //父关闭读
        write(fd[1],str,strlen(str)); //向管道写数据
        wait(NULL); //回收子进程
        close(fd[1]); 
    }else{ //子进程
        close(fd[1]); //子关闭写
        len = read(fd[0],buf,sizeof(buf)); //从管道读数据
        write(STDOUT_FILENO,buf,len); //将数据写到屏幕
        close(fd[0]);               
    }
    return 0;
}

2 fifo有名管道

# 借助fifo实现非血缘进程间通信
mkfifo myfifo
//扮演写的程序
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

void sys_err(const char* str,int errno){
    perror(str);
    exit(errno);
}

int main(){
    
    int fd;
    char* str = "Hello Fifo";
    fd = open("myfifo",O_WRONLY); //打开fifo管道
    if(fd == -1 ){
        sys_err("open error",1);
    }
    write(fd,str,strlen(str)); //向管道写数据
    close(fd);
    return 0;
}
//扮演读的程序
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

void sys_err(const char* str,int errno){
    perror(str);
    exit(errno);
}

int main(){
    
    int fd;
    char buf[1024];
    int len;

    fd = open("myfifo",O_RDONLY); //打开fifo管道
    if(fd == -1 ){
        sys_err("open error",1);
    }
    len = read(fd,buf,sizeof(buf)); //读数据
    write(STDOUT_FILENO,buf,len); 
    close(fd);
    return 0;
}

3 mmap内存共享映射

//借助mmap实现非血缘关系进程的通信
//扮演写的程序
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<string.h>
#include<unistd.h>

#define MAPLEN 0x1000

struct Stu{ //定义传输的数据格式为结构体
    int id;
    char name[20];
    char sex;
};

void sys_err(const char* str, int errno){
    perror(str);
    exit(errno);
}

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

    int fd,i=0;
    struct Stu* mm;

    if(argc < 2){
        printf("./a.out filename");
        exit(1);
    }

    fd = open(argv[1],O_RDWR | O_CREAT, 0777); //有则打开无则创建
    if(fd < 0 ){
        sys_err("open error",1);
    }

    if(lseek(fd,MAPLEN-1,SEEK_SET)<0){ //提供4KB大写的空文件
        sys_err("lseek error",2);
    }
    if(write(fd,"\0",1)<0){ //lseek写入空洞字符后,需要再追加写入一个字符
        sys_err("write error",3);
    }
    //内存共享映射
    mm = (struct Stu*)mmap(NULL,MAPLEN,PROT_READ | PROT_WRITE, MAP_SHARED,fd,0);
    if(mm == MAP_FAILED){ //映射失败
        sys_err("mmap error",4);
    }
    close(fd); //可以关闭文件描述符,已经建立映射故不影响

    while(1){ //循环向共享内存中写数据
        mm->id = i;
        sprintf(mm->name,"stu-%d",i);
        if(i%2==0)
            mm->sex = 'm';
        else
            mm->sex = 'w';
        i++;
        sleep(1);
    }
    munmap(mm,MAPLEN); //解绑映射
    return 0;
}

//扮演读的程序
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<string.h>
#include<unistd.h>

#define MAPLEN 0x1000

struct Stu{
    int id;
    char name[20];
    char sex;
};

void sys_err(const char* str, int errno){
    perror(str);
    exit(errno);
}

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

    int fd,i=0;
    struct Stu* mm;

    if(argc < 2){
        printf("./a.out filename");
        exit(1);
    }

    fd = open(argv[1],O_RDONLY); //只读
    if(fd < 0 ){
        sys_err("open error",1);
    }

    mm = (struct Stu*)mmap(NULL,MAPLEN,PROT_READ , MAP_SHARED,fd,0);
    if(mm == MAP_FAILED){
        sys_err("mmap error",4);
    }
    close(fd);

    while(1){ //循环读取映射共享的数据
        printf("id:%d ",mm->id);
        printf("name:%s ",mm->name);
        printf("sex:%c\n",mm->sex);
        sleep(1);
    }
    munmap(mm,MAPLEN);
    return 0;
}

五 信号

1 kill函数

//使用kill函数模拟系统的kill命令,向目标进程pid发送信号
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<signal.h>
int main(int argc,char* argv[]){
    
    int ret;

    if(argc < 3){
        printf("./a.out signalNumber pid");
        exit(1);
    }
    
    ret = kill((pid_t)atoi(argv[2]),atoi(argv[1])); //发送信号,pid,signal
    if(ret < 0 ){
        perror("kill");
        exit(1);
    }
    return 0;
}

2 alarm函数

//定时器,运行规定的时间后自动终止进程
#include<stdio.h>
#include<unistd.h>

int main(){

    int count;

    alarm(1); //定时1s
    for(count = 0 ; 1 ; count++){
        printf("count=%d\n",count);
    }
    return 0;
}

3 信号集处理函数

//通过更改屏蔽字,屏蔽相应的信号
#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void printsigset(const sigset_t* set){ //打印当前未决信号集
    int i;
    for(i=1 ; i<32 ; i++){
        if(sigismember(set,i)==1){ //判断i信号是否在未决信号集上激活
            putchar('1');
        }else{
            putchar('0');
        }
    }
    puts("");
}

int main(){
    
    sigset_t s,p;
    int i=0;    
    sigemptyset(&s); //置零
    sigaddset(&s,SIGINT); //SIGINT信号阻塞置1
    sigaddset(&s,SIGQUIT); //SIGQUIT信号阻塞置1
    sigprocmask(SIG_BLOCK,&s,NULL); //使用s来更改当前进程的信号屏蔽字

    while(1){
        sigpending(&p); //获取未决信号集
        printsigset(&p);
        sleep(1);
        if(i == 10){
            sigdelset(&s,SIGQUIT); //删除阻塞信号集的SIGQUIT信号,置零
            sigprocmask(SIG_UNBLOCK,&s,NULL); //将SIGINT信号置零放行SIGINT未决信号
        }
        i++;
    }   

    return 0;
}

4 信号捕捉

#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void do_sig(int num){ //捕捉函数
    printf("I am do_sig function\n");
    printf("num=%d\n",num);
}

int main(){

    struct sigaction act; //定义捕捉的结构体

    act.sa_handler = do_sig; //添加捕捉函数
    sigemptyset(&act.sa_mask); //临时信号屏蔽字
    act.sa_flags = 0; //选定旧版本捕捉函数

    sigaction(SIGINT,&act,NULL); //给SIGINT信号添加捕捉函数

    while(1){
        printf("=============\n");
        sleep(1);
    }

    return 0;
}
#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void do_sig(int num){
    int n=5;
    printf("I am do_sig function\n");
    while(n--){ //添加循环,增加捕捉函数的执行时间
        printf("num=%d\n",num);
        sleep(1);
    }
}

int main(){

    struct sigaction act;

    //act.sa_handler = SIG_DFL; //默认
    //act.sa_handler = SIG_IGN; //忽略
    act.sa_handler = do_sig;
    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask,SIGQUIT); //对SIGQUIT信号临时屏蔽
    act.sa_flags = 0;

    sigaction(SIGINT,&act,NULL);

    while(1){
        printf("=============\n");
        sleep(1);
    }

    return 0;
}

5 signal 函数

//捕捉SIGUSR1信号
#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void do_sig(int num){
    printf("num=%d\n",num);
}

int main(){
    
    signal(SIGUSR1,do_sig);
    
    while(1){
        printf("***********\n");
        sleep(1);
    }
    
    return 0;
}

6 可重入函数

//将不可重入函数strtok更改为可重入函数strtok_r,用于对字符串的切割
#include<stdio.h>
#include<string.h>

int main(){
    
    char buf[] = "Hello Tom Jerry Smith";
    char *save = buf, *p;

    while((p = strtok_r(save," ",&save))!=NULL){
        printf("%s\n",p);
    }

    return 0;
}

7 时序京态

//自己实现sleep函数,需要考虑时序静态的发生,在alarm前,需要设置信号屏蔽字来屏蔽SIGALRM信号,之后再解除SIGALRM信号的阻塞,通过sigsuspend函数原子化操作实现信号唤醒挂起
#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void do_sig(int num){
    //nothing to do
}

unsigned int mysleep(unsigned int nsecs){
    struct sigaction act,oldact;
    sigset_t newmask,oldmask,susmask;
    unsigned int unslept;
    //给SIGALRM信号设置捕捉函数
    act.sa_handler = do_sig; 
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGALRM,&act,&oldact);
    //设置信号屏蔽字,屏蔽SIGALRM信号
    sigemptyset(&newmask);
    sigaddset(&newmask,SIGALRM);
    sigprocmask(SIG_BLOCK,&newmask,&oldmask);
    //定时器
    alarm(nsecs);
    //解除SIGALRM信号的屏蔽
    susmask = oldmask;
    sigdelset(&susmask,SIGALRM);
    sigsuspend(&susmask); //挂起等待SIGALRM信号幻醒

    unslept = alarm(0);
    sigaction(SIGALRM,&oldact,NULL); //恢复SIGALRM信号默认响应状态
    sigprocmask(SIG_SETMASK,&oldmask,NULL); //恢复信号屏蔽字
    return unslept;
}


int main(){
    while(1){
        mysleep(2);
        printf("Two seconds passed\n");
    }

    return 0;
}

SIGCHLD信号处理

//通过对信号的处理实现清理僵尸进程
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>


void sys_err(const char* str,int errno){
    perror(str);
    exit(errno);
}
//捕捉函数
void do_sig_child(int num){
    int status;
    pid_t pid;
    while((pid = waitpid(0,&status,WNOHANG))>0){ //waitpid 处理一个僵尸进程
        if(WIFEXITED(status)){ //正常退出
            printf("child %d exit %d\n",pid,WEXITSTATUS(status));
        }else if(WIFSIGNALED(status)){ //异常退出
            printf("childe %d cancel signal %d\n",pid,WTERMSIG(status));
        }
    }
}

int main(){

    pid_t pid;
    int i;
    for(i=0 ; i<10 ; i++){ //fork 10个子进程
        pid = fork();
        if(pid < 0) {
            sys_err("fork",1);
        }else if(pid == 0 ){
            break;
        }
    }

    if(pid == 0 ){ //子进程
        int n = 15;
        while(n--){
            sleep(1);
            printf("child pid %d\n",getpid());
        }
        return i; //返回退出的值
    }else if(pid > 0){ //父进程
         //设置捕捉SIGCHLD信号
        struct sigaction act;
        act.sa_handler = do_sig_child;
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGCHLD,&act,NULL);
        while(1){
            sleep(1);
            printf("parent pid %d\n",getpid());
        }
    }

    return 0;
}

六 进程间关系

1 进程组

//获取进程组gid的几种方式
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int main(){

    pid_t pid;

    if((pid = fork())<0){
        perror("fork");
        exit(1);
    }else if(pid == 0) {
        printf("child pid:%d\n",getpid());
        printf("child pgid:%d\n",getpgrp()); //获取组id
        printf("child pgid:%d\n",getpgid(0));//获取组id
        printf("child pgid:%d\n",getpgid(getpid()));//获取组id
    }else{  
        sleep(3);
        printf("parent pid:%d\n",getpid());
        printf("parent pgid:%d\n",getpgid(0));//获取组id
    }
    return 0;
}
//改变进程的进程组id
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
    pid_t pid;
    if ((pid = fork()) < 0) {
        perror("fork");
        exit(1);
    } else if (pid == 0) {
        printf("child process PID is %d\n",getpid());
        printf("Group ID of child is %d\n",getpgid(0)); // 返回组id
        sleep(5);
        printf("Group ID of child is changed to %d\n",getpgid(0));
        exit(0);
    }
    sleep(1);
    setpgid(pid,pid); // 父进程改变子进程的组id为子进程本身
    sleep(5);
    printf("parent process PID is %d\n",getpid());
    printf("parent of parent process PID is %d\n",getppid());
    printf("Group ID of parent is %d\n",getpgid(0));
    setpgid(getpid(),getppid()); // 改变父进程的组id为父进程的父进程
    printf("Group ID of parent is changed to %d\n",getpgid(0));
    return 0;
}

2 会话设置

//子进程创建独立的会话,关闭当前终端不影响该会话的正常运行
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int main(){

    pid_t pid;

    if((pid = fork())<0){
        perror("fork");
        exit(1);
    }else if(pid == 0 ){
        printf("child pid:%d\n",getpid());
        printf("child pgid:%d\n",getpgid(0));
        printf("child sid:%d\n",getsid(0));
        sleep(10);
        setsid(); //子进程非组长进程,故其成为新会话首进程,且成为组长进程。该进程组id即为会话进程
        printf("Changed\n");
        printf("child pid:%d\n",getpid());
        printf("child pgid:%d\n",getpgid(0));
        printf("child sid:%d\n",getsid(0));
        sleep(20);  
        exit(0);
    }

    return 0;
}

七 线程

1 pthread_create 创建线程

//创建线程,打印一些线程信息
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>

void* do_thread_func(void* arg){
    int* p = (int*)arg;
    printf("thread running...\n");
    printf("thread pid:%d\n",getpid());
    printf("thread id:%x\n",(unsigned int)pthread_self());
    printf("thread arg:%d\n",*p);
}

int main(){
    
    pthread_t tid;
    int arg = 233;  
    
    pthread_create(&tid,NULL,do_thread_func,(void *)&arg); //创建线程
    
    printf("main pid:%d\n",getpid());
    printf("main thread id:%x\n",(unsigned int)pthread_self()); //main线程的tid
    printf("main child thread id:%x\n",(unsigned int)tid); //新线程的tid
    sleep(1);   
    return 0;
}

2 pthread_join 回收线程

//回收线程
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>

void* do_thread_func1(void* arg){
    printf("thread 1 running...\n");
    return (void*)1;
}

void* do_thread_func2(void* arg){
    printf("thread 2 running...\n");
    pthread_exit((void*)2);
}

void* do_thread_func3(void* arg){
    while(1){
        printf("thread 3 writing...\n");
        sleep(1);
    }
}

int main(){

    pthread_t tid;
    void* retval;

    pthread_create(&tid,NULL,do_thread_func1,NULL);
    pthread_join(tid,&retval);
    printf("thread 1 exit code:%d\n",(int*)retval);

    pthread_create(&tid,NULL,do_thread_func2,NULL);
    pthread_join(tid,&retval);  
    printf("thread 2 exit code:%d\n",(int*)retval);
    
    pthread_create(&tid,NULL,do_thread_func3,NULL);
    sleep(3);   
    pthread_cancel(tid);
    pthread_join(tid,&retval);
    printf("thread 3 exit code:%d\n",(int*)retval);

    return 0;
}

3 pthread_detached 分离线程

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>

void* do_thread_func(void* arg){
    int n=3;
    while(n--){
        printf("thread running...\n");
        sleep(1);
    }
    return (void*)233;
}

int main(){

    pthread_t tid;
    int err;
    void *retval;
        
    pthread_create(&tid,NULL,do_thread_func,NULL);
    pthread_detach(tid); //分离线程,无法使用join回收,无法接收返回的参数
    while(1){
        err = pthread_join(tid,&retval);
        if(err != 0 ){
            printf("thread %s\n",strerror(err));
        }else{
            printf("thread exit code %d\n",(int)retval);
        }
        sleep(1);   
    }

    return 0;
}

4 设置线程属性

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>

#define SIZE 0x100000

void* do_thread_func(void* arg){
    int n = 3;
    while(n--){
//      printf("Hello thread\n");
        sleep(1);
    }
}

int main(){

    pthread_t tid;
    pthread_attr_t attr;
    int err,detachstate,i=1;
    void* stackaddr;
    size_t stacksize;
    
    pthread_attr_init(&attr);

    pthread_attr_getstack(&attr,&stackaddr,&stacksize);
    printf("origin stackaddr:%p\n",stackaddr);
    printf("origin stacksize:%d\n",(int)stacksize);
    
    pthread_attr_getdetachstate(&attr,&detachstate);
    if(detachstate == PTHREAD_CREATE_DETACHED){
        printf("thread detached\n");
    }else if(detachstate == PTHREAD_CREATE_JOINABLE){
        printf("thread joinable\n");
    }
    
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
    while(1){
        stackaddr = malloc(SIZE);
        if(stackaddr == NULL){
            perror("malloc");
            exit(1);
        }
        stacksize = SIZE;
        pthread_attr_setstack(&attr,stackaddr,stacksize);
        err = pthread_create(&tid,&attr,do_thread_func,NULL);
        if(err != 0 ){
            printf("thread:%s\n",strerror(err));
            exit(1);
        }
        printf("%d\n",i++);
    }
    
    pthread_attr_destroy(&attr);
    return 0;
}

八 线程同步

1 互斥量

//通过互斥量mutex实现对临界区资源count的加锁访问,比卖你出现并发问题
#include<stdio.h>
#include<pthread.h>

#define LOOPNUM 5000
int count;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //互斥量静态初始化

void* do_thread_func(void* arg){
    int i,val;
    for(i=0 ; i<LOOPNUM ; i++){
        pthread_mutex_lock(&mutex); //加锁访问临界资源
        val = count;
        printf("%x:%d\n",(unsigned int)pthread_self(),val+1);
        count = val+1;
        pthread_mutex_unlock(&mutex); //解锁临界资源
    }
    return NULL;
}

int main(){

    pthread_t tid1,tid2;
    
    pthread_create(&tid1,NULL,do_thread_func,NULL);
    pthread_create(&tid2,NULL,do_thread_func,NULL);

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

    return 0;
}

2 死锁

//线程一拥有A锁,请求获得B锁;线程二拥有B锁,请求获得A锁
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>

char* str = "Hello World";
pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;

void* do_thread_func1(void* arg){
    pthread_mutex_lock(&lock1);
    sleep(1);
    pthread_mutex_lock(&lock2);
    printf("%s\n",str);
    pthread_mutex_unlock(&lock2);
    pthread_mutex_unlock(&lock1);
    return NULL;
}

void* do_thread_func2(void* arg){
    pthread_mutex_lock(&lock2);
    sleep(2);
    pthread_mutex_lock(&lock1);
    printf("%s\n",str);
    pthread_mutex_unlock(&lock1);
    pthread_mutex_unlock(&lock2);
    return NULL;
}

int main(){

    pthread_t tid1,tid2;

    pthread_create(&tid1,NULL,do_thread_func1,NULL);
    pthread_create(&tid2,NULL,do_thread_func2,NULL);

    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    return 0;
}

3 读写锁

//3个线程不定时写同一全局资源,5个线程不定时读同一全局资源
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>

int count;
pthread_rwlock_t lock = PTHREAD_RWLOCK_INITIALIZER;

void* do_write_func(void* arg){
    int t;
    while(1){
        usleep(100);
        pthread_rwlock_wrlock(&lock);
        t = count;
        usleep(100);
        printf("%x write: counter=%d,new counter=%d\n",(unsigned int)pthread_self(),t,++count);
        pthread_rwlock_unlock(&lock);
        usleep(100);
    }
    return NULL;
}

void* do_read_func(void* arg){
    while(1){
        pthread_rwlock_rdlock(&lock);
        printf("%x read: counter=%d\n",(unsigned int)pthread_self(),count);
        pthread_rwlock_unlock(&lock);
        usleep(100);
    }
    return NULL;
}

int main(){
    pthread_t tid[8];
    int i;
    for(i=0 ; i<3 ; i++){
        pthread_create(&tid[i],NULL,do_write_func,NULL);
    }
    for(i=0 ; i<5 ; i++){
        pthread_create(&tid[i+3],NULL,do_read_func,NULL);
    }
    for(i=0 ; i<8 ; i++){
        pthread_join(tid[i],NULL);
    }
    return 0;
}

4 条件变量

//生产者-消费者模型
    //pthread_cond_wait前要先加锁
    //pthread_cond_wait内部会解锁,然后等待条件变量被其它线程激活
    //pthread_cond_wait被激活后会再自动加锁
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<unistd.h>

struct msg{
    int num;
    struct msg* next;
};

struct msg* head = NULL;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;

void* producer(void* arg){
    struct msg* mp;
    while(1){
        mp = (struct msg*)malloc(sizeof(struct msg));
        mp->num = rand()%1000 + 1;
        printf("produce %d\n",mp->num);
        pthread_mutex_lock(&lock);
        mp->next = head;
        head = mp;
        pthread_mutex_unlock(&lock);
        pthread_cond_signal(&has_product);
        sleep(rand()%5);
    }
}

void* consumer(void* arg){
    struct msg* mp;
    while(1){
        pthread_mutex_lock(&lock);
        while(head == NULL){
            pthread_cond_wait(&has_product,&lock);
        }
        mp = head;
        head = mp->next;
        pthread_mutex_unlock(&lock);
        printf("consume %d\n",mp->num);
        free(mp);
        sleep(rand()%5);
    }
}

int main(){

    pthread_t ptid,ctid;
    srand(time(NULL));
    pthread_create(&ptid,NULL,producer,NULL);
    pthread_create(&ctid,NULL,consumer,NULL);

    pthread_join(ptid,NULL);
    pthread_join(ctid,NULL);
    return 0;
}

5 信号量

//通过信号量的方式实现生产者和消费者模型
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<semaphore.h>
#include<pthread.h>

#define NUM 5
sem_t blank_n,product_n; //定义两个信号量
int queue[NUM]; //资源

void* producer(void* arg){
    int p=0;
    while(1){
        sem_wait(&blank_n); //消耗一个blank_n信号量
        queue[p] = rand()%1000 + 1;
        printf("produce %d\n",queue[p]);
        sem_post(&product_n); //归还一个product_n信号量
        p = (p+1)%NUM;
        sleep(rand()%3);
    }
    return NULL;
}

void* consumer(void* arg){
    int p=0;
    while(1){
        sem_wait(&product_n); //消耗一个product_n信号量
        printf("consume %d\n",queue[p]);
        sem_post(&blank_n); //归还一个blank_n信号量
        p = (p+1)%NUM;
        sleep(rand()%3);
    }
    return NULL;
}

int main(){

    srand(time(NULL));
    pthread_t ptid,ctid;
    
    sem_init(&blank_n,0,NUM);
    sem_init(&product_n,0,0);

    pthread_create(&ptid,NULL,producer,NULL);
    pthread_create(&ctid,NULL,consumer,NULL);

    pthread_join(ptid,NULL);
    pthread_join(ctid,NULL);
    
    sem_destroy(&blank_n);
    sem_destroy(&product_n);
    return 0;
}

6 进程间锁-进程间pthread_mutex

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<pthread.h>
#include<string.h>
#include<sys/wait.h>

struct mt{ //控制的进程间共享资源
    int num;
    pthread_mutexattr_t attr;
    pthread_mutex_t mutex;
};

int main(){

    int fd,i;
    pid_t pid;
    struct mt* mm;

    fd = open("my_test",O_RDWR|O_CREAT,0777); //进程间共享数据基于的文件
    ftruncate(fd,sizeof(*mm)); //填入空洞值
    mm = (struct mt*) mmap(NULL,sizeof(*mm),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); //映射
    close(fd);
    memset(mm,0,sizeof(*mm)); //清零
    pthread_mutexattr_init(&mm->attr); //pthread的属性设置,设置为进程锁
    pthread_mutexattr_setpshared(&mm->attr,PTHREAD_PROCESS_SHARED);
    pthread_mutex_init(&mm->mutex,&mm->attr); //lock为进程锁

    pid = fork(); //fork子进程
    if(pid == 0){ //子进程
        for(i=0 ;i<10 ;i++){
            pthread_mutex_lock(&mm->mutex); //上锁访问进程共享资源
            (mm->num)++;
            printf("num++ %d\n",mm->num);
            pthread_mutex_unlock(&mm->mutex);
            sleep(1);
        }
    }else if(pid > 0 ){ //父进程
        for(i=0 ; i<10 ; i++){
            pthread_mutex_lock(&mm->mutex);//上锁访问进程共享资源
            mm->num += 2;
            printf("num+=2 %d\n",mm->num);
            pthread_mutex_unlock(&mm->mutex);
            sleep(1);
        }
        wait(NULL);
    }

    pthread_mutex_destroy(&mm->mutex); //销毁锁
    pthread_mutexattr_destroy(&mm->attr); //销毁属性
    munmap(mm,sizeof(*mm)); //解除映射绑定
    unlink("my_test"); //清除临时文件
    
    return 0;
}

7 进程间锁-文件锁

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

int main(int argc,char* argv[]){
    int fd;
    struct flock f_lock;
    if(argc < 2){
        printf("./a.out filename\n");   
        exit(1);
    }

    fd = open(argv[1],O_RDWR);
    f_lock.l_type = F_RDLCK;
    f_lock.l_whence = SEEK_SET;
    f_lock.l_start = 0;
    f_lock.l_len = 0;

    fcntl(fd,F_SETLKW,&f_lock);
    printf("get flock\n");
    sleep(5);
    f_lock.l_type = F_UNLCK;
    fcntl(fd,F_SETLKW,&f_lock);
    printf("un flock\n");
    close(fd);
    return 0;
}
上一篇下一篇

猜你喜欢

热点阅读