linux下的进程与线程

ps  aux              静态查询进程信息
ps ajx          查询进程之间关系

一.进程

(一).进程的创建

创建进程一般使用fork()函数,没有参数

返回值

On success, the PID of the child process is returned in the parent, and 0 is returned in the child.  On failure, -1 is returned in the  parent, no child process is created, and errno is set appropriately.

成功返回pid,pid为零代表子进程,小于零创建失败

#include
#include
#include


int main()
{
	pid_t pid = fork();
	return 0;
}

还有一种vfork()创建进程,需要配合execl来使用

(二).进程的使用

1.如何区分父子进程

getpid()    可以查询当前进程句柄,如果是大于零的值说明当前是父进程,如果等于零是子进程。

 调用fork的进程为父进程,fork 创建的子进程把所有代码拷贝过去,从fork后面的部分开始执行

因为写的进程的代码段是相同的,可以通过pid的不同值来区分父进程还是子进程。

2.替换子进程

可以通过exec系列函数来把子进程进行替换

exec系列函数(execl、execlp、execle、execv、execvp)

头文件

extern char **environ;

原型:

int execl(const char *path, const char *arg, ...);

int execlp(const char *file, const char *arg, ...);

int execv(const char *path, char *const argv[]);

int execvp(const char *file, char *const argv[]);

int execle(const char *path, const char *arg, ..., char * const envp[]);

参数:

path参数表示你要启动程序的名称包括路径名

arg参数表示启动程序所带的参数,一般第一个参数为要执行命令名,不是带路径且arg必须以NULL结束

NULL为哨兵节点,exec初始化加载参数到NULL结束,必须写,否则exec报错。(NULL不可以用0代替)

返回值:成功返回0,失败返回-1

以上exec系列函数区别:

1,带l 的exec函数:execl,execlp,execle,表示后边的参数以可变参数的形式给出且都以一个空指针结束。

2,带 p 的exec函数:execlp,execvp,表示第一个参数path不用输入完整路径,只有给出命令名即可,它会在环境变量PATH当中查找命令

3,不带 l 的exec函数:execv,execvp表示命令所需的参数以char *arg[]形式给出且arg最后一个元素必须

是NULL

4,带 e 的exec函数:execle表示,将环境变量传递给需要替换的进程

代码

#include

int main()
{

	printf("abc\n");

	return 0;
}


//gcc print.c -o app
#include
#include
#include

int main()
{
	pid_t pid =fork();//创建进程
	if(pid == 0)//子进程
	{
		execl("./app","./app",NULL);//app是自己创建的程序
        printf("child\n");
	}
	else if(pid > 0)//父进程
	{

		printf("parent\n");
	}

	return 0;
}

结果为parent   和abc 各打印一次,而child没有,所以如果需要子进程执行自定义代码或者任务,需要在fork后,exec之前完成。

(三).进程的回收

创建进程后,子进程先于父进程死亡,父进程不知道,就会产生残留的pcb即僵尸进程,需要在父进程进行回收

wait(NULL)  是阻塞函数,回收僵尸进程
waitpid() 非阻塞回收

wait函数

调用一次wait只能回收一个进程

wait返回值:1.回收成功,wait返回值僵尸进程的pid 2.回收失败,wait 返回-1(什么时候失败? 在没有子进程的时候)

 waitpid 回收函数 (比wait功能性更强,回收更为灵活)

waitpid(pid_t pid,int* status,int option);

1)pid:可以通过该参数指定waitpid回收

小于-1 跨组回收,指定组id,回收指定进程组种的(僵尸)子进 程,-5000(指定组id) ​

-1 回收任意子进程if ​

0 同组回收,父进程只能回收与自己同组的子进程

大于0 点名回收,指定子进程的pid,回收子进程3000 前面三个可以回收多个,>0回收一个 2)status 回收成功,传出子进程的退出信息,无论是wait还是waitpid,int* status 可以传NULL,不接收子进程退出信息

3)options: 可以通过选项参数,设计waitpid的工作状态以及回收方式

WNOHANG=可以通过该选项,将waitpid设置为非阻塞回收,没有子进程退出立即返回:

waitpid 返回值: 1)回收成功,子进程id

2)回收失败,wait返回-1 (没有子进程) ​ 3)非阻塞回收,返回0,表示当前的子进程没有结束,无需回收

