精华内容
下载资源
问答
  • linux 线程间发送信号

    2019-02-26 02:49:00
    线程通过 pthread_kill(thid,signo)给指定的thid线程发送signo信号。 创建线程与线程屏蔽字顺序  1. pthread_create();  pthread_sigmask(); 线程创建在前。所以子线程没有继承主线程的接下来设置的屏蔽字。...

    线程间通过 pthread_kill(thid,signo)给指定的thid线程发送signo信号。

    创建线程与线程屏蔽字顺序

     1. pthread_create();

         pthread_sigmask(); 线程创建在前。所以子线程没有继承主线程的接下来设置的屏蔽字。子线程依然可以响应主线程接下来要屏蔽的信号。也可以使用sigprocmask()屏蔽调用线程

        2.   pthread_sigmask();

         pthread_create();则两个线程拥有相同的屏蔽字。

     

    转载于:https://www.cnblogs.com/libing029/p/10434947.html

    展开全文
  • 信号量在进程中和在线程中有不同的接口实现,使用信号量来做多进程同步与使用信号量来做多线程间的同步是不一样的,这里将分别介绍信号量在多进程和多线程中的应用。 信号量在多进程中的应用: 这篇文章将讲述...

    前言:

        信号量在进程中和在线程中有不同的接口实现,使用信号量来做多进程间同步与使用信号量来做多线程间的同步是不一样的,这里将分别介绍信号量在多进程和多线程中的应用。

    信号量在多进程中的应用:

        这篇文章将讲述别一种进程间通信的机制——信号量。注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物。有关信号的更多内容,可以阅读我的另一篇文章:Linux进程间通信——使用信号。下面就进入信号量的讲解。

    一、什么是信号量

        为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需要独占式地执行。而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来调协进程对共享资源的访问的。

        信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。最简单的信号量是只能取0和1的变量,这也是信号量最常见的一种形式,叫做二进制信号量。而可以取多个正整数的信号量被称为通用信号量。这里主要讨论二进制信号量。

    二、信号量的工作原理

    由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:

    • P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
    • V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.

        举个例子,就是两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,它将得到信号量,并可以进入临界区,使sv减1。而第二个进程将被阻止进入临界区,因为当它试图执行P(sv)时,sv为0,它会被挂起以等待第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执行。

    三、Linux的信号量机制

    Linux提供了一组精心设计的信号量接口来对信号进行操作,它们不只是针对二进制信号量,下面将会对这些函数进行介绍,但请注意,这些函数都是用来对成组的信号量值进行操作的。它们声明在头文件sys/sem.h中。

    1、semget函数

    它的作用是创建一个新信号量或取得一个已有信号量,原型为:

    int semget(key_t key, int num_sems, int sem_flags);
    • 第一个参数key是整数值(唯一非零),不相关的进程可以通过它访问一个信号量,它代表程序可能要使用的某个资源,程序对所有信号量的访问都是间接的,程序先通过调用semget函数并提供一个键,再由系统生成一个相应的信号标识符(semget函数的返回值),只有semget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。如果多个程序使用相同的key值,key将负责协调工作。
    • 第二个参数num_sems指定需要的信号量数目,它的值几乎总是1。
    • 第三个参数sem_flags是一组标志,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。

    semget函数成功返回一个相应信号标识符(非零),失败返回-1.

    2、semop函数

    它的作用是改变信号量的值,原型为:

    int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);

    sem_id是由semget返回的信号量标识符,sembuf结构的定义如下:

    struct sembuf{
        short sem_num;//除非使用一组信号量,否则它为0
        short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,
                        //一个是+1,即V(发送信号)操作。
        short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,
                        //并在进程没有释放该信号量而终止时,操作系统释放信号量
    };

    3、semctl函数

    该函数用来直接控制信号量信息,它的原型为:

    int semctl(int sem_id, int sem_num, int command, ...);

    如果有第四个参数,它通常是一个union semum结构,定义如下:

    union semun{
        int val;
        struct semid_ds *buf;
        unsigned short *arry;
    };

    前两个参数与前面一个函数中的一样,command通常是下面两个值中的其中一个
    SETVAL:用来把信号量初始化为一个已知的值。p 这个值通过union semun中的val成员设置,其作用是在信号量第一次使用前对它进行设置。
    IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。

    四、进程使用信号量通信

        下面使用一个例子来说明进程间如何使用信号量来进行通信,这个例子是两个相同的程序同时向屏幕输出数据,我们可以看到如何使用信号量来使两个进程协调工作,使同一时间只有一个进程可以向屏幕输出数据。注意,如果程序是第一次被调用(为了区分,第一次调用程序时带一个要输出到屏幕中的字符作为一个参数),则需要调用set_semvalue函数初始化信号并将message字符设置为传递给程序的参数的第一个字符,同时第一个启动的进程还负责信号量的删除工作。如果不删除信号量,它将继续在系统中存在,即使程序已经退出,它可能在你下次运行此程序时引发问题,而且信号量是一种有限的资源。

        在main函数中调用semget来创建一个信号量,该函数将返回一个信号量标识符,保存于全局变量sem_id中,然后以后的函数就使用这个标识符来访问信号量。

    源文件为seml.c,代码如下:

    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/sem.h>
     
    union semun
    {
    	int val;
    	struct semid_ds *buf;
    	unsigned short *arry;
    };
     
    static int sem_id = 0;
     
    static int set_semvalue();
    static void del_semvalue();
    static int semaphore_p();
    static int semaphore_v();
     
    int main(int argc, char *argv[])
    {
    	char message = 'X';
    	int i = 0;
     
    	//创建信号量
    	sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);
     
    	if(argc > 1)
    	{
    		//程序第一次被调用,初始化信号量
    		if(!set_semvalue())
    		{
    			fprintf(stderr, "Failed to initialize semaphore\n");
    			exit(EXIT_FAILURE);
    		}
    		//设置要输出到屏幕中的信息,即其参数的第一个字符
    		message = argv[1][0];
    		sleep(2);
    	}
    	for(i = 0; i < 10; ++i)
    	{
    		//进入临界区
    		if(!semaphore_p())
    			exit(EXIT_FAILURE);
    		//向屏幕中输出数据
    		printf("%c", message);
    		//清理缓冲区,然后休眠随机时间
    		fflush(stdout);
    		sleep(rand() % 3);
    		//离开临界区前再一次向屏幕输出数据
    		printf("%c", message);
    		fflush(stdout);
    		//离开临界区,休眠随机时间后继续循环
    		if(!semaphore_v())
    			exit(EXIT_FAILURE);
    		sleep(rand() % 2);
    	}
     
    	sleep(10);
    	printf("\n%d - finished\n", getpid());
     
    	if(argc > 1)
    	{
    		//如果程序是第一次被调用,则在退出前删除信号量
    		sleep(3);
    		del_semvalue();
    	}
    	exit(EXIT_SUCCESS);
    }
     
    static int set_semvalue()
    {
    	//用于初始化信号量,在使用信号量前必须这样做
    	union semun sem_union;
     
    	sem_union.val = 1;
    	if(semctl(sem_id, 0, SETVAL, sem_union) == -1)
    		return 0;
    	return 1;
    }
     
    static void del_semvalue()
    {
    	//删除信号量
    	union semun sem_union;
     
    	if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
    		fprintf(stderr, "Failed to delete semaphore\n");
    }
     
    static int semaphore_p()
    {
    	//对信号量做减1操作,即等待P(sv)
    	struct sembuf sem_b;
    	sem_b.sem_num = 0;
    	sem_b.sem_op = -1;//P()
    	sem_b.sem_flg = SEM_UNDO;
    	if(semop(sem_id, &sem_b, 1) == -1)
    	{
    		fprintf(stderr, "semaphore_p failed\n");
    		return 0;
    	}
    	return 1;
    }
     
    static int semaphore_v()
    {
    	//这是一个释放操作,它使信号量变为可用,即发送信号V(sv)
    	struct sembuf sem_b;
    	sem_b.sem_num = 0;
    	sem_b.sem_op = 1;//V()
    	sem_b.sem_flg = SEM_UNDO;
    	if(semop(sem_id, &sem_b, 1) == -1)
    	{
    		fprintf(stderr, "semaphore_v failed\n");
    		return 0;
    	}
    	return 1;
    }

    运行结果如下:

    biao@ubuntu:~/test/sem$ ls
    sem.c
    biao@ubuntu:~/test/sem$ gcc sem.c 
    biao@ubuntu:~/test/sem$ 
    biao@ubuntu:~/test/sem$ 
    biao@ubuntu:~/test/sem$ ls
    a.out  sem.c
    biao@ubuntu:~/test/sem$ ./a.out 
    ^C
    biao@ubuntu:~/test/sem$ ./a.out 0 & ./a.out 
    [1] 3972
    XXXX00XX00XX00XX00XX00XX00XX00XXXX000000
    3973 - finished
    biao@ubuntu:~/test/sem$ 
    3972 - finished
    
    [1]+  Done                    ./a.out 0
    biao@ubuntu:~/test/sem$ 

    注意:

        (1)这个程序的临界区为main函数for循环下的semaphore_p和semaphore_v函数中间的代码。

        (2)同一个Key值的信号量在不同进程间只能初始化一次。

    例子分析 :

        同时运行一个程序的两个实例,注意第一次运行时,要加上一个字符作为参数,例如本例中的字符‘O’,它用于区分是否为第一次调用,同时这个字符输出到屏幕中。因为每个程序都在其进入临界区后和离开临界区前打印一个字符,所以每个字符都应该成对出现,正如你看到的上图的输出那样。在main函数中循环中我们可以看到,每次进程要访问stdout(标准输出),即要输出字符时,每次都要检查信号量是否可用(即stdout有没有正在被其他进程使用)。所以,当一个进程A在调用函数semaphore_p进入了临界区,输出字符后,调用sleep时,另一个进程B可能想访问stdout,但是信号量的P请求操作失败,只能挂起自己的执行,当进程A调用函数semaphore_v离开了临界区,进程B马上被恢复执行。然后进程A和进程B就这样一直循环了10次。

    五、对比例子——进程间的资源竞争

        看了上面的例子,你可能还不是很明白,不过没关系,下面我就以另一个例子来说明一下,它实现的功能与前面的例子一样,运行方式也一样,都是两个相同的进程,同时向stdout中输出字符,只是没有使用信号量,两个进程在互相竞争stdout。它的代码非常简单,文件名为normalprint.c,代码如下:

    #include <stdio.h>
    #include <stdlib.h>
     
    int main(int argc, char *argv[])
    {
        char message = 'X';
        int i = 0;    
        if(argc > 1)
            message = argv[1][0];
        for(i = 0; i < 10; ++i)
        {
            printf("%c", message);
            fflush(stdout);
            sleep(rand() % 3);
            printf("%c", message);
            fflush(stdout);
            sleep(rand() % 2);
        }
        sleep(10);
        printf("\n%d - finished\n", getpid());
        exit(EXIT_SUCCESS);
    }

    运行结果如下:

    biao@ubuntu:~/test/sem$ ls 
    a.out  normalprint.c
    biao@ubuntu:~/test/sem$ ./a.out 0 & ./a.out 
    [1] 4023
    X0XXX000X0X0X0XXX000X0X0X0X0X0XXXX0000X0
    4024 - finished
    biao@ubuntu:~/test/sem$ 
    4023 - finished
    
    [1]+  Done                    ./a.out 0
    biao@ubuntu:~/test/sem$ 

    例子分析:

        从上面的输出结果,我们可以看到字符‘X’和‘O’并不像前面的例子那样,总是成对出现,因为当第一个进程A输出了字符后,调用sleep休眠时,另一个进程B立即输出并休眠,而进程A醒来时,再继续执行输出,同样的进程B也是如此。所以输出的字符就是不成对的出现。这两个进程在竞争stdout这一共同的资源。通过两个例子的对比,我想信号量的意义和使用应该比较清楚了。

    六、信号量的总结

    信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。我们通常通过信号来解决多个进程对同一资源的访问竞争的问题,使在任一时刻只能有一个执行线程访问代码的临界区域,也可以说它是协调进程间的对同一资源的访问权,也就是用于同步进程的。

     

    信号量在多线程中的应用:

        信号量、同步这些名词在进程间通信时就已经说过,在这里它们的意思是相同的,只不过是同步的对象不同而已。但是下面介绍的信号量的接口是用于线程的信号量,注意不要跟用于进程间通信的信号量混淆,关于用于进程间通信的信号量的详细介绍可以参阅我的另一篇博文:Linux进程间通信——使用信号量。相似地,线程同步是控制线程执行和访问临界区域的方法。

    一、什么是信号量

        线程的信号量与进程间通信中使用的信号量的概念是一样,它是一种特殊的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作。如果一个程序中有多个线程试图改变一个信号量的值,系统将保证所有的操作都将依次进行。

    而只有0和1两种取值的信号量叫做二进制信号量,在这里将重点介绍。而信号量一般常用于保护一段代码,使其每次只被一个执行线程运行。我们可以使用二进制信号量来完成这个工作。

    二、信号量的接口和使用

    信号量的函数都以sem_开头,线程中使用的基本信号量函数有4个,它们都声明在头文件semaphore.h中。

    1、sem_init函数

    该函数用于创建信号量,其原型如下:

    int sem_init(sem_t *sem, int pshared, unsigned int value);

        该函数初始化由sem指向的信号对象,设置它的共享选项,并给它一个初始的整数值。pshared控制信号量的类型,如果其值为0,就表示这个信号量是当前进程的局部信号量,否则信号量就可以在多个进程之间共享,value为sem的初始值。调用成功时返回0,失败返回-1.

    2、sem_wait函数

        该函数用于以原子操作的方式将信号量的值减1。原子操作就是,如果两个线程企图同时给一个信号量加1或减1,它们之间不会互相干扰。它的原型如下:

    int sem_wait(sem_t *sem);

        sem指向的对象是由sem_init调用初始化的信号量。调用成功时返回0,失败返回-1.

    3、sem_post函数

    该函数用于以原子操作的方式将信号量的值加1。它的原型如下:

    int sem_post(sem_t *sem);

    与sem_wait一样,sem指向的对象是由sem_init调用初始化的信号量。调用成功时返回0,失败返回-1.

    4、sem_destroy函数

    该函数用于对用完的信号量的清理。它的原型如下:

    int sem_destroy(sem_t *sem);

    成功时返回0,失败时返回-1.

    三、使用信号量同步线程

        下面以一个简单的多线程程序来说明如何使用信号量进行线程同步。在主线程中,我们创建子线程,并把数组msg作为参数传递给子线程,然后主线程等待直到有文本输入,然后调用sem_post来增加信号量的值,这样就会立刻使子线程从sem_wait的等待中返回并开始执行。线程函数在把字符串的小写字母变成大写并统计输入的字符数量之后,它再次调用sem_wait并再次被阻塞,直到主线程再次调用sem_post增加信号量的值。

    #include <unistd.h>
    #include <pthread.h>
    #include <semaphore.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
     
    //线程函数
    void *thread_func(void *msg);
    sem_t sem;//信号量
     
    #define MSG_SIZE 512
     
    int main()
    {
    	int res = -1;
    	pthread_t thread;
    	void *thread_result = NULL;
    	char msg[MSG_SIZE];
    	//初始化信号量,其初值为0
    	res = sem_init(&sem, 0, 0);
    	if(res == -1)
    	{
    		perror("semaphore intitialization failed\n");
    		exit(EXIT_FAILURE);
    	}
    	//创建线程,并把msg作为线程函数的参数
    	res = pthread_create(&thread, NULL, thread_func, msg);
    	if(res != 0)
    	{
    		perror("pthread_create failed\n");
    		exit(EXIT_FAILURE);
    	}
    	//输入信息,以输入end结束,由于fgets会把回车(\n)也读入,所以判断时就变成了“end\n”
    	printf("Input some text. Enter 'end'to finish...\n");
    	while(strcmp("end\n", msg) != 0)
    	{
    		fgets(msg, MSG_SIZE, stdin);
    		//把信号量加1
    		sem_post(&sem);
    	}
     
    	printf("Waiting for thread to finish...\n");
    	//等待子线程结束
    	res = pthread_join(thread, &thread_result);
    	if(res != 0)
    	{
    		perror("pthread_join failed\n");
    		exit(EXIT_FAILURE);
    	}
    	printf("Thread joined\n");
    	//清理信号量
    	sem_destroy(&sem);
    	exit(EXIT_SUCCESS);
    }
     
    void* thread_func(void *msg)
    {
    	//把信号量减1
    	sem_wait(&sem);
    	char *ptr = msg;
    	while(strcmp("end\n", msg) != 0)
    	{
    		int i = 0;
    		//把小写字母变成大写
    		for(; ptr[i] != '\0'; ++i)
    		{
    			if(ptr[i] >= 'a' && ptr[i] <= 'z')
    			{
    				ptr[i] -= 'a' - 'A';
    			}
    		}
    		printf("You input %d characters\n", i-1);
    		printf("To Uppercase: %s\n", ptr);
    		//把信号量减1
    		sem_wait(&sem);
    	}
    	//退出线程
    	pthread_exit(NULL);
    }

    运行结果如下:

    biao@ubuntu:~/test/sem$ ls
    semthread.c
    biao@ubuntu:~/test/sem$ gcc semthread.c -lpthread
    biao@ubuntu:~/test/sem$ ls
    a.out  semthread.c
    biao@ubuntu:~/test/sem$ ./a.out 
    Input some text. Enter 'end'to finish...
    abc
    You input 3 characters
    To Uppercase: ABC
    
    Adgd
    You input 4 characters
    To Uppercase: ADGD
    
    end
    Waiting for thread to finish...
    Thread joined
    biao@ubuntu:~/test/sem$ 

    从运行的结果来看,这个程序的确是同时在运行两个线程,一个控制输入,另一个控制处理统计和输出。

    四、分析此信号量同步程序的缺陷

        但是这个程序有一点点的小问题,就是这个程序依赖接收文本输入的时间足够长,这样子线程才有足够的时间在主线程还未准备好给它更多的单词去处理和统计之前处理和统计出工作区中字符的个数。所以当我们连续快速地给它两组不同的单词去统计时,子线程就没有足够的时间支执行,但是信号量已被增加不止一次,所以字符统计线程(子线程)就会反复处理和统计字符数目,并减少信号量的值,直到它再次变成0为止。

    为了更加清楚地说明上面所说的情况,修改主线程的while循环中的代码,如下:

    	printf("Input some text. Enter 'end'to finish...\n");
    	while(strcmp("end\n", msg) != 0)
    	{
    		if(strncmp("TEST", msg, 4) == 0)
    		{
    			strcpy(msg, "copy_data\n");
    			sem_post(&sem);
    		}
    		fgets(msg, MSG_SIZE, stdin);
    		//把信号量加1
    		sem_post(&sem);
    	}

    重新编译程序,此时运行结果如下:

    biao@ubuntu:~/test/sem$ 
    biao@ubuntu:~/test/sem$ ./a.out 
    Input some text. Enter 'end'to finish...
    dhang
    You input 5 characters
    To Uppercase: DHANG
    
    Test
    You input 4 characters
    To Uppercase: TEST
    
    TEST
    You input 9 characters
    To Uppercase: COPY_DATA
    
    You input 9 characters
    To Uppercase: COPY_DATA
    
    adfgad
    You input 6 characters
    To Uppercase: ADFGAD
    
    end
    Waiting for thread to finish...
    Thread joined
    biao@ubuntu:~/test/sem$ 

        当我们输入TEST时,主线程向子线程提供了两个输入,一个是来自键盘的输入,一个来自主线程复数据到msg中,然后从运行结果可以看出,运行出现了异常,没有处理和统计从键盘输入TEST的字符串而却对复制的数据作了两次处理。原因如上面所述。

    五、解决此缺陷的方法

        解决方法有两个,一个就是再增加一个信号量,让主线程等到子线程处理统计完成之后再继续执行;另一个方法就是使用互斥量。

       下面给出用增加一个信号量的方法来解决该问题的代码,源文件名为semthread2.c,源代码如下:

    #include <unistd.h>
    #include <pthread.h>
    #include <semaphore.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
     
     
    //线程函数
    void *thread_func(void *msg);
    sem_t sem;//信号量
    sem_t sem_add;//增加的信号量
     
     
    #define MSG_SIZE 512
     
     
    int main()
    {
    	int res = -1;
    	pthread_t thread;
    	void *thread_result = NULL;
    	char msg[MSG_SIZE];
    	//初始化信号量,初始值为0
    	res = sem_init(&sem, 0, 0);
    	if(res == -1)
    	{
    		perror("semaphore intitialization failed\n");
    		exit(EXIT_FAILURE);
    	}
    	//初始化信号量,初始值为1
    	res = sem_init(&sem_add, 0, 1);
    	if(res == -1)
    	{
    		perror("semaphore intitialization failed\n");
    		exit(EXIT_FAILURE);
    	}
    	//创建线程,并把msg作为线程函数的参数
    	res = pthread_create(&thread, NULL, thread_func, msg);
    	if(res != 0)
    	{
    		perror("pthread_create failed\n");
    		exit(EXIT_FAILURE);
    	}
    	//输入信息,以输入end结束,由于fgets会把回车(\n)也读入,所以判断时就变成了“end\n”
    	printf("Input some text. Enter 'end'to finish...\n");
    	
    	sem_wait(&sem_add);
    	while(strcmp("end\n", msg) != 0)
    	{
    		if(strncmp("TEST", msg, 4) == 0)
    		{
    			strcpy(msg, "copy_data\n");
    			sem_post(&sem);
    			//把sem_add的值减1,即等待子线程处理完成
    			sem_wait(&sem_add);
    		}
    		fgets(msg, MSG_SIZE, stdin);
    		//把信号量加1
    		sem_post(&sem);
    		//把sem_add的值减1,即等待子线程处理完成
    		sem_wait(&sem_add);
    	}
     
     
    	printf("Waiting for thread to finish...\n");
    	//等待子线程结束
    	res = pthread_join(thread, &thread_result);
    	if(res != 0)
    	{
    		perror("pthread_join failed\n");
    		exit(EXIT_FAILURE);
    	}
    	printf("Thread joined\n");
    	//清理信号量
    	sem_destroy(&sem);
    	sem_destroy(&sem_add);
    	exit(EXIT_SUCCESS);
    }
     
     
    void* thread_func(void *msg)
    {
    	char *ptr = msg;
    	//把信号量减1
    	sem_wait(&sem);
    	while(strcmp("end\n", msg) != 0)
    	{
    		int i = 0;
    		//把小写字母变成大写
    		for(; ptr[i] != '\0'; ++i)
    		{
    			if(ptr[i] >= 'a' && ptr[i] <= 'z')
    			{
    				ptr[i] -= 'a' - 'A';
    			}
    		}
    		printf("You input %d characters\n", i-1);
    		printf("To Uppercase: %s\n", ptr);
    		//把信号量加1,表明子线程处理完成
    		sem_post(&sem_add);
    		//把信号量减1
    		sem_wait(&sem);
    	}
    	sem_post(&sem_add);
    	//退出线程
    	pthread_exit(NULL);
    }

    其运行结果如下:

    biao@ubuntu:~/test/sem$ ./a.out 
    Input some text. Enter 'end'to finish...
    djfagm
    You input 6 characters
    To Uppercase: DJFAGM
    
    mfa
    You input 3 characters
    To Uppercase: MFA
    
    TEST
    You input 4 characters
    To Uppercase: TEST
    
    You input 9 characters
    To Uppercase: COPY_DATA
    
    test
    You input 4 characters
    To Uppercase: TEST
    
    You input 9 characters
    To Uppercase: COPY_DATA
    
    end
    Waiting for thread to finish...
    Thread joined
    biao@ubuntu:~/test/sem$ 

        分析:这里我们多使用了一个信号量sem_add,并把它的初值赋为1,在主线程在使用sem_wait来等待子线程处理完全,由于它的初值为1,所以主线程第一次调用sem_wait总是立即返回,而第二次调用则需要等待子线程处理完成之后。而在子线程中,若处理完成就会马上使用sem_post来增加信号量的值,使主线程中的sem_wait马上返回并执行紧接下面的代码。从运行结果来看,运行终于正常了。注意,在线程函数中,信号量sem和sem_add使用sem_wait和sem_post函数的次序,它们的次序不能错乱,否则在输入end时,可能运行不正常,子线程不能正常退出,从而导致程序不能退出。

    问章内容转载自:

    https://blog.csdn.net/ljianhui/article/details/10813469

    https://blog.csdn.net/ljianhui/article/details/10243617

    展开全文
  • linux线程同步之信号

    千次阅读 2014-02-25 11:21:40
    linux中向某个线程发送信号,若没有对该信号的处理函数,则会导致程序结束。 如下面的程序,创建一个线程,主线程向其发送一个信号,会导致程序立即结束 #include #include pthread_t t; void* run(void* arg)...

    linux中向某个线程发送信号,若没有对该信号的处理函数,则会导致程序结束。

    如下面的程序,创建一个线程,主线程向其发送一个信号,会导致程序立即结束


    #include <stdio.h>
    #include <pthread.h>
    
    pthread_t t;
    
    void* run(void* arg)
    {
    	while(1)
    	{
    		printf("Hello\n");
    	}
    }
    
    main()
    {
    	pthread_create(&t, 0, run, 0);
    	pthread_kill(t,34);
    	while(1);
    }
    

    这就要求利用pthread_kill 与 sigwait 通过等待信号来控制线程时应添加一个信号处理函数来实现对线程的控制

    改进后的控制线程程序

    #include <stdio.h>
    #include <pthread.h>
    #include <signal.h>
    
    pthread_t t1, t2;
    sigset_t sigs;
    
    void handle(int s)
    {
    }
    
    void* r1(void* arg)
    {
    	int s;
    	while(1)
    	{
    		printf("1\n");
    		sigwait(&sigs, &s);
    	}
    }
    
    void* r2(void* arg)
    {
    	while(1)
    	{
    		sleep(1);
    		printf("2\n");
    		pthread_kill(t1, 34);
    	}
    }
    
    main()
    {
    	signal(34, handle);
    	sigemptyset(&sigs);
    	sigaddset(&sigs, 34);
    	
    	pthread_create(&t1, 0, r1, 0);
    	pthread_create(&t2, 0, r2, 0);
    
    	while(1);
    }

    部分效果


    展开全文
  • 一、发送信号的函数 int pthread_kill(pthread_t thread, int sig); 1、别被名字吓到,pthread_kill可不是kill,而是向线程发送signal。还记得signal吗,大部分signal的默认动作是终止进程的运行,所以,我们才要...

    一、发送信号的函数

    int pthread_kill(pthread_t thread, int sig);
    1、别被名字吓到,pthread_kill可不是kill,而是向线程发送signal。还记得signal吗,大部分signal的默认动作是终止进程的运行,所以,我们才要用sigaction()去抓信号并加上处理函数。

    ​ 2、向指定ID的线程发送sig信号,如果线程代码内不做处理,则按照信号默认的行为影响整个进程,也就是说,如果你给一个线程发送了SIGQUIT,但线程却没有实现signal处理函数,则整个进程退出。如果要获得正确的行为,就需要在线程内实现sigaction了。所以,如果int sig的参数不是0,那一定要清楚到底要干什么,而且一定要实现线程的信号处理函数,否则,就会影响整个进程。如果int sig是0呢,这是一个保留信号,其实并没有发送信号,作用是用来判断线程是不是还活着。

    二、信号处理

    1、进程信号处理:
    int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
    给信号signum设置一个处理函数,处理函数在sigaction中指定
    act.sa_mask 信号屏蔽字
    act.sa_handler 信号集处理程序

    ​ 2、信号集的处理

    int sigemptyset(sigset_t *set); //清空信号集int sigfillset(sigset_t *set);  //将所有信号加入信号集int sigaddset(sigset_t *set, int signum); //增加一个信号到信号集int sigdelset(sigset_t *set, int signum); //删除一个信号到信号集
    

    ​ 3、多线程信号屏蔽处理
    ​ /* int sigprocmask(int how, const sigset_t *set, sigset_t oldset)/这是进程的信号屏蔽处理

    int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
    how = SIG_BLOCK:向当前的信号掩码中添加set,其中set表示要阻塞的信号组。
    SIG_UNBLOCK:向当前的信号掩码中删除set,其中set表示要取消阻塞的信号组。
    SIG_SETMASK:将当前的信号掩码替换为set,其中set表示新的信号掩码。
    在多线程中,新线程的当前信号掩码会继承创造它的线程的信号掩码。
    一般情况下,被阻塞的信号将不能中断此线程的执行,除非此信号的产生是因为程序运行出错如 SIGSEGV;另外不能被忽略处理的信号 SIGKILL 和 SIGSTOP 也无法被阻塞。

    三、实例

    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <iostream>
    
    #include <signal.h>
    #include <errno.h>
    
    #include "include/pthread.h"
    
    
    
    #ifndef _WIN64
    #pragma comment(lib,".\\lib32\\pthreadVC2.lib")
    #pragma comment(lib,".\\lib32\\pthreadVCE2.lib")
    #pragma comment(lib,".\\lib32\\pthreadVSE2.lib")
    #else
    #pragma comment(lib,".\\lib64\\pthreadVC2.lib")
    #endif 
    
    
    /*
    WINDOWS 缺少库函数,得在Linux下运行
    
    
    *DECRIPTION:    正确到处理信号
    
    *    int pthread_kill(pthread_t thread, int sig);
    *        向线程thread发送sig信号,成功返回0,失败返回错误码
    *
    *    int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
    *        为信号signum设置处理函数,处理函数在sigaction中指定
    *        act.sa_mask 信号屏蔽字
    *        act.sa_handler 信号集处理程序
    *
    * int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
    *        多线程信号屏蔽函数
    *        how = SIG_BLOCK:向当前的信号掩码中添加set,其中set表示要阻塞的信号组。
    *        SIG_UNBLOCK:向当前的信号掩码中删除set,其中set表示要取消阻塞的信号组。
    * SIG_SETMASK:将当前的信号掩码替换为set,其中set表示新的信号掩码。
    * 在多线程中,新线程的当前信号掩码会继承创造它的线程的信号掩码
    */
    
    
    void sig_handler1(int arg)
    {
    	printf("thread1 get signal\n");
    	return;
    }
    void sig_handler2(int arg)
    {
    	printf("thread2 get signal\n");
    	return;
    }
    void* thread_fun1(void* arg)
    {
    	printf("new thread 1\n");
    
    	struct sigaction act;
    	
    
    	memset(&act, 0, sizeof(act));
    	sigaddset(&act.sa_mask,SIGQUIT);
    	act.sa_handler = sig_handler1;
    	sigaction(SIGQUIT,&act,NULL);
    
    	pthread_sigmask(SIG_BLOCK,&act.sa_mask,NULL);
    	Sleep(2);
    
    	return (void*)0;
    }
    void *thread_fun2(void *arg)
    {
    	printf("new thread 2\n");
    	
    	//WINDOWS 缺少库函数,得在Linux下运行
    	struct sigaction act;
    	memset(&act, 0, sizeof(act));
    	sigaddset(&act.sa_mask, SIGQUIT);
    	act.sa_handler = sig_handler2;
    	sigaction(SIGQUIT, &act, NULL);
    
    	//    pthread_sigmask(SIG_BLOCK, &act.sa_mask, NULL);
    	Sleep(2);
    }
    
    int main()
    {
    	pthread_t tid1, tid2;
    	int err;
    	int s;
    
    	err = pthread_create(&tid1,NULL,thread_fun1,NULL);
    	if (err!=0)
    	{
    		printf("create new thread 1 failed\n");
    		return 0;
    	}
    	err = pthread_create(&tid2,NULL,thread_fun2,NULL);
    	if (err != 0)
    	{
    		printf("create new thread 2 failed\n");
    		return 0;
    	}
    
    	Sleep(1);
    	s = pthread_kill(tid1, SIGQUIT);
    	if (s!=0)
    	{
    		printf("send signal to thread1 failed\n");
    	}
    
    	s = pthread_kill(tid2, SIGQUIT);
    	if (s != 0)
    	{
    		printf("send signal to thread2 failed\n");
    	}
    	pthread_join(tid1, NULL);
    	pthread_join(tid2, NULL);
    
    	return 0;
    }
    
    
    展开全文
  • Linux线程同步信号发送与接收总结

    千次阅读 2017-11-27 17:32:23
    Linux中关于线程同步信号发送主要分为4步(默认有一个主线程和一个子线程): (1)首先包含头文件,再创建一个信号量,并初始化之:  #include &...(2)主线程发送信号:  sem_post(&amp;sem...
  • linux线程间通信

    2019-02-19 15:30:43
    线程间的通信有两种情况: 1、一个进程中的线程与另外一个进程中的线程通信,由于两个线程只能访问自己所属进程的地址空间和资源,故等同于进程的通信。 2、同一个进程中的两个线程进行通信。本文说的就是第...
  • Linux线程信号

    千次阅读 2018-12-01 15:17:30
    1信号线程的关系 POSIX标准对多线程情况下的信号机制提出了一些要求: 信号处理函数必须在多线程进程的所有线程之间共享, 但是每个线程要有自己的挂起信号集合和阻塞信号掩码。... 如果发送一个致命信号到...
  • Linux线程信号

    千次阅读 2010-04-06 12:51:00
    1. 概念按照 POSIX, 异步 (外部) 信号发送到整个进程. 所有线程共享同一个设置, 即通过 sigaction 设置的线程... 由于Linux 线程实现上的独特性, 外部信号始终发送到特定的线程. 2. 例子 #include #include #include
  • linux线程信号总结

    2019-05-17 11:13:24
    可以给任何线程发送信号,收到信号的线程执行相应的信号函数。由于一个进程中所有的线程共用一份信号函数(当然,一个信号对应一个信号处理函数),即使对应于当前信号的信号处理函数不是当前线程注册的,也可...
  • Linux 线程信号处理

    2014-03-26 15:39:35
    转自:... ... 线程信号 类UNIX信号以前是专为进程设计的,它比线程的出现早了很多年。当线程模型出现后,专家们试图也在线程上实现信号,这导致了一个问题:如果
  • Linux线程浅析[线程的同步和互斥之线程死锁,线程与信号的关系] 线程死锁 线程与信号 记得以前在学习java线程的时候,也会接触死锁,当时不断强调锁千万不要不能去做嵌套,不然容易一个线程在执行的时候所需要的锁...
  • linux C 线程间通信-信号量机制

    千次阅读 2015-04-20 17:01:49
    1. 线程间通信-互斥锁 互斥锁,适用于共享资源只有一个的情况下。用简单的加锁方法控制对共享资源的原子操作  只有两种状态: 上锁、解锁 可把互斥锁看作某种意义上的全局变量  在同一时刻只能有一个线程掌握某个...
  • 一、linux下进程通信的几种主要手段简介: ... 信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程通信外,进程还可以发送信号给进程本身;linux除了支...
  • Linux高级编程——线程信号处理

    千次阅读 2017-09-27 12:08:04
    Linux线程环境中的信号处理不同于进程的信号处理。一方面线程间信号处理函数的共享性使得信号处理更为复杂,另一方面普通异步信号又可转换为同步方式来简化处理。  本文首先介绍信号处理在进程中和线程间的不同...
  • linux基础——linux线程间通信及同步机制总结

    万次阅读 多人点赞 2016-07-25 22:45:16
    线程间的通信有两种情况: 1、一个进程中的线程与另外一个进程中的线程通信,由于两个线程只能访问自己所属进程的地址空间和资源,故等同于进程的通信。 2、同一个进程中的两个线程进行通信。本文说的就是第二种...
  • windows和linux进程线程间通信

    千次阅读 2017-02-17 11:57:04
    来源自我的博客 ...Linux线程间通信:互斥量(mutex),信号量,条件变量 Windows进程通信:管道、消息队列、共享内存、信号
  • Linux线程应用中如何编写安全的信号处理函数 转自:http://www.ibm.com/developerworks/cn/linux/l-cn-signalsec/index.html?ca=drs-cn-0618 周 婷 (zhouting@cn.ibm.com), 软件工程师, IBM 中国软件...
  • Linux线程编程与线程间通信机制

    千次阅读 2016-01-26 17:04:44
    Linux中多线程编程技术被广泛使用,这主要是因为多线程可以提升程序的运行效率和便利性。在现在的比较大一点的linux...本文就来讲讲linux中多线程编程的实现,以及利用消息队列进行线程间通信。   一、线程的创建
  • linux中的线程信号

    2011-03-18 12:04:00
    因此linux线程在遇到信号时的行为与其他操作系统实现不同。在posix.1线程模型中,异步信号发送到进程以后,进程中当前没有阻塞该信号(如设置了屏蔽位sigprocmask(&oldmask, &newmask))的某个线程就被选中,接收...
  • linux线程信号量 sem_init

    万次阅读 2018-09-29 20:32:54
    linux sem 信号量是一种特殊的变量,访问具有原子性, 用于解决进程或线程间共享资源引发的同步问题。 用户态进程对 sem 信号量可以有以下两种操作: -&amp;gt; 等待信号量 当信号量值为 0 时,程序等待;...
  • 线程间的通信有两种情况: 1、一个进程中的线程与另外一个进程中的线程通信,由于两个线程只能访问自己所属进程的地址空间和资源,故等同于进程的通信。 2、同一个进程中的两个线程进行通信。本文说的就是第二种...
  • Linux线程中使用信号-1

    万次阅读 2012-03-06 16:07:30
    Linux的多线程中使用信号机制,与在进程中使用信号机制有着根本的区别,可以说是完全不同。在进程环境中,对信号的处理是,先注册信号处理函数,当信号异步发生时,调用处理函数来处理信号。它完全是异步的(我们...
  • Linux线程编程 与 信号处理

    千次阅读 2014-02-24 11:40:13
    原来在main函数中把几个子线程启动后就睡10分钟后开始清理子线程后退出。...#include //信号处理所需要的头文件 int main(int argc, char * argv[]){  //其他所需要的变量声明   sigset_t sig_set,sig_pend
  • linux线程信号总结

    2014-05-16 13:48:29
    linux线程信号总结(一) 1. 在多线程环境下,产生的信号是传递给整个进程的,一般而言,所有线程都有机会收到这个信号,进程在收到信号的的线程上下文执行信号处理函数,具体是哪个线程执行的难以获知。也就是...
  • (1)每一个线程可以向别的线程发送信号。pthread_kill()函数用来完成这一操作。 (2)每一个线程可以设置自己的信号阻塞集合。pthread_sigmask()函数用来完成这一操作,其类似于进程的sigprocmask()函数。 (3)...
  • linux内核线程信号的处理过程

    千次阅读 2010-02-09 20:19:00
    linux中的线程分为用户线程和内核线程,用户线程是标准的线程,完全的自主性,完全的抢占性;但是内核线程就不那么好了,某种意义上没有用户线程那么清闲,这个怎么理解呢?用户线程的编写者只需要实现应用逻辑就...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 53,743
精华内容 21,497
关键字:

linux线程间发送信号

linux 订阅