阻塞回收策略 和非阻塞回收策略

阻塞回收:阻塞工作需要挂起唤醒,如果挂起唤醒频繁,增大系统开销

非阻塞回收: 无挂起唤醒开销

阻塞回收:阻塞回收会对父进程造成一定影响,父进程自身的任务被搁置 无法进行推进

非阻塞回收:轮询回收,定时查看子进程是否需要回收,如果不需要返回执行自己的任务,如果需要则回收释放PCB残留

waitpid的使用代码

#include
#include
#include
#include
#include

int main()
{
    int  status;
    pid_t pid = fork();//创建子进程
    if(pid > 0)//大于零为父进程
    {
        printf("parent id : %d\n",getpid());
        pid_t zid; 
        while((zid = waitpid(-1,0,WNOHANG)) != -1)//waitpid()函数的使用
        {
            if(zid > 0)
                printf("child zid is %d\n",zid);
            else if(zid == 0)
            {
                printf("continue\n");
            }
            sleep(1);
        }

        while(1) sleep(1);
    }
    else if(pid == 0)//子进程直接退出
    {
        printf("child id : %d\n",getpid());
        sleep(5);
        exit(0);//退出进程
        //while(1) sleep(1);
    }

    return 0;
}

(四)守护进程

在后台默默运行,进行日志书写等任务

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

int main()
{
    pid_t pid = fork();


    //    1.创建子进程
    if(pid > 0)
        exit(0);
    //2.创建新的会话
    setsid();
    //3.关闭输入输出
    for(int i = 0;i < 3; i ++)
    {
        close(i);
    }
    //4.更改工作目录
    chdir("/tmp");

    //5.更改权限
    umask(0);
    6.创建文件
    FILE* fd = fopen("mylog.txt","w+");
    //7.定时更新
    time_t t;
    while(1)
    {
        t = time(NULL);
        fprintf(fd,"%s",ctime(&t));
        fflush(fd);
        sleep(1);
    }
    //8.退出
    //9.开机启动


    return 0;
}

(五)进程间通信

1.消息队列


消息结构体
struct mass
{
     int msgtype;
     char text[1024];
}

随机生成key值
key_t keyid = ftok(“地址”,ID号(可任意));

创建消息队列,获得消息id
int massageid = msgget(刚才得到的keyid,权限(IPC_CREAT |0664));


发送消息
msgsnd(消息id,(void*)&消息结构体,消息结构体大小,权限(一般可填0));

接受消息

ssize_t ant = msgrcv(消息id,接收结构体地址,结果体大小,希望收到来自谁,权限)
ant为接收到的数量

msgctl(消息id,权限(IPC_RMID),0);


2.管道pipe

(1).匿名管道


匿名管道是单向的,只能父子进程间使用

int pipefd[2];
pipe(pipefd)   // 返回值为-1,则失败

pid_t pid = fork()
if(pid > 0)
{
    close(pipefd[0]);
    write(pipefd[1],szbuf,sizeof(szbuf));
    close(pipefd[1]);
}

if(pid == 0)
{
      char buf[1024];
     close(pipefd[1]);
     ssize_t ant = read(pipefd[0],buf,sizeof(buf));
     if(ant >0)
     {
            printf("%s\n",buf);
     }
     close(pipefd[0];
}

(2).命名管道

更加方便,创建方式有两种
1:通过命令创建管道

int fd = open("mypipe",O_RDONLY);
char buf[1024];
ssize_t ant = read(fd,buf,sizeof(buf));
if(ant > 0)
   printf("%s\n",buf);
close(fd);


创建管道
还需要在命令行输入       mkfifo  mypipe//这是打开的管道的名字


3.共享文件

(1)写入文件

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

int main()
{
     int fd = open("filemap",O_CREAT|O_RDWR,0664);//打开一个文件,需要读写功能,没有则创建,
                                                  //给权限为0664
     int filesize =  ftruncate(fd,4096);//文件大小,不偏移文件大小为零
    char *strbuf = (char*)mmap(0,4096,PROT_WRITE,MAP_SHARED,fd,0);//设置共享空间
    strcpy(strbuf,"hello world");//写入数据
    
    munmap(strbuf,4096);//回收
    close(fd); //关闭文件句柄


return 0;


}

(2)读取文件

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

int main()
{
     int fd = open("filemap",O_RDONLY);//打开文件,需要读的权限
     int filesize =  ftruncate(fd,4096);//文件大小为一页4096
    char *szbuf = (char*)mmap(0,4096,PROT_READ,MAP_SHARED,fd,0);//读取共享空间内容
    //strcpy(strbuf,"hello world");
    printf("szbuf = %s\n",szbuf);//打印读取到的内容
    munmap(szbuf,4096);//回收
    close(fd); //关闭文件句柄


return 0;


}

命令行下输入:

mkfifo filemap;

4.信号

屏蔽信号

#include
#include
#include
#include
#include

int main()
{

sigset_t newsig,oldsig;
sigemptyset(&newsig);
sigaddset(&newsig,2);
sigprocmask(SIG_SETMASK,&newsig,&oldsig);
while(1) sleep(1);
return 0;
}


忽略信号

#include
#include
#include
#include
#include

int main()
{

//    sigset_t newsig,oldsig;
//    sigemptyset(&newsig);
//    sigaddset(&newsig,2);
//    sigprocmask(SIG_SETMASK,&newsig,&oldsig);

        struct sigaction newact,oldact;
        newact.sa_handler = SIG_IGN;
        newact.sa_flags = 0;
        sigaction(2,&newact,&oldact);
        
    while(1) sleep(1);
    return 0;
}

捕捉信号

#include
#include
#include
#include
#include

void myjob(int a)
{
   printf("get c\n");
}

int main()
{

//    sigset_t newsig,oldsig;
//    sigemptyset(&newsig);
//    sigaddset(&newsig,2);
//    sigprocmask(SIG_SETMASK,&newsig,&oldsig);

        struct sigaction newact,oldact;
        newact.sa_handler = myjob;
        newact.sa_flags = 0;
        sigemptyset(&newact.sa_mask);
        sigaction(2,&newact,&oldact);
        
    while(1) sleep(1);
    return 0;
}

时间竞争

alarm信号,定时发送信号

#include
#include
#include
#include

void myjob(int a)
{
}

void mysleep(int num)
{
    struct sigaction newact,oldact;
    newact.sa_handler = myjob;
    newact.sa_flags = 0;
    sigemptyset(&newact.sa_mask);
    sigaction(14,&newact,&oldact);
    alarm(num);
    pause();


}

int main()
{
    while(1)
    {
        mysleep(2);
        printf("it has pasted two seconds\n");


    }

    return 0;
}

定时发信号的时候,多进程因为CPU时间资源的竞争,导致关键信号在被进程使用前提前处理,导致该进程依赖信号的功能产生异常

可以通过先屏蔽在解除屏蔽,来解决时序竞争问题

#include
#include
#include
#include

void myjob(int a)
{
}

void mysleep(int num)
{
         //屏蔽信号
        sigset_t newset,oldset;
        sigemptyset(&newset);
        sigaddset(&newset,14);
        sigprocmask(SIG_SETMASK,&newset,&oldset);

        //捕捉信号
    struct sigaction newact,oldact;
    newact.sa_handler = myjob;
    newact.sa_flags = 0;
    sigemptyset(&newact.sa_mask);
    sigaction(14,&newact,&oldact);
    alarm(num);
     sleep(3);
    //pause();
        //挂起并解除屏蔽
         sigsuspend(&oldact.sa_mask);

}

int main()
{
    while(1)
    {
        mysleep(2);
        printf("it has pasted two seconds\n");


    }

    return 0;
}

信号通信

信号还可以携带信息

//发信息
#include
#include
#include
#include

int main(int agrc,char**agrv)
{
     pid_t pid = atoi(agrv[1]);
     int signum = atoi(agrv[2]);
     union sigval sigvalue;
     sigvalue.sival_int = 100;
     sigqueue(pid,signum,sigvalue);


return 0;
}

//收信息
#include
#include
#include
#include

void  myjok(int num, siginfo_t *info, void *constext)
{
   printf("num is %d\ninfo is %d\n",num,info->si_int);
}
int main()
{
     struct sigaction newact;
     newact.sa_flags = SA_SIGINFO;
     newact.sa_sigaction = myjok;
     sigemptyset(&newact.sa_mask);
     sigaction(SIGUSR1,&newact,NULL);

while(1);
return 0;
}


二.线程

(一)linux创建线程

#include
#include
#include
#include
#include

void * threadproc (void *a)
{
while(1)
{
 sleep(1);
   printf("1\n");
}
}


int main()
{
pthread_t pid;
int err = pthread_create(&pid,NULL,&threadproc,NULL);

if(err)
{
      printf("false is %s\n",strerror(err));
      return 0;
}

while(1);
return 0;

}

(二).线程的回收


pthread_self()  获取线程id
回收态:    pthread_join(退出的线程id,退出码(返回的值))   回收线程,阻塞
分离态:    pthread_datach(线程id)   分离后会自动回收
处于回收态,不能进行分离
pthread_cancel(线程id);   发送推出码


pthread_testcancel()   空调用,可以执行一次系统调用

(三).线程间通信

互斥锁:

pthread_mutex_t  mut;    //定义锁
pthread_mutex_init(&mut,0);  //初试化
pthread_mutex_destroy(&mut); // 清除

加锁
pthread_mutex_lock(&mut);//上锁
pthread_mutex_unlock(&mut);//开锁

安全灵活,效率不高


读写锁


读的时候是共享的,写是互斥的
和互斥锁类似

#include
#include
#include
#include

pthread_rwlock_t mylock;
int ant = 0;
void *procthread1(void *a)
{
    for(int i = 0; i< 1000;i ++)
    {
        usleep(1000);
                pthread_rwlock_rdlock(&mylock);
        //ant ++;
        printf("thread id = [0x%x],read ant = %d\n",(unsigned int)pthread_self(),ant);
                pthread_rwlock_unlock(&mylock);
    }
}

void *procthread2(void *a)
{
    for(int i = 0; i< 1000;i ++)
    {
        usleep(1000);
                pthread_rwlock_wrlock(&mylock);
        ant ++;
        printf("thread id = [0x%x],write ant = %d\n",(unsigned int)pthread_self(),ant);
                pthread_rwlock_unlock(&mylock);
    }
}

int main()
{
        pthread_rwlock_init(&mylock,0);
    pthread_t pid[8];
        for(int i = 0;i < 3; i ++)
{

     pthread_create(&pid[i],NULL,&procthread1,0);
}
        for(int i = 3; i < 8;i ++)
{
    pthread_create(&pid[i],NULL,&procthread2,0);
}
        for(int i = 0;i < 8;i ++)
{
    pthread_join(pid[i],NULL);
}
        pthread_rwlock_destroy(&mylock);
    return 0;
}

进程共享锁

可以通过设置互斥锁的属性,来把他变成进程间的共享锁
pthread_mutexattr_t   
pthread_mutexattr_init();
pthread_mutexattr_getpshared(哪个,返回一个共享属性);

pthread_mutexattr_setpshared();//设置共享属性



进程共享锁

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

typedef struct fdfd
{
    pthread_mutex_t mlock;
    int ncount;
}myshare;
int main()
{

    int fd = open("myfi",O_RDWR|O_CREAT,0664);
    ftruncate(fd,sizeof(myshare));
    myshare *pshared = (myshare*)mmap(0,sizeof(myshare),PROT_WRITE,  MAP_SHARED,fd,0);
         

    pshared->ncount = 0; 
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_setpshared(&attr,PTHREAD_PROCESS_SHARED);
    pthread_mutex_init(&(pshared->mlock),&attr);

    pid_t pid = fork();
    if(pid > 0 )
    {
        for(int i = 0; i < 100; i++)
        {
            pthread_mutex_lock(&(pshared->mlock));
            printf("parent id = %d   ncount = %d \n",getpid(),++pshared->ncount);
            pthread_mutex_unlock(&(pshared->mlock));
        }

    }
    else if(pid == 0 )
    {
        for(int i = 0; i < 100; i++)
        {
            pthread_mutex_lock(&(pshared->mlock));
            printf("child id = %d   ncount = %d \n",getpid(),++pshared->ncount);
            pthread_mutex_unlock(&(pshared->mlock));
        }

    }

        printf("1\n");
    pthread_mutex_destroy(&(pshared->mlock));
        printf("2\n");
    pthread_mutexattr_destroy(&attr);
        printf("3\n");

    munmap(pshared,sizeof(myshare));
        
        printf("4\n");

    return 0;
}

你可能感兴趣的:(linux